From 2fa57ff510284c657a8ff87e369a30e5cfdaf734 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 1 May 2019 17:41:36 +0200 Subject: [PATCH 01/31] define IImageVisitor --- src/ImageSharp/IImage.cs | 2 +- src/ImageSharp/Image.cs | 40 +++++++++++++++++++ src/ImageSharp/Image{TPixel}.cs | 5 +++ .../IImageProcessingContext{TPixel}.cs | 5 +++ .../Processing/ProcessingExtensions.cs | 5 +++ .../Processing/Processors/IImageProcessor.cs | 6 +++ 6 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 src/ImageSharp/Image.cs diff --git a/src/ImageSharp/IImage.cs b/src/ImageSharp/IImage.cs index b9e2cee616..fb251cfd5a 100644 --- a/src/ImageSharp/IImage.cs +++ b/src/ImageSharp/IImage.cs @@ -4,7 +4,7 @@ using System; namespace SixLabors.ImageSharp -{ +{ /// /// Encapsulates the properties and methods that describe an image. /// diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs new file mode 100644 index 0000000000..6bea675b9f --- /dev/null +++ b/src/ImageSharp/Image.cs @@ -0,0 +1,40 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Metadata; +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp +{ + internal interface IImageVisitor + { + void Visit(Image image) + where TPixel : struct, IPixel; + } + + public abstract partial class Image : IImage + { + /// + public PixelTypeInfo PixelType { get; } + + /// + public abstract int Width { get; } + + /// + public abstract int Height { get; } + + /// + public ImageMetadata Metadata { get; } + + protected Image(PixelTypeInfo pixelType, ImageMetadata metadata) + { + this.PixelType = pixelType; + this.Metadata = metadata ?? new ImageMetadata(); + } + + public abstract void Dispose(); + + internal abstract void ApplyVisitor(IImageVisitor visitor); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index f2bef78e1a..2cf389d37e 100644 --- a/src/ImageSharp/Image{TPixel}.cs +++ b/src/ImageSharp/Image{TPixel}.cs @@ -220,6 +220,11 @@ namespace SixLabors.ImageSharp /// public void Dispose() => this.Frames.Dispose(); + internal override void ApplyVisitor(IImageVisitor visitor) + { + visitor.Visit(this); + } + /// public override string ToString() => $"Image<{typeof(TPixel).Name}>: {this.Width}x{this.Height}"; diff --git a/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs b/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs index 4897cc58b4..72b78b32ac 100644 --- a/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs +++ b/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs @@ -8,6 +8,11 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { + public interface IImageProcessingContext + { + + } + /// /// An interface to queue up image operations to apply to an image. /// diff --git a/src/ImageSharp/Processing/ProcessingExtensions.cs b/src/ImageSharp/Processing/ProcessingExtensions.cs index 9d06c61d4c..c96b971117 100644 --- a/src/ImageSharp/Processing/ProcessingExtensions.cs +++ b/src/ImageSharp/Processing/ProcessingExtensions.cs @@ -13,6 +13,11 @@ namespace SixLabors.ImageSharp.Processing /// public static class ProcessingExtensions { + public static void Mutate(this Image source, Action operation) + { + + } + /// /// Applies the given operation to the mutable image. /// Useful when we need to extract information like Width/Height to parametrize the next operation working on the chain. diff --git a/src/ImageSharp/Processing/Processors/IImageProcessor.cs b/src/ImageSharp/Processing/Processors/IImageProcessor.cs index d7fe0465be..78800affbc 100644 --- a/src/ImageSharp/Processing/Processors/IImageProcessor.cs +++ b/src/ImageSharp/Processing/Processors/IImageProcessor.cs @@ -6,6 +6,12 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors { + public interface IImageProcessor + { + IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel; + } + /// /// Encapsulates methods to alter the pixels of an image. /// From 0d2c0e78c8b4bf873484aba3caa283c955df4cc0 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 1 May 2019 21:26:35 +0200 Subject: [PATCH 02/31] pixel agnostic Mutate/Clone defined --- src/ImageSharp/Image.cs | 15 ++++++-- src/ImageSharp/Image{TPixel}.cs | 19 ++++------- .../IImageProcessingContext{TPixel}.cs | 20 +++++------ .../Processing/ProcessingExtensions.cs | 34 ++++++++++++++++++- 4 files changed, 60 insertions(+), 28 deletions(-) diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs index 6bea675b9f..00a9a20755 100644 --- a/src/ImageSharp/Image.cs +++ b/src/ImageSharp/Image.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; @@ -13,8 +14,10 @@ namespace SixLabors.ImageSharp where TPixel : struct, IPixel; } - public abstract partial class Image : IImage + public abstract partial class Image : IImage, IConfigurable { + protected readonly Configuration configuration; + /// public PixelTypeInfo PixelType { get; } @@ -27,14 +30,20 @@ namespace SixLabors.ImageSharp /// public ImageMetadata Metadata { get; } - protected Image(PixelTypeInfo pixelType, ImageMetadata metadata) + /// + /// Gets the pixel buffer. + /// + Configuration IConfigurable.Configuration => this.configuration; + + protected Image(Configuration configuration, PixelTypeInfo pixelType, ImageMetadata metadata) { + this.configuration = configuration ?? Configuration.Default; this.PixelType = pixelType; this.Metadata = metadata ?? new ImageMetadata(); } public abstract void Dispose(); - internal abstract void ApplyVisitor(IImageVisitor visitor); + internal abstract void AcceptVisitor(IImageVisitor visitor); } } \ No newline at end of file diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index 2cf389d37e..64ec87c4dd 100644 --- a/src/ImageSharp/Image{TPixel}.cs +++ b/src/ImageSharp/Image{TPixel}.cs @@ -18,11 +18,9 @@ namespace SixLabors.ImageSharp /// Encapsulates an image, which consists of the pixel data for a graphics image and its attributes. /// /// The pixel format. - public sealed class Image : IImage, IConfigurable + public sealed class Image : Image where TPixel : struct, IPixel { - private readonly Configuration configuration; - /// /// Initializes a new instance of the class /// with the height and the width of the image. @@ -68,8 +66,8 @@ namespace SixLabors.ImageSharp /// The height of the image in pixels. /// The images metadata. internal Image(Configuration configuration, int width, int height, ImageMetadata metadata) + : base(configuration, PixelTypeInfo.Create(), metadata) { - this.configuration = configuration ?? Configuration.Default; this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8); this.Metadata = metadata ?? new ImageMetadata(); this.Frames = new ImageFrameCollection(this, width, height, default(TPixel)); @@ -85,8 +83,8 @@ namespace SixLabors.ImageSharp /// The height of the image in pixels. /// The images metadata. internal Image(Configuration configuration, MemorySource memorySource, int width, int height, ImageMetadata metadata) + : base(configuration, PixelTypeInfo.Create(), metadata) { - this.configuration = configuration; this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8); this.Metadata = metadata; this.Frames = new ImageFrameCollection(this, width, height, memorySource); @@ -102,8 +100,8 @@ namespace SixLabors.ImageSharp /// The color to initialize the pixels with. /// The images metadata. internal Image(Configuration configuration, int width, int height, TPixel backgroundColor, ImageMetadata metadata) + : base(configuration, PixelTypeInfo.Create(), metadata) { - this.configuration = configuration ?? Configuration.Default; this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8); this.Metadata = metadata ?? new ImageMetadata(); this.Frames = new ImageFrameCollection(this, width, height, backgroundColor); @@ -117,18 +115,14 @@ namespace SixLabors.ImageSharp /// The images metadata. /// The frames that will be owned by this image instance. internal Image(Configuration configuration, ImageMetadata metadata, IEnumerable> frames) + : base(configuration,PixelTypeInfo.Create(), metadata) { - this.configuration = configuration ?? Configuration.Default; this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8); this.Metadata = metadata ?? new ImageMetadata(); this.Frames = new ImageFrameCollection(this, frames); } - /// - /// Gets the pixel buffer. - /// - Configuration IConfigurable.Configuration => this.configuration; /// public PixelTypeInfo PixelType { get; } @@ -142,7 +136,6 @@ namespace SixLabors.ImageSharp /// public ImageMetadata Metadata { get; } - /// /// Gets the frames. /// public ImageFrameCollection Frames { get; } @@ -220,7 +213,7 @@ namespace SixLabors.ImageSharp /// public void Dispose() => this.Frames.Dispose(); - internal override void ApplyVisitor(IImageVisitor visitor) + internal override void AcceptVisitor(IImageVisitor visitor) { visitor.Visit(this); } diff --git a/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs b/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs index 72b78b32ac..1b4fbd3be5 100644 --- a/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs +++ b/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs @@ -9,16 +9,6 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { public interface IImageProcessingContext - { - - } - - /// - /// An interface to queue up image operations to apply to an image. - /// - /// The pixel format - public interface IImageProcessingContext - where TPixel : struct, IPixel { /// /// Gets a reference to the used to allocate buffers @@ -31,7 +21,15 @@ namespace SixLabors.ImageSharp.Processing /// /// The Size GetCurrentSize(); - + } + + /// + /// An interface to queue up image operations to apply to an image. + /// + /// The pixel format + public interface IImageProcessingContext : IImageProcessingContext + where TPixel : struct, IPixel + { /// /// Adds the processor to the current set of image operations to be applied. /// diff --git a/src/ImageSharp/Processing/ProcessingExtensions.cs b/src/ImageSharp/Processing/ProcessingExtensions.cs index c96b971117..a1acf3aa0b 100644 --- a/src/ImageSharp/Processing/ProcessingExtensions.cs +++ b/src/ImageSharp/Processing/ProcessingExtensions.cs @@ -13,9 +13,41 @@ namespace SixLabors.ImageSharp.Processing /// public static class ProcessingExtensions { - public static void Mutate(this Image source, Action operation) + class ProcessingVisitor : IImageVisitor { + private readonly Action operation; + + private readonly bool mutate; + public Image ResultImage { get; private set; } + + public ProcessingVisitor(Action operation, bool mutate) + { + this.operation = operation; + this.mutate = mutate; + } + + public void Visit(Image image) + where TPixel : struct, IPixel + { + IInternalImageProcessingContext operationsRunner = image.GetConfiguration() + .ImageOperationsProvider.CreateImageProcessingContext(image, this.mutate); + this.operation(operationsRunner); + this.ResultImage = operationsRunner.Apply(); + } + } + + public static void Mutate(this Image source, Action operation) + { + ProcessingVisitor visitor = new ProcessingVisitor(operation, true); + source.AcceptVisitor(visitor); + } + + public static Image Clone(this Image source, Action operation) + { + ProcessingVisitor visitor = new ProcessingVisitor(operation, false); + source.AcceptVisitor(visitor); + return visitor.ResultImage; } /// From 7fae100254b9e81823112fa201b3059b8b7be41a Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 1 May 2019 21:56:21 +0200 Subject: [PATCH 03/31] pixel-agnostic ResizeProcessor --- .../DefaultInternalImageProcessorContext.cs | 12 + .../IImageProcessingContext{TPixel}.cs | 6 +- src/ImageSharp/Processing/PadExtensions.cs | 3 +- .../Transforms/Resize/ResizeProcessor.cs | 260 ++++-------------- .../Resize/ResizeProcessorImplementation.cs | 188 +++++++++++++ src/ImageSharp/Processing/ResizeExtensions.cs | 40 +-- .../FakeImageOperationsProvider.cs | 10 + .../Image/ImageProcessingContextTests.cs | 74 ++--- 8 files changed, 326 insertions(+), 267 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessorImplementation.cs diff --git a/src/ImageSharp/Processing/DefaultInternalImageProcessorContext.cs b/src/ImageSharp/Processing/DefaultInternalImageProcessorContext.cs index 43ba259725..091da003a2 100644 --- a/src/ImageSharp/Processing/DefaultInternalImageProcessorContext.cs +++ b/src/ImageSharp/Processing/DefaultInternalImageProcessorContext.cs @@ -53,6 +53,18 @@ namespace SixLabors.ImageSharp.Processing /// public Size GetCurrentSize() => this.GetCurrentBounds().Size; + public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle) + { + var processorImplementation = processor.CreatePixelSpecificProcessor(); + return this.ApplyProcessor(processorImplementation, rectangle); + } + + public IImageProcessingContext ApplyProcessor(IImageProcessor processor) + { + var processorImplementation = processor.CreatePixelSpecificProcessor(); + return this.ApplyProcessor(processorImplementation); + } + /// public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle) { diff --git a/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs b/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs index 1b4fbd3be5..fefa973f7c 100644 --- a/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs +++ b/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs @@ -21,8 +21,12 @@ namespace SixLabors.ImageSharp.Processing /// /// The Size GetCurrentSize(); + + IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle); + + IImageProcessingContext ApplyProcessor(IImageProcessor processor); } - + /// /// An interface to queue up image operations to apply to an image. /// diff --git a/src/ImageSharp/Processing/PadExtensions.cs b/src/ImageSharp/Processing/PadExtensions.cs index f730339686..2db219795f 100644 --- a/src/ImageSharp/Processing/PadExtensions.cs +++ b/src/ImageSharp/Processing/PadExtensions.cs @@ -19,8 +19,7 @@ namespace SixLabors.ImageSharp.Processing /// The new width. /// The new height. /// The . - public static IImageProcessingContext Pad(this IImageProcessingContext source, int width, int height) - where TPixel : struct, IPixel + public static IImageProcessingContext Pad(this IImageProcessingContext source, int width, int height) { var options = new ResizeOptions { diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index e75f6014ab..4348ecdaba 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -1,98 +1,40 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. +// // Copyright (c) Six Labors and contributors. +// // Licensed under the Apache License, Version 2.0. using System; -using System.Buffers; -using System.Collections.Generic; -using System.Linq; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { - /// - /// Provides methods that allow the resizing of images using various algorithms. - /// Adapted from - /// - /// The pixel format. - internal class ResizeProcessor : TransformProcessorBase - where TPixel : struct, IPixel + public class ResizeProcessor : IImageProcessor { - // The following fields are not immutable but are optionally created on demand. - private ResizeKernelMap horizontalKernelMap; - private ResizeKernelMap verticalKernelMap; - /// - /// Initializes a new instance of the class. + /// Gets the sampler to perform the resize operation. /// - /// The resize options - /// The source image size - public ResizeProcessor(ResizeOptions options, Size sourceSize) - { - Guard.NotNull(options, nameof(options)); - Guard.NotNull(options.Sampler, nameof(options.Sampler)); - - int targetWidth = options.Size.Width; - int targetHeight = options.Size.Height; - - // Ensure size is populated across both dimensions. - // These dimensions are used to calculate the final dimensions determined by the mode algorithm. - // If only one of the incoming dimensions is 0, it will be modified here to maintain aspect ratio. - // If it is not possible to keep aspect ratio, make sure at least the minimum is is kept. - const int min = 1; - if (targetWidth == 0 && targetHeight > 0) - { - targetWidth = (int)MathF.Max(min, MathF.Round(sourceSize.Width * targetHeight / (float)sourceSize.Height)); - } - - if (targetHeight == 0 && targetWidth > 0) - { - targetHeight = (int)MathF.Max(min, MathF.Round(sourceSize.Height * targetWidth / (float)sourceSize.Width)); - } - - Guard.MustBeGreaterThan(targetWidth, 0, nameof(targetWidth)); - Guard.MustBeGreaterThan(targetHeight, 0, nameof(targetHeight)); + public IResampler Sampler { get; } - (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds(sourceSize, options, targetWidth, targetHeight); + /// + /// Gets the target width. + /// + public int Width { get; } - this.Sampler = options.Sampler; - this.Width = size.Width; - this.Height = size.Height; - this.TargetRectangle = rectangle; - this.Compand = options.Compand; - } + /// + /// Gets the target height. + /// + public int Height { get; } /// - /// Initializes a new instance of the class. + /// Gets the resize rectangle. /// - /// The sampler to perform the resize operation. - /// The target width. - /// The target height. - /// The source image size - public ResizeProcessor(IResampler sampler, int width, int height, Size sourceSize) - : this(sampler, width, height, sourceSize, new Rectangle(0, 0, width, height), false) - { - } + public Rectangle TargetRectangle { get; } /// - /// Initializes a new instance of the class. + /// Gets a value indicating whether to compress or expand individual pixel color values on processing. /// - /// The sampler to perform the resize operation. - /// The target width. - /// The target height. - /// The source image size - /// - /// The structure that specifies the portion of the target image object to draw to. - /// - /// Whether to compress or expand individual pixel color values on processing. + public bool Compand { get; } + public ResizeProcessor(IResampler sampler, int width, int height, Size sourceSize, Rectangle targetRectangle, bool compand) { Guard.NotNull(sampler, nameof(sampler)); @@ -122,149 +64,63 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.TargetRectangle = targetRectangle; this.Compand = compand; } - - /// - /// Gets the sampler to perform the resize operation. - /// - public IResampler Sampler { get; } - - /// - /// Gets the target width. - /// - public int Width { get; } - + /// - /// Gets the target height. + /// Initializes a new instance of the class. /// - public int Height { get; } - - /// - /// Gets the resize rectangle. - /// - public Rectangle TargetRectangle { get; } - - /// - /// Gets a value indicating whether to compress or expand individual pixel color values on processing. - /// - public bool Compand { get; } - - /// - protected override Image CreateDestination(Image source, Rectangle sourceRectangle) - { - // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = source.Frames.Select(x => new ImageFrame(source.GetConfiguration(), this.Width, this.Height, x.Metadata.DeepClone())); - - // Use the overload to prevent an extra frame being added - return new Image(source.GetConfiguration(), source.Metadata.DeepClone(), frames); - } - - /// - protected override void BeforeImageApply(Image source, Image destination, Rectangle sourceRectangle) + /// The resize options + /// The source image size + public ResizeProcessor(ResizeOptions options, Size sourceSize) { - if (!(this.Sampler is NearestNeighborResampler)) - { - // 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, - sourceRectangle.Width, - memoryAllocator); + Guard.NotNull(options, nameof(options)); + Guard.NotNull(options.Sampler, nameof(options.Sampler)); - this.verticalKernelMap = ResizeKernelMap.Calculate( - this.Sampler, - this.TargetRectangle.Height, - sourceRectangle.Height, - memoryAllocator); - } - } + int targetWidth = options.Size.Width; + int targetHeight = options.Size.Height; - /// - protected override void OnFrameApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) - { - // Handle resize dimensions identical to the original - if (source.Width == destination.Width && source.Height == destination.Height && sourceRectangle == this.TargetRectangle) + // Ensure size is populated across both dimensions. + // These dimensions are used to calculate the final dimensions determined by the mode algorithm. + // If only one of the incoming dimensions is 0, it will be modified here to maintain aspect ratio. + // If it is not possible to keep aspect ratio, make sure at least the minimum is is kept. + const int min = 1; + if (targetWidth == 0 && targetHeight > 0) { - // The cloned will be blank here copy all the pixel data over - source.GetPixelSpan().CopyTo(destination.GetPixelSpan()); - return; + targetWidth = (int)MathF.Max(min, MathF.Round(sourceSize.Width * targetHeight / (float)sourceSize.Height)); } - int width = this.Width; - int height = this.Height; - int sourceX = sourceRectangle.X; - int sourceY = sourceRectangle.Y; - int startY = this.TargetRectangle.Y; - int startX = this.TargetRectangle.X; - - var targetWorkingRect = Rectangle.Intersect( - this.TargetRectangle, - new Rectangle(0, 0, width, height)); - - if (this.Sampler is NearestNeighborResampler) + if (targetHeight == 0 && targetWidth > 0) { - // Scaling factors - float widthFactor = sourceRectangle.Width / (float)this.TargetRectangle.Width; - float heightFactor = sourceRectangle.Height / (float)this.TargetRectangle.Height; - - ParallelHelper.IterateRows( - targetWorkingRect, - configuration, - rows => - { - for (int y = rows.Min; y < rows.Max; y++) - { - // Y coordinates of source points - Span sourceRow = - source.GetPixelRowSpan((int)(((y - startY) * heightFactor) + sourceY)); - Span targetRow = destination.GetPixelRowSpan(y); - - for (int x = targetWorkingRect.Left; x < targetWorkingRect.Right; x++) - { - // X coordinates of source points - targetRow[x] = sourceRow[(int)(((x - startX) * widthFactor) + sourceX)]; - } - } - }); - - return; + targetHeight = (int)MathF.Max(min, MathF.Round(sourceSize.Height * targetWidth / (float)sourceSize.Width)); } - int sourceHeight = source.Height; - - PixelConversionModifiers conversionModifiers = - PixelConversionModifiers.Premultiply.ApplyCompanding(this.Compand); - - BufferArea sourceArea = source.PixelBuffer.GetArea(sourceRectangle); + Guard.MustBeGreaterThan(targetWidth, 0, nameof(targetWidth)); + Guard.MustBeGreaterThan(targetHeight, 0, nameof(targetHeight)); - // To reintroduce parallel processing, we to launch multiple workers - // for different row intervals of the image. - using (var worker = new ResizeWorker( - configuration, - sourceArea, - conversionModifiers, - this.horizontalKernelMap, - this.verticalKernelMap, - width, - targetWorkingRect, - this.TargetRectangle.Location)) - { - worker.Initialize(); + (Size size, Rectangle rectangle) = ResizeHelper.CalculateTargetLocationAndBounds(sourceSize, options, targetWidth, targetHeight); - var workingInterval = new RowInterval(targetWorkingRect.Top, targetWorkingRect.Bottom); - worker.FillDestinationPixels(workingInterval, destination.PixelBuffer); - } + this.Sampler = options.Sampler; + this.Width = size.Width; + this.Height = size.Height; + this.TargetRectangle = rectangle; + this.Compand = options.Compand; } - protected override void AfterImageApply(Image source, Image destination, Rectangle sourceRectangle) + /// + /// Initializes a new instance of the class. + /// + /// The sampler to perform the resize operation. + /// The target width. + /// The target height. + /// The source image size + public ResizeProcessor(IResampler sampler, int width, int height, Size sourceSize) + : this(sampler, width, height, sourceSize, new Rectangle(0, 0, width, height), false) { - base.AfterImageApply(source, destination, sourceRectangle); + } - // TODO: An exception in the processing chain can leave these buffers undisposed. We should consider making image processors IDisposable! - this.horizontalKernelMap?.Dispose(); - this.horizontalKernelMap = null; - this.verticalKernelMap?.Dispose(); - this.verticalKernelMap = null; + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel + { + return new ResizeProcessorImplementation(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessorImplementation.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessorImplementation.cs new file mode 100644 index 0000000000..6c3db3da77 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessorImplementation.cs @@ -0,0 +1,188 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Linq; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Memory; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Transforms +{ + // The non-generic processor is responsible for: + // - Encapsulating the parameters of the processor + // - Implementing a factory method to create the pixel-specific processor that contains the implementation + + /// + /// Provides methods that allow the resizing of images using various algorithms. + /// Adapted from + /// + /// The pixel format. + internal class ResizeProcessorImplementation : TransformProcessorBase + where TPixel : struct, IPixel + { + // The following fields are not immutable but are optionally created on demand. + private ResizeKernelMap horizontalKernelMap; + private ResizeKernelMap verticalKernelMap; + + private readonly ResizeProcessor parameterSource; + + public ResizeProcessorImplementation(ResizeProcessor parameterSource) + { + this.parameterSource = parameterSource; + } + + /// + /// Gets the sampler to perform the resize operation. + /// + public IResampler Sampler => this.parameterSource.Sampler; + + /// + /// Gets the target width. + /// + public int Width => this.parameterSource.Width; + + /// + /// Gets the target height. + /// + public int Height => this.parameterSource.Height; + + /// + /// Gets the 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; + + /// + protected override Image CreateDestination(Image source, Rectangle sourceRectangle) + { + // We will always be creating the clone even for mutate because we may need to resize the canvas + IEnumerable> frames = source.Frames.Select(x => new ImageFrame(source.GetConfiguration(), this.Width, this.Height, x.Metadata.DeepClone())); + + // Use the overload to prevent an extra frame being added + return new Image(source.GetConfiguration(), source.Metadata.DeepClone(), frames); + } + + /// + protected override void BeforeImageApply(Image source, Image destination, Rectangle sourceRectangle) + { + if (!(this.Sampler is NearestNeighborResampler)) + { + // 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, + sourceRectangle.Width, + memoryAllocator); + + this.verticalKernelMap = ResizeKernelMap.Calculate( + this.Sampler, + this.TargetRectangle.Height, + sourceRectangle.Height, + memoryAllocator); + } + } + + /// + protected override void OnFrameApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) + { + // Handle resize dimensions identical to the original + if (source.Width == destination.Width && source.Height == destination.Height && sourceRectangle == this.TargetRectangle) + { + // The cloned will be blank here copy all the pixel data over + source.GetPixelSpan().CopyTo(destination.GetPixelSpan()); + return; + } + + int width = this.Width; + int height = this.Height; + int sourceX = sourceRectangle.X; + int sourceY = sourceRectangle.Y; + int startY = this.TargetRectangle.Y; + int startX = this.TargetRectangle.X; + + var targetWorkingRect = Rectangle.Intersect( + this.TargetRectangle, + new Rectangle(0, 0, width, height)); + + if (this.Sampler is NearestNeighborResampler) + { + // Scaling factors + float widthFactor = sourceRectangle.Width / (float)this.TargetRectangle.Width; + float heightFactor = sourceRectangle.Height / (float)this.TargetRectangle.Height; + + ParallelHelper.IterateRows( + targetWorkingRect, + configuration, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + // Y coordinates of source points + Span sourceRow = + source.GetPixelRowSpan((int)(((y - startY) * heightFactor) + sourceY)); + Span targetRow = destination.GetPixelRowSpan(y); + + for (int x = targetWorkingRect.Left; x < targetWorkingRect.Right; x++) + { + // X coordinates of source points + targetRow[x] = sourceRow[(int)(((x - startX) * widthFactor) + sourceX)]; + } + } + }); + + return; + } + + int sourceHeight = source.Height; + + PixelConversionModifiers conversionModifiers = + PixelConversionModifiers.Premultiply.ApplyCompanding(this.Compand); + + BufferArea sourceArea = source.PixelBuffer.GetArea(sourceRectangle); + + // To reintroduce parallel processing, we to launch multiple workers + // for different row intervals of the image. + using (var worker = new ResizeWorker( + configuration, + sourceArea, + conversionModifiers, + this.horizontalKernelMap, + this.verticalKernelMap, + width, + targetWorkingRect, + this.TargetRectangle.Location)) + { + worker.Initialize(); + + var workingInterval = new RowInterval(targetWorkingRect.Top, targetWorkingRect.Bottom); + worker.FillDestinationPixels(workingInterval, destination.PixelBuffer); + } + } + + protected override void AfterImageApply(Image source, Image destination, Rectangle sourceRectangle) + { + base.AfterImageApply(source, destination, sourceRectangle); + + // TODO: An exception in the processing chain can leave these buffers undisposed. We should consider making image processors IDisposable! + this.horizontalKernelMap?.Dispose(); + this.horizontalKernelMap = null; + this.verticalKernelMap?.Dispose(); + this.verticalKernelMap = null; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/ResizeExtensions.cs b/src/ImageSharp/Processing/ResizeExtensions.cs index 7b6c14d7de..cf029eb152 100644 --- a/src/ImageSharp/Processing/ResizeExtensions.cs +++ b/src/ImageSharp/Processing/ResizeExtensions.cs @@ -20,9 +20,8 @@ namespace SixLabors.ImageSharp.Processing /// The resize options. /// The /// Passing zero for one of height or width within the resize options will automatically preserve the aspect ratio of the original image or the nearest possible ratio. - public static IImageProcessingContext Resize(this IImageProcessingContext source, ResizeOptions options) - where TPixel : struct, IPixel - => source.ApplyProcessor(new ResizeProcessor(options, source.GetCurrentSize())); + public static IImageProcessingContext Resize(this IImageProcessingContext source, ResizeOptions options) + => source.ApplyProcessor(new ResizeProcessor(options, source.GetCurrentSize())); /// /// Resizes an image to the given . @@ -32,8 +31,7 @@ namespace SixLabors.ImageSharp.Processing /// The target image size. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. - public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size) - where TPixel : struct, IPixel + public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size) => Resize(source, size.Width, size.Height, KnownResamplers.Bicubic, false); /// @@ -45,8 +43,7 @@ namespace SixLabors.ImageSharp.Processing /// Whether to compress and expand the image color-space to gamma correct the image during processing. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. - public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size, bool compand) - where TPixel : struct, IPixel + public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size, bool compand) => Resize(source, size.Width, size.Height, KnownResamplers.Bicubic, compand); /// @@ -58,8 +55,7 @@ namespace SixLabors.ImageSharp.Processing /// The target image height. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. - public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height) - where TPixel : struct, IPixel + public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height) => Resize(source, width, height, KnownResamplers.Bicubic, false); /// @@ -72,8 +68,7 @@ namespace SixLabors.ImageSharp.Processing /// Whether to compress and expand the image color-space to gamma correct the image during processing. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. - public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, bool compand) - where TPixel : struct, IPixel + public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, bool compand) => Resize(source, width, height, KnownResamplers.Bicubic, compand); /// @@ -86,8 +81,7 @@ namespace SixLabors.ImageSharp.Processing /// The to perform the resampling. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. - public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, IResampler sampler) - where TPixel : struct, IPixel + public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, IResampler sampler) => Resize(source, width, height, sampler, false); /// @@ -100,8 +94,7 @@ namespace SixLabors.ImageSharp.Processing /// Whether to compress and expand the image color-space to gamma correct the image during processing. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. - public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size, IResampler sampler, bool compand) - where TPixel : struct, IPixel + public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size, IResampler sampler, bool compand) => Resize(source, size.Width, size.Height, sampler, new Rectangle(0, 0, size.Width, size.Height), compand); /// @@ -115,8 +108,7 @@ namespace SixLabors.ImageSharp.Processing /// Whether to compress and expand the image color-space to gamma correct the image during processing. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. - public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, IResampler sampler, bool compand) - where TPixel : struct, IPixel + public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, IResampler sampler, bool compand) => Resize(source, width, height, sampler, new Rectangle(0, 0, width, height), compand); /// @@ -137,16 +129,15 @@ namespace SixLabors.ImageSharp.Processing /// Whether to compress and expand the image color-space to gamma correct the image during processing. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. - public static IImageProcessingContext Resize( - this IImageProcessingContext source, + public static IImageProcessingContext Resize( + this IImageProcessingContext source, int width, int height, IResampler sampler, Rectangle sourceRectangle, Rectangle targetRectangle, bool compand) - where TPixel : struct, IPixel - => source.ApplyProcessor(new ResizeProcessor(sampler, width, height, source.GetCurrentSize(), targetRectangle, compand), sourceRectangle); + => source.ApplyProcessor(new ResizeProcessor(sampler, width, height, source.GetCurrentSize(), targetRectangle, compand), sourceRectangle); /// /// Resizes an image to the given width and height with the given sampler and source rectangle. @@ -162,14 +153,13 @@ namespace SixLabors.ImageSharp.Processing /// Whether to compress and expand the image color-space to gamma correct the image during processing. /// The /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. - public static IImageProcessingContext Resize( - this IImageProcessingContext source, + public static IImageProcessingContext Resize( + this IImageProcessingContext source, int width, int height, IResampler sampler, Rectangle targetRectangle, bool compand) - where TPixel : struct, IPixel - => source.ApplyProcessor(new ResizeProcessor(sampler, width, height, source.GetCurrentSize(), targetRectangle, compand)); + => source.ApplyProcessor(new ResizeProcessor(sampler, width, height, source.GetCurrentSize(), targetRectangle, compand)); } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs b/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs index ff4014e616..276cc5da85 100644 --- a/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs +++ b/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs @@ -67,6 +67,16 @@ namespace SixLabors.ImageSharp.Tests return this.Source.Size(); } + public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle) + { + throw new System.NotImplementedException(); + } + + public IImageProcessingContext ApplyProcessor(IImageProcessor processor) + { + throw new System.NotImplementedException(); + } + public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle) { this.Applied.Add(new AppliedOperation diff --git a/tests/ImageSharp.Tests/Image/ImageProcessingContextTests.cs b/tests/ImageSharp.Tests/Image/ImageProcessingContextTests.cs index 041b6c8468..6dadc6e7a2 100644 --- a/tests/ImageSharp.Tests/Image/ImageProcessingContextTests.cs +++ b/tests/ImageSharp.Tests/Image/ImageProcessingContextTests.cs @@ -10,43 +10,43 @@ namespace SixLabors.ImageSharp.Tests { public class ImageProcessingContextTests { - [Fact] - public void MutatedSizeIsAccuratePerOperation() - { - var x500 = new Size(500, 500); - var x400 = new Size(400, 400); - var x300 = new Size(300, 300); - var x200 = new Size(200, 200); - var x100 = new Size(100, 100); - using (var image = new Image(500, 500)) - { - image.Mutate(x => - x.AssertSize(x500) - .Resize(x400).AssertSize(x400) - .Resize(x300).AssertSize(x300) - .Resize(x200).AssertSize(x200) - .Resize(x100).AssertSize(x100)); - } - } - - [Fact] - public void ClonedSizeIsAccuratePerOperation() - { - var x500 = new Size(500, 500); - var x400 = new Size(400, 400); - var x300 = new Size(300, 300); - var x200 = new Size(200, 200); - var x100 = new Size(100, 100); - using (var image = new Image(500, 500)) - { - image.Clone(x => - x.AssertSize(x500) - .Resize(x400).AssertSize(x400) - .Resize(x300).AssertSize(x300) - .Resize(x200).AssertSize(x200) - .Resize(x100).AssertSize(x100)); - } - } + // [Fact] + // public void MutatedSizeIsAccuratePerOperation() + // { + // var x500 = new Size(500, 500); + // var x400 = new Size(400, 400); + // var x300 = new Size(300, 300); + // var x200 = new Size(200, 200); + // var x100 = new Size(100, 100); + // using (var image = new Image(500, 500)) + // { + // image.Mutate(x => + // x.AssertSize(x500) + // .Resize(x400).AssertSize(x400) + // .Resize(x300).AssertSize(x300) + // .Resize(x200).AssertSize(x200) + // .Resize(x100).AssertSize(x100)); + // } + // } + // + // [Fact] + // public void ClonedSizeIsAccuratePerOperation() + // { + // var x500 = new Size(500, 500); + // var x400 = new Size(400, 400); + // var x300 = new Size(300, 300); + // var x200 = new Size(200, 200); + // var x100 = new Size(100, 100); + // using (var image = new Image(500, 500)) + // { + // image.Clone(x => + // x.AssertSize(x500) + // .Resize(x400).AssertSize(x400) + // .Resize(x300).AssertSize(x300) + // .Resize(x200).AssertSize(x200) + // .Resize(x100).AssertSize(x100)); + // } + // } } public static class SizeAssertationExtensions From ebfecf4fda287502d83dd2d21ec9e6d1653e8b31 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 1 May 2019 22:25:37 +0200 Subject: [PATCH 04/31] pixel-agnostic decoder API --- .../Advanced/AdvancedImageExtensions.cs | 3 +- src/ImageSharp/Formats/Bmp/BmpDecoder.cs | 2 + src/ImageSharp/Formats/Gif/GifDecoder.cs | 2 + src/ImageSharp/Formats/IImageDecoder.cs | 2 + src/ImageSharp/Formats/Jpeg/JpegDecoder.cs | 2 + src/ImageSharp/Formats/Png/PngDecoder.cs | 2 + src/ImageSharp/Image.Decode.cs | 12 ++++++ src/ImageSharp/Image.FromFile.cs | 30 ++++++-------- src/ImageSharp/Image.FromStream.cs | 41 ++++++++++++------- src/ImageSharp/Image.cs | 36 ++++++++++++++++ src/ImageSharp/ImageExtensions.cs | 9 ++-- src/ImageSharp/Image{TPixel}.cs | 14 ------- .../Processors/Transforms/ResizeTests.cs | 16 ++++++++ tests/ImageSharp.Tests/TestFormat.cs | 2 + .../ReferenceCodecs/MagickReferenceDecoder.cs | 2 + .../SystemDrawingReferenceDecoder.cs | 2 + .../Tests/TestImageProviderTests.cs | 4 ++ 17 files changed, 127 insertions(+), 54 deletions(-) diff --git a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs index bdcb4c10f1..378b6e8b96 100644 --- a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs +++ b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs @@ -21,8 +21,7 @@ namespace SixLabors.ImageSharp.Advanced /// The Pixel format. /// The source image. /// Returns the configuration. - public static Configuration GetConfiguration(this Image source) - where TPixel : struct, IPixel + public static Configuration GetConfiguration(this Image source) => GetConfiguration((IConfigurable)source); /// diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs index 82af2a671e..4922a92003 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs @@ -30,6 +30,8 @@ namespace SixLabors.ImageSharp.Formats.Bmp return new BmpDecoderCore(configuration, this).Decode(stream); } + public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); + /// public IImageInfo Identify(Configuration configuration, Stream stream) { diff --git a/src/ImageSharp/Formats/Gif/GifDecoder.cs b/src/ImageSharp/Formats/Gif/GifDecoder.cs index 6af75f2d0f..6498f4aa27 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoder.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoder.cs @@ -44,5 +44,7 @@ namespace SixLabors.ImageSharp.Formats.Gif var decoder = new GifDecoderCore(configuration, this); return decoder.Identify(stream); } + + public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); } } diff --git a/src/ImageSharp/Formats/IImageDecoder.cs b/src/ImageSharp/Formats/IImageDecoder.cs index ffc40314d8..625c4efb6f 100644 --- a/src/ImageSharp/Formats/IImageDecoder.cs +++ b/src/ImageSharp/Formats/IImageDecoder.cs @@ -20,5 +20,7 @@ namespace SixLabors.ImageSharp.Formats /// The decoded image Image Decode(Configuration configuration, Stream stream) where TPixel : struct, IPixel; + + Image Decode(Configuration configuration, Stream stream); } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs index 57b70dd26e..7459abec56 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs @@ -38,5 +38,7 @@ namespace SixLabors.ImageSharp.Formats.Jpeg return decoder.Identify(stream); } } + + public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Png/PngDecoder.cs b/src/ImageSharp/Formats/Png/PngDecoder.cs index 39dfb1d0bd..e8c5ac8e81 100644 --- a/src/ImageSharp/Formats/Png/PngDecoder.cs +++ b/src/ImageSharp/Formats/Png/PngDecoder.cs @@ -59,5 +59,7 @@ namespace SixLabors.ImageSharp.Formats.Png var decoder = new PngDecoderCore(configuration, this); return decoder.Identify(stream); } + + public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); } } \ No newline at end of file diff --git a/src/ImageSharp/Image.Decode.cs b/src/ImageSharp/Image.Decode.cs index 9e83d173f2..36944dc2c0 100644 --- a/src/ImageSharp/Image.Decode.cs +++ b/src/ImageSharp/Image.Decode.cs @@ -102,6 +102,18 @@ namespace SixLabors.ImageSharp Image img = decoder.Decode(config, stream); return (img, format); } + + private static (Image img, IImageFormat format) Decode(Stream stream, Configuration config) + { + IImageDecoder decoder = DiscoverDecoder(stream, config, out IImageFormat format); + if (decoder is null) + { + return (null, null); + } + + Image img = decoder.Decode(config, stream); + return (img, format); + } /// /// Reads the raw image information from the specified stream. diff --git a/src/ImageSharp/Image.FromFile.cs b/src/ImageSharp/Image.FromFile.cs index b13cef4824..80267146d5 100644 --- a/src/ImageSharp/Image.FromFile.cs +++ b/src/ImageSharp/Image.FromFile.cs @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp /// Thrown if the stream is not readable nor seekable. /// /// A new . - public static Image Load(string path) => Load(path); + public static Image Load(string path) => Load(path); /// /// Create a new instance of the class from the given file. @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp /// Thrown if the stream is not readable nor seekable. /// /// A new . - public static Image Load(string path, out IImageFormat format) => Load(path, out format); + public static Image Load(string path, out IImageFormat format) => Load(path, out format); /// /// Create a new instance of the class from the given file. @@ -68,19 +68,7 @@ namespace SixLabors.ImageSharp /// Thrown if the stream is not readable nor seekable. /// /// A new . - public static Image Load(Configuration config, string path) => Load(config, path); - - /// - /// Create a new instance of the class from the given file. - /// - /// The config for the decoder. - /// The file path to the image. - /// The mime type of the decoded image. - /// - /// Thrown if the stream is not readable nor seekable. - /// - /// A new . - public static Image Load(Configuration config, string path, out IImageFormat format) => Load(config, path, out format); + public static Image Load(Configuration config, string path) => Load(config, path); /// /// Create a new instance of the class from the given file. @@ -92,7 +80,7 @@ namespace SixLabors.ImageSharp /// Thrown if the stream is not readable nor seekable. /// /// A new . - public static Image Load(Configuration config, string path, IImageDecoder decoder) => Load(config, path, decoder); + public static Image Load(Configuration config, string path, IImageDecoder decoder) => Load(config, path, decoder); /// /// Create a new instance of the class from the given file. @@ -103,7 +91,7 @@ namespace SixLabors.ImageSharp /// Thrown if the stream is not readable nor seekable. /// /// A new . - public static Image Load(string path, IImageDecoder decoder) => Load(path, decoder); + public static Image Load(string path, IImageDecoder decoder) => Load(path, decoder); /// /// Create a new instance of the class from the given file. @@ -174,6 +162,14 @@ namespace SixLabors.ImageSharp return Load(config, stream, out format); } } + + public static Image Load(Configuration config, string path, out IImageFormat format) + { + using (Stream stream = config.FileSystem.OpenRead(path)) + { + return Load(config, stream, out format); + } + } /// /// Create a new instance of the class from the given file. diff --git a/src/ImageSharp/Image.FromStream.cs b/src/ImageSharp/Image.FromStream.cs index 3236e00072..8848225f54 100644 --- a/src/ImageSharp/Image.FromStream.cs +++ b/src/ImageSharp/Image.FromStream.cs @@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp /// the mime type of the decoded image. /// Thrown if the stream is not readable. /// A new .> - public static Image Load(Stream stream, out IImageFormat format) => Load(stream, out format); + public static Image Load(Stream stream, out IImageFormat format) => Load(stream, out format); /// /// Create a new instance of the class from the given stream. @@ -70,7 +70,7 @@ namespace SixLabors.ImageSharp /// The stream containing image information. /// Thrown if the stream is not readable. /// A new .> - public static Image Load(Stream stream) => Load(stream); + public static Image Load(Stream stream) => Load(stream); /// /// Create a new instance of the class from the given stream. @@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp /// The decoder. /// Thrown if the stream is not readable. /// A new .> - public static Image Load(Stream stream, IImageDecoder decoder) => Load(stream, decoder); + public static Image Load(Stream stream, IImageDecoder decoder) => Load(stream, decoder); /// /// Create a new instance of the class from the given stream. @@ -88,18 +88,7 @@ namespace SixLabors.ImageSharp /// The stream containing image information. /// Thrown if the stream is not readable. /// A new .> - public static Image Load(Configuration config, Stream stream) => Load(config, stream); - - /// - /// Create a new instance of the class from the given stream. - /// - /// The config for the decoder. - /// The stream containing image information. - /// the mime type of the decoded image. - /// Thrown if the stream is not readable. - /// A new .> - public static Image Load(Configuration config, Stream stream, out IImageFormat format) - => Load(config, stream, out format); + public static Image Load(Configuration config, Stream stream) => Load(config, stream); /// /// Create a new instance of the class from the given stream. @@ -193,6 +182,28 @@ namespace SixLabors.ImageSharp throw new NotSupportedException(sb.ToString()); } + private static Image Load(Configuration config, Stream stream, out IImageFormat format) + { + config = config ?? Configuration.Default; + (Image img, IImageFormat format) data = WithSeekableStream(config, stream, s => Decode(s, config)); + + format = data.format; + + if (data.img != null) + { + return data.img; + } + + var sb = new StringBuilder(); + sb.AppendLine("Image cannot be loaded. Available decoders:"); + + foreach (KeyValuePair val in config.ImageFormatsManager.ImageDecoders) + { + sb.AppendLine($" - {val.Key.Name} : {val.Value.GetType().Name}"); + } + + throw new NotSupportedException(sb.ToString()); + } private static T WithSeekableStream(Configuration config, Stream stream, Func action) { diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs index 00a9a20755..a27f16ab30 100644 --- a/src/ImageSharp/Image.cs +++ b/src/ImageSharp/Image.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System.IO; + using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Metadata; @@ -45,5 +47,39 @@ namespace SixLabors.ImageSharp public abstract void Dispose(); internal abstract void AcceptVisitor(IImageVisitor visitor); + + /// + /// Saves the image to the given stream using the given image encoder. + /// + /// The stream to save the image to. + /// The encoder to save the image with. + /// Thrown if the stream or encoder is null. + public void Save(Stream stream, IImageEncoder encoder) + { + Guard.NotNull(stream, nameof(stream)); + Guard.NotNull(encoder, nameof(encoder)); + + EncodeVisitor visitor = new EncodeVisitor(encoder, stream); + this.AcceptVisitor(visitor); + } + + class EncodeVisitor : IImageVisitor + { + private readonly IImageEncoder encoder; + + private readonly Stream stream; + + public EncodeVisitor(IImageEncoder encoder, Stream stream) + { + this.encoder = encoder; + this.stream = stream; + } + + public void Visit(Image image) + where TPixel : struct, IPixel + { + this.encoder.Encode(image, this.stream); + } + } } } \ No newline at end of file diff --git a/src/ImageSharp/ImageExtensions.cs b/src/ImageSharp/ImageExtensions.cs index 5010451b8e..3bd60694d5 100644 --- a/src/ImageSharp/ImageExtensions.cs +++ b/src/ImageSharp/ImageExtensions.cs @@ -23,8 +23,7 @@ namespace SixLabors.ImageSharp /// The source image. /// The file path to save the image to. /// Thrown if the stream is null. - public static void Save(this Image source, string filePath) - where TPixel : struct, IPixel + public static void Save(this Image source, string filePath) { Guard.NotNullOrWhiteSpace(filePath, nameof(filePath)); @@ -67,8 +66,7 @@ namespace SixLabors.ImageSharp /// The file path to save the image to. /// The encoder to save the image with. /// Thrown if the encoder is null. - public static void Save(this Image source, string filePath, IImageEncoder encoder) - where TPixel : struct, IPixel + public static void Save(this Image source, string filePath, IImageEncoder encoder) { Guard.NotNull(encoder, nameof(encoder)); using (Stream fs = source.GetConfiguration().FileSystem.Create(filePath)) @@ -85,8 +83,7 @@ namespace SixLabors.ImageSharp /// The stream to save the image to. /// The format to save the image in. /// Thrown if the stream is null. - public static void Save(this Image source, Stream stream, IImageFormat format) - where TPixel : struct, IPixel + public static void Save(this Image source, Stream stream, IImageFormat format) { Guard.NotNull(format, nameof(format)); IImageEncoder encoder = source.GetConfiguration().ImageFormatsManager.FindEncoder(format); diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index 64ec87c4dd..e37f3e4c1c 100644 --- a/src/ImageSharp/Image{TPixel}.cs +++ b/src/ImageSharp/Image{TPixel}.cs @@ -158,20 +158,6 @@ namespace SixLabors.ImageSharp set => this.PixelSource.PixelBuffer[x, y] = value; } - /// - /// Saves the image to the given stream using the given image encoder. - /// - /// The stream to save the image to. - /// The encoder to save the image with. - /// Thrown if the stream or encoder is null. - public void Save(Stream stream, IImageEncoder encoder) - { - Guard.NotNull(stream, nameof(stream)); - Guard.NotNull(encoder, nameof(encoder)); - - encoder.Encode(this, stream); - } - /// /// Clones the current image /// diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs index e3a43a652e..c7af8d4688 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs @@ -42,6 +42,22 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.07F); + [Fact] + public void Resize_PixelAgnostic() + { + var filePath = TestFile.GetInputFileFullPath(TestImages.Jpeg.Baseline.Calliphora); + + using (Image image = Image.Load(filePath)) + { + image.Mutate(x => x.Resize(image.Size() / 2)); + string path = System.IO.Path.Combine( + TestEnvironment.CreateOutputDirectory(nameof(ResizeTests)), + nameof(this.Resize_PixelAgnostic) + ".png"); + + image.Save(path); + } + } + [Theory( Skip = "Debug only, enable manually" )] diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index 64357a17e1..91e31b356e 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -199,6 +199,8 @@ namespace SixLabors.ImageSharp.Tests } public bool IsSupportedFileFormat(Span header) => testFormat.IsSupportedFileFormat(header); + + public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); } public class TestEncoder : ImageSharp.Formats.IImageEncoder diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs index 3dd330e4d3..e81714ddc4 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/MagickReferenceDecoder.cs @@ -57,5 +57,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs return result; } } + + public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs index 7a775c0817..2de3c03aaf 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ReferenceCodecs/SystemDrawingReferenceDecoder.cs @@ -51,5 +51,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs return new ImageInfo(pixelType, sourceBitmap.Width, sourceBitmap.Height, new ImageMetadata()); } } + + public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs index 4ef6a582c9..b49baa5c41 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestImageProviderTests.cs @@ -379,6 +379,8 @@ namespace SixLabors.ImageSharp.Tests this.callerName = name; invocationCounts[name] = 0; } + + public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); } private class TestDecoderWithParameters : IImageDecoder @@ -416,6 +418,8 @@ namespace SixLabors.ImageSharp.Tests this.callerName = name; invocationCounts[name] = 0; } + + public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); } } } \ No newline at end of file From ff045a13624575d38dd3cf142c074511c8aeb1b1 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Wed, 1 May 2019 22:38:25 +0200 Subject: [PATCH 05/31] refactor FilterProcessor stuff --- src/ImageSharp/IImage.cs | 2 +- .../Processing/BlackWhiteExtensions.cs | 12 ++-- src/ImageSharp/Processing/FilterExtensions.cs | 12 ++-- .../Processing/OpacityExtensions.cs | 12 ++-- .../Processors/Filters/BlackWhiteProcessor.cs | 3 +- .../Processors/Filters/FilterProcessor.cs | 43 ++------------- .../Filters/FilterProcessorImplementation.cs | 55 +++++++++++++++++++ .../Processors/Filters/OpacityProcessor.cs | 3 +- .../Transforms/Resize/ResizeProcessor.cs | 7 ++- .../Resize/ResizeProcessorImplementation.cs | 4 -- .../Processors/Transforms/ResizeTests.cs | 1 + 11 files changed, 82 insertions(+), 72 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Filters/FilterProcessorImplementation.cs diff --git a/src/ImageSharp/IImage.cs b/src/ImageSharp/IImage.cs index fb251cfd5a..b9e2cee616 100644 --- a/src/ImageSharp/IImage.cs +++ b/src/ImageSharp/IImage.cs @@ -4,7 +4,7 @@ using System; namespace SixLabors.ImageSharp -{ +{ /// /// Encapsulates the properties and methods that describe an image. /// diff --git a/src/ImageSharp/Processing/BlackWhiteExtensions.cs b/src/ImageSharp/Processing/BlackWhiteExtensions.cs index 0484fa84e1..5dc2341e57 100644 --- a/src/ImageSharp/Processing/BlackWhiteExtensions.cs +++ b/src/ImageSharp/Processing/BlackWhiteExtensions.cs @@ -15,24 +15,20 @@ namespace SixLabors.ImageSharp.Processing /// /// Applies black and white toning to the image. /// - /// The pixel format. /// The image this method extends. /// The . - public static IImageProcessingContext BlackWhite(this IImageProcessingContext source) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BlackWhiteProcessor()); + public static IImageProcessingContext BlackWhite(this IImageProcessingContext source) + => source.ApplyProcessor(new BlackWhiteProcessor()); /// /// Applies black and white toning to the image. /// - /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext BlackWhite(this IImageProcessingContext source, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BlackWhiteProcessor(), rectangle); + public static IImageProcessingContext BlackWhite(this IImageProcessingContext source, Rectangle rectangle) + => source.ApplyProcessor(new BlackWhiteProcessor(), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/FilterExtensions.cs b/src/ImageSharp/Processing/FilterExtensions.cs index 70ac232863..2a1c6bc7f9 100644 --- a/src/ImageSharp/Processing/FilterExtensions.cs +++ b/src/ImageSharp/Processing/FilterExtensions.cs @@ -16,26 +16,22 @@ namespace SixLabors.ImageSharp.Processing /// /// Filters an image but the given color matrix /// - /// The pixel format. /// The image this method extends. /// The filter color matrix /// The . - public static IImageProcessingContext Filter(this IImageProcessingContext source, ColorMatrix matrix) - where TPixel : struct, IPixel - => source.ApplyProcessor(new FilterProcessor(matrix)); + public static IImageProcessingContext Filter(this IImageProcessingContext source, ColorMatrix matrix) + => source.ApplyProcessor(new FilterProcessor(matrix)); /// /// Filters an image but the given color matrix /// - /// The pixel format. /// The image this method extends. /// The filter color matrix /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext Filter(this IImageProcessingContext source, ColorMatrix matrix, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new FilterProcessor(matrix), rectangle); + public static IImageProcessingContext Filter(this IImageProcessingContext source, ColorMatrix matrix, Rectangle rectangle) + => source.ApplyProcessor(new FilterProcessor(matrix), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/OpacityExtensions.cs b/src/ImageSharp/Processing/OpacityExtensions.cs index fc3fd331de..6d9198648c 100644 --- a/src/ImageSharp/Processing/OpacityExtensions.cs +++ b/src/ImageSharp/Processing/OpacityExtensions.cs @@ -15,26 +15,22 @@ namespace SixLabors.ImageSharp.Processing /// /// Multiplies the alpha component of the image. /// - /// The pixel format. /// The image this method extends. /// The proportion of the conversion. Must be between 0 and 1. /// The . - public static IImageProcessingContext Opacity(this IImageProcessingContext source, float amount) - where TPixel : struct, IPixel - => source.ApplyProcessor(new OpacityProcessor(amount)); + public static IImageProcessingContext Opacity(this IImageProcessingContext source, float amount) + => source.ApplyProcessor(new OpacityProcessor(amount)); /// /// Multiplies the alpha component of the image. /// - /// The pixel format. /// The image this method extends. /// The proportion of the conversion. Must be between 0 and 1. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext Opacity(this IImageProcessingContext source, float amount, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new OpacityProcessor(amount), rectangle); + public static IImageProcessingContext Opacity(this IImageProcessingContext source, float amount, Rectangle rectangle) + => source.ApplyProcessor(new OpacityProcessor(amount), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs index 9925ce5c21..8bf925cf0f 100644 --- a/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs @@ -9,8 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// Applies a black and white filter matrix to the image /// /// The pixel format. - internal class BlackWhiteProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class BlackWhiteProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs index dbd8433410..3f9f586877 100644 --- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs @@ -1,25 +1,15 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Numerics; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Filters { - /// - /// Provides methods that accept a matrix to apply free-form filters to images. - /// - /// The pixel format. - internal class FilterProcessor : ImageProcessor - where TPixel : struct, IPixel + public class FilterProcessor : IImageProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The matrix used to apply the image filter public FilterProcessor(ColorMatrix matrix) => this.Matrix = matrix; @@ -29,31 +19,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// public ColorMatrix Matrix { get; } - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); - int startX = interest.X; - - ColorMatrix matrix = this.Matrix; - - ParallelHelper.IterateRowsWithTempBuffer( - interest, - configuration, - (rows, vectorBuffer) => - { - for (int y = rows.Min; y < rows.Max; y++) - { - Span vectorSpan = vectorBuffer.Span; - int length = vectorSpan.Length; - Span rowSpan = source.GetPixelRowSpan(y).Slice(startX, length); - PixelOperations.Instance.ToVector4(configuration, rowSpan, vectorSpan); - - Vector4Utils.Transform(vectorSpan, ref matrix); - - PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan, rowSpan); - } - }); + return new FilterProcessorImplementation(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessorImplementation.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessorImplementation.cs new file mode 100644 index 0000000000..1eaaf1ea5b --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessorImplementation.cs @@ -0,0 +1,55 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Filters +{ + /// + /// Provides methods that accept a matrix to apply free-form filters to images. + /// + /// The pixel format. + internal class FilterProcessorImplementation : ImageProcessor + where TPixel : struct, IPixel + { + private readonly FilterProcessor paramterSource; + + public FilterProcessorImplementation(FilterProcessor paramterSource) + { + this.paramterSource = paramterSource; + } + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); + int startX = interest.X; + + ColorMatrix matrix = this.paramterSource.Matrix; + + ParallelHelper.IterateRowsWithTempBuffer( + interest, + configuration, + (rows, vectorBuffer) => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span vectorSpan = vectorBuffer.Span; + int length = vectorSpan.Length; + Span rowSpan = source.GetPixelRowSpan(y).Slice(startX, length); + PixelOperations.Instance.ToVector4(configuration, rowSpan, vectorSpan); + + Vector4Utils.Transform(vectorSpan, ref matrix); + + PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan, rowSpan); + } + }); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs index 0fea61cad9..2d6bf6e833 100644 --- a/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs @@ -9,8 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// Applies an opacity filter matrix using the given amount. /// /// The pixel format. - internal class OpacityProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class OpacityProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index 4348ecdaba..d3077b7e65 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -1,5 +1,5 @@ -// // Copyright (c) Six Labors and contributors. -// // Licensed under the Apache License, Version 2.0. +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. using System; @@ -8,6 +8,9 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { + // The non-generic processor is responsible for: + // - Encapsulating the parameters of the processor + // - Implementing a factory method to create the pixel-specific processor that contains the implementation public class ResizeProcessor : IImageProcessor { /// diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessorImplementation.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessorImplementation.cs index 6c3db3da77..1a8bb931e0 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessorImplementation.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessorImplementation.cs @@ -18,10 +18,6 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { - // The non-generic processor is responsible for: - // - Encapsulating the parameters of the processor - // - Implementing a factory method to create the pixel-specific processor that contains the implementation - /// /// Provides methods that allow the resizing of images using various algorithms. /// Adapted from diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs index c7af8d4688..9bf9079d41 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/ResizeTests.cs @@ -40,6 +40,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms nameof(KnownResamplers.Lanczos5), }; + private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.07F); [Fact] From 1978731ac12de8f5b745763a33e0e5b347257124 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 00:15:12 +0200 Subject: [PATCH 06/31] basic fixes after rebase + temporarily comment out target frameworks --- src/ImageSharp/Formats/PixelTypeInfo.cs | 8 ++++++++ src/ImageSharp/Image.Decode.cs | 2 +- src/ImageSharp/Image.FromBytes.cs | 2 +- src/ImageSharp/Image.FromFile.cs | 2 +- src/ImageSharp/Image.FromStream.cs | 2 +- src/ImageSharp/Image.LoadPixelData.cs | 2 +- src/ImageSharp/Image.WrapMemory.cs | 2 +- src/ImageSharp/ImageSharp.csproj | 5 ++++- src/ImageSharp/Image{TPixel}.cs | 6 +++--- 9 files changed, 21 insertions(+), 10 deletions(-) diff --git a/src/ImageSharp/Formats/PixelTypeInfo.cs b/src/ImageSharp/Formats/PixelTypeInfo.cs index ed21b91bfc..66d04f39fd 100644 --- a/src/ImageSharp/Formats/PixelTypeInfo.cs +++ b/src/ImageSharp/Formats/PixelTypeInfo.cs @@ -1,6 +1,10 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System.Runtime.CompilerServices; + +using SixLabors.ImageSharp.PixelFormats; + namespace SixLabors.ImageSharp.Formats { /// @@ -21,5 +25,9 @@ namespace SixLabors.ImageSharp.Formats /// Gets color depth, in number of bits per pixel. /// public int BitsPerPixel { get; } + + internal static PixelTypeInfo Create() + where TPixel : struct, IPixel => + new PixelTypeInfo(Unsafe.SizeOf() * 8); } } diff --git a/src/ImageSharp/Image.Decode.cs b/src/ImageSharp/Image.Decode.cs index 36944dc2c0..17a15a9160 100644 --- a/src/ImageSharp/Image.Decode.cs +++ b/src/ImageSharp/Image.Decode.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp /// /// Adds static methods allowing the decoding of new images. /// - public static partial class Image + public abstract partial class Image { /// /// Creates an instance backed by an uninitialized memory buffer. diff --git a/src/ImageSharp/Image.FromBytes.cs b/src/ImageSharp/Image.FromBytes.cs index 7ceeea9498..24a169f6e9 100644 --- a/src/ImageSharp/Image.FromBytes.cs +++ b/src/ImageSharp/Image.FromBytes.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp /// /// Adds static methods allowing the creation of new image from a byte array. /// - public static partial class Image + public abstract partial class Image { /// /// By reading the header on the provided byte array this calculates the images format. diff --git a/src/ImageSharp/Image.FromFile.cs b/src/ImageSharp/Image.FromFile.cs index 80267146d5..0b49668862 100644 --- a/src/ImageSharp/Image.FromFile.cs +++ b/src/ImageSharp/Image.FromFile.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp /// /// Adds static methods allowing the creation of new image from a given file. /// - public static partial class Image + public abstract partial class Image { /// /// By reading the header on the provided file this calculates the images mime type. diff --git a/src/ImageSharp/Image.FromStream.cs b/src/ImageSharp/Image.FromStream.cs index 8848225f54..b16501b0f2 100644 --- a/src/ImageSharp/Image.FromStream.cs +++ b/src/ImageSharp/Image.FromStream.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp /// /// Adds static methods allowing the creation of new image from a given stream. /// - public static partial class Image + public abstract partial class Image { /// /// By reading the header on the provided stream this calculates the images mime type. diff --git a/src/ImageSharp/Image.LoadPixelData.cs b/src/ImageSharp/Image.LoadPixelData.cs index 282f980865..eb7ce261f8 100644 --- a/src/ImageSharp/Image.LoadPixelData.cs +++ b/src/ImageSharp/Image.LoadPixelData.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp /// /// Adds static methods allowing the creation of new image from raw pixel data. /// - public static partial class Image + public abstract partial class Image { /// /// Create a new instance of the class from the raw data. diff --git a/src/ImageSharp/Image.WrapMemory.cs b/src/ImageSharp/Image.WrapMemory.cs index 3d788bb73a..49bc99e9ee 100644 --- a/src/ImageSharp/Image.WrapMemory.cs +++ b/src/ImageSharp/Image.WrapMemory.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp /// /// Adds static methods allowing wrapping an existing memory area as an image. /// - public static partial class Image + public abstract partial class Image { /// /// Wraps an existing contiguous memory area of 'width' x 'height' pixels, diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index a124ddcacd..f780ca666a 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -10,7 +10,10 @@ $(packageversion) 0.0.1 - netcoreapp2.1;netstandard1.3;netstandard2.0;net472 + + + netcoreapp2.1 + true true SixLabors.ImageSharp diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index e37f3e4c1c..ea6d0beb25 100644 --- a/src/ImageSharp/Image{TPixel}.cs +++ b/src/ImageSharp/Image{TPixel}.cs @@ -128,10 +128,10 @@ namespace SixLabors.ImageSharp public PixelTypeInfo PixelType { get; } /// - public int Width => this.Frames.RootFrame.Width; + public override int Width => this.Frames.RootFrame.Width; /// - public int Height => this.Frames.RootFrame.Height; + public override int Height => this.Frames.RootFrame.Height; /// public ImageMetadata Metadata { get; } @@ -197,7 +197,7 @@ namespace SixLabors.ImageSharp } /// - public void Dispose() => this.Frames.Dispose(); + public override void Dispose() => this.Frames.Dispose(); internal override void AcceptVisitor(IImageVisitor visitor) { From 86cc38ad940e03a1944444c568f1df544effa673 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 00:55:09 +0200 Subject: [PATCH 07/31] refactor the rest of the FilterProcessor code --- .../Processing/BrightnessExtensions.cs | 12 ++-- .../Processing/ColorBlindnessExtensions.cs | 31 ++++------ .../Processing/ContrastExtensions.cs | 14 ++--- .../Processing/GrayscaleExtensions.cs | 62 +++++++------------ src/ImageSharp/Processing/HueExtensions.cs | 14 ++--- src/ImageSharp/Processing/InvertExtensions.cs | 14 ++--- .../Processing/KodachromeExtensions.cs | 14 ++--- .../Processing/LomographExtensions.cs | 14 ++--- .../Processing/PolaroidExtensions.cs | 13 ++-- .../Convolution/EdgeDetector2DProcessor.cs | 2 +- .../EdgeDetectorCompassProcessor.cs | 2 +- .../Convolution/EdgeDetectorProcessor.cs | 2 +- .../Filters/AchromatomalyProcessor.cs | 6 +- .../Filters/AchromatopsiaProcessor.cs | 6 +- .../Processors/Filters/BrightnessProcessor.cs | 6 +- .../Processors/Filters/ContrastProcessor.cs | 6 +- .../Filters/DeuteranomalyProcessor.cs | 6 +- .../Filters/DeuteranopiaProcessor.cs | 6 +- .../Processors/Filters/FilterProcessor.cs | 2 +- .../Filters/FilterProcessorImplementation.cs | 8 +-- .../Filters/GrayscaleBt601Processor.cs | 6 +- .../Filters/GrayscaleBt709Processor.cs | 17 +++-- .../Processors/Filters/HueProcessor.cs | 5 +- .../Processors/Filters/InvertProcessor.cs | 6 +- .../Processors/Filters/KodachromeProcessor.cs | 6 +- .../Processors/Filters/LomographProcessor.cs | 19 ++---- .../LomographProcessorImplementation.cs | 26 ++++++++ .../Processors/Filters/PolaroidProcessor.cs | 22 +++++-- .../Filters/ProtanomalyProcessor.cs | 6 +- .../Processors/Filters/ProtanopiaProcessor.cs | 5 +- .../Processors/Filters/SaturateProcessor.cs | 6 +- .../Processors/Filters/SepiaProcessor.cs | 6 +- .../Filters/TritanomalyProcessor.cs | 6 +- .../Processors/Filters/TritanopiaProcessor.cs | 6 +- .../Processing/Processors/IImageProcessor.cs | 31 +++++++++- .../Processing/SaturateExtensions.cs | 14 ++--- src/ImageSharp/Processing/SepiaExtensions.cs | 22 +++---- 37 files changed, 210 insertions(+), 239 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Filters/LomographProcessorImplementation.cs diff --git a/src/ImageSharp/Processing/BrightnessExtensions.cs b/src/ImageSharp/Processing/BrightnessExtensions.cs index 2f252ad305..4de4dc897b 100644 --- a/src/ImageSharp/Processing/BrightnessExtensions.cs +++ b/src/ImageSharp/Processing/BrightnessExtensions.cs @@ -19,13 +19,11 @@ namespace SixLabors.ImageSharp.Processing /// A value of 0 will create an image that is completely black. A value of 1 leaves the input unchanged. /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing brighter results. /// - /// The pixel format. /// The image this method extends. /// The proportion of the conversion. Must be greater than or equal to 0. /// The . - public static IImageProcessingContext Brightness(this IImageProcessingContext source, float amount) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BrightnessProcessor(amount)); + public static IImageProcessingContext Brightness(this IImageProcessingContext source, float amount) + => source.ApplyProcessor(new BrightnessProcessor(amount)); /// /// Alters the brightness component of the image. @@ -34,15 +32,13 @@ namespace SixLabors.ImageSharp.Processing /// A value of 0 will create an image that is completely black. A value of 1 leaves the input unchanged. /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing brighter results. /// - /// The pixel format. /// The image this method extends. /// The proportion of the conversion. Must be greater than or equal to 0. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext Brightness(this IImageProcessingContext source, float amount, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BrightnessProcessor(amount), rectangle); + public static IImageProcessingContext Brightness(this IImageProcessingContext source, float amount, Rectangle rectangle) + => source.ApplyProcessor(new BrightnessProcessor(amount), rectangle); } } diff --git a/src/ImageSharp/Processing/ColorBlindnessExtensions.cs b/src/ImageSharp/Processing/ColorBlindnessExtensions.cs index 3316358954..000d47bd3e 100644 --- a/src/ImageSharp/Processing/ColorBlindnessExtensions.cs +++ b/src/ImageSharp/Processing/ColorBlindnessExtensions.cs @@ -16,49 +16,44 @@ namespace SixLabors.ImageSharp.Processing /// /// Applies the given colorblindness simulator to the image. /// - /// The pixel format. /// The image this method extends. /// The type of color blindness simulator to apply. /// The . - public static IImageProcessingContext ColorBlindness(this IImageProcessingContext source, ColorBlindnessMode colorBlindness) - where TPixel : struct, IPixel - => source.ApplyProcessor(GetProcessor(colorBlindness)); + public static IImageProcessingContext ColorBlindness(this IImageProcessingContext source, ColorBlindnessMode colorBlindness) + => source.ApplyProcessor(GetProcessor(colorBlindness)); /// /// Applies the given colorblindness simulator to the image. /// - /// The pixel format. /// The image this method extends. /// The type of color blindness simulator to apply. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext ColorBlindness(this IImageProcessingContext source, ColorBlindnessMode colorBlindnessMode, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(GetProcessor(colorBlindnessMode), rectangle); + public static IImageProcessingContext ColorBlindness(this IImageProcessingContext source, ColorBlindnessMode colorBlindnessMode, Rectangle rectangle) + => source.ApplyProcessor(GetProcessor(colorBlindnessMode), rectangle); - private static IImageProcessor GetProcessor(ColorBlindnessMode colorBlindness) - where TPixel : struct, IPixel + private static IImageProcessor GetProcessor(ColorBlindnessMode colorBlindness) { switch (colorBlindness) { case ColorBlindnessMode.Achromatomaly: - return new AchromatomalyProcessor(); + return new AchromatomalyProcessor(); case ColorBlindnessMode.Achromatopsia: - return new AchromatopsiaProcessor(); + return new AchromatopsiaProcessor(); case ColorBlindnessMode.Deuteranomaly: - return new DeuteranomalyProcessor(); + return new DeuteranomalyProcessor(); case ColorBlindnessMode.Deuteranopia: - return new DeuteranopiaProcessor(); + return new DeuteranopiaProcessor(); case ColorBlindnessMode.Protanomaly: - return new ProtanomalyProcessor(); + return new ProtanomalyProcessor(); case ColorBlindnessMode.Protanopia: - return new ProtanopiaProcessor(); + return new ProtanopiaProcessor(); case ColorBlindnessMode.Tritanomaly: - return new TritanomalyProcessor(); + return new TritanomalyProcessor(); default: - return new TritanopiaProcessor(); + return new TritanopiaProcessor(); } } } diff --git a/src/ImageSharp/Processing/ContrastExtensions.cs b/src/ImageSharp/Processing/ContrastExtensions.cs index 776aa67518..b03844d93b 100644 --- a/src/ImageSharp/Processing/ContrastExtensions.cs +++ b/src/ImageSharp/Processing/ContrastExtensions.cs @@ -8,7 +8,7 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the alteration of the contrast component to the type. + /// Adds extensions that allow the alteration of the contrast component to the type. /// public static class ContrastExtensions { @@ -19,13 +19,11 @@ namespace SixLabors.ImageSharp.Processing /// A value of 0 will create an image that is completely gray. A value of 1 leaves the input unchanged. /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing results with more contrast. /// - /// The pixel format. /// The image this method extends. /// The proportion of the conversion. Must be greater than or equal to 0. /// The . - public static IImageProcessingContext Contrast(this IImageProcessingContext source, float amount) - where TPixel : struct, IPixel - => source.ApplyProcessor(new ContrastProcessor(amount)); + public static IImageProcessingContext Contrast(this IImageProcessingContext source, float amount) + => source.ApplyProcessor(new ContrastProcessor(amount)); /// /// Alters the contrast component of the image. @@ -34,15 +32,13 @@ namespace SixLabors.ImageSharp.Processing /// A value of 0 will create an image that is completely gray. A value of 1 leaves the input unchanged. /// Other values are linear multipliers on the effect. Values of an amount over 1 are allowed, providing results with more contrast. /// - /// The pixel format. /// The image this method extends. /// The proportion of the conversion. Must be greater than or equal to 0. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext Contrast(this IImageProcessingContext source, float amount, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new ContrastProcessor(amount), rectangle); + public static IImageProcessingContext Contrast(this IImageProcessingContext source, float amount, Rectangle rectangle) + => source.ApplyProcessor(new ContrastProcessor(amount), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/GrayscaleExtensions.cs b/src/ImageSharp/Processing/GrayscaleExtensions.cs index 9ab664056b..d68a385e47 100644 --- a/src/ImageSharp/Processing/GrayscaleExtensions.cs +++ b/src/ImageSharp/Processing/GrayscaleExtensions.cs @@ -9,56 +9,48 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of grayscale toning to the type. + /// Adds extensions that allow the application of grayscale toning to the type. /// public static class GrayscaleExtensions { /// /// Applies grayscale toning to the image. /// - /// The pixel format. /// The image this method extends. - /// The . - public static IImageProcessingContext Grayscale(this IImageProcessingContext source) - where TPixel : struct, IPixel + /// The . + public static IImageProcessingContext Grayscale(this IImageProcessingContext source) => Grayscale(source, GrayscaleMode.Bt709); /// /// Applies grayscale toning to the image using the given amount. /// - /// The pixel format. /// The image this method extends. /// The proportion of the conversion. Must be between 0 and 1. - /// The . - public static IImageProcessingContext Grayscale(this IImageProcessingContext source, float amount) - where TPixel : struct, IPixel + /// The . + public static IImageProcessingContext Grayscale(this IImageProcessingContext source, float amount) => Grayscale(source, GrayscaleMode.Bt709, amount); /// /// Applies grayscale toning to the image with the given . /// - /// The pixel format. /// The image this method extends. /// The formula to apply to perform the operation. - /// The . - public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode) - where TPixel : struct, IPixel + /// The . + public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode) => Grayscale(source, mode, 1F); /// /// Applies grayscale toning to the image with the given using the given amount. /// - /// The pixel format. /// The image this method extends. /// The formula to apply to perform the operation. /// The proportion of the conversion. Must be between 0 and 1. - /// The . - public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode, float amount) - where TPixel : struct, IPixel + /// The . + public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode, float amount) { - IImageProcessor processor = mode == GrayscaleMode.Bt709 - ? (IImageProcessor)new GrayscaleBt709Processor(amount) - : new GrayscaleBt601Processor(1F); + IImageProcessor processor = mode == GrayscaleMode.Bt709 + ? (IImageProcessor)new GrayscaleBt709Processor(amount) + : new GrayscaleBt601Processor(1F); source.ApplyProcessor(processor); return source; @@ -67,61 +59,53 @@ namespace SixLabors.ImageSharp.Processing /// /// Applies grayscale toning to the image. /// - /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static IImageProcessingContext Grayscale(this IImageProcessingContext source, Rectangle rectangle) - where TPixel : struct, IPixel + /// The . + public static IImageProcessingContext Grayscale(this IImageProcessingContext source, Rectangle rectangle) => Grayscale(source, 1F, rectangle); /// /// Applies grayscale toning to the image using the given amount. /// - /// The pixel format. /// The image this method extends. /// The proportion of the conversion. Must be between 0 and 1. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static IImageProcessingContext Grayscale(this IImageProcessingContext source, float amount, Rectangle rectangle) - where TPixel : struct, IPixel + /// The . + public static IImageProcessingContext Grayscale(this IImageProcessingContext source, float amount, Rectangle rectangle) => Grayscale(source, GrayscaleMode.Bt709, amount, rectangle); /// /// Applies grayscale toning to the image. /// - /// The pixel format. /// The image this method extends. /// The formula to apply to perform the operation. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode, Rectangle rectangle) - where TPixel : struct, IPixel + /// The . + public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode, Rectangle rectangle) => Grayscale(source, mode, 1F, rectangle); /// /// Applies grayscale toning to the image using the given amount. /// - /// The pixel format. /// The image this method extends. /// The formula to apply to perform the operation. /// The proportion of the conversion. Must be between 0 and 1. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode, float amount, Rectangle rectangle) - where TPixel : struct, IPixel + /// The . + public static IImageProcessingContext Grayscale(this IImageProcessingContext source, GrayscaleMode mode, float amount, Rectangle rectangle) { - IImageProcessor processor = mode == GrayscaleMode.Bt709 - ? (IImageProcessor)new GrayscaleBt709Processor(amount) - : new GrayscaleBt601Processor(amount); + IImageProcessor processor = mode == GrayscaleMode.Bt709 + ? (IImageProcessor)new GrayscaleBt709Processor(amount) + : new GrayscaleBt601Processor(amount); source.ApplyProcessor(processor, rectangle); return source; diff --git a/src/ImageSharp/Processing/HueExtensions.cs b/src/ImageSharp/Processing/HueExtensions.cs index 246d4bf2bd..17296e1db5 100644 --- a/src/ImageSharp/Processing/HueExtensions.cs +++ b/src/ImageSharp/Processing/HueExtensions.cs @@ -8,33 +8,29 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the alteration of the hue component to the type. + /// Adds extensions that allow the alteration of the hue component to the type. /// public static class HueExtensions { /// /// Alters the hue component of the image. /// - /// The pixel format. /// The image this method extends. /// The rotation angle in degrees to adjust the hue. /// The . - public static IImageProcessingContext Hue(this IImageProcessingContext source, float degrees) - where TPixel : struct, IPixel - => source.ApplyProcessor(new HueProcessor(degrees)); + public static IImageProcessingContext Hue(this IImageProcessingContext source, float degrees) + => source.ApplyProcessor(new HueProcessor(degrees)); /// /// Alters the hue component of the image. /// - /// The pixel format. /// The image this method extends. /// The rotation angle in degrees to adjust the hue. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext Hue(this IImageProcessingContext source, float degrees, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new HueProcessor(degrees), rectangle); + public static IImageProcessingContext Hue(this IImageProcessingContext source, float degrees, Rectangle rectangle) + => source.ApplyProcessor(new HueProcessor(degrees), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/InvertExtensions.cs b/src/ImageSharp/Processing/InvertExtensions.cs index 9e031bc95a..828aaa1bbc 100644 --- a/src/ImageSharp/Processing/InvertExtensions.cs +++ b/src/ImageSharp/Processing/InvertExtensions.cs @@ -8,31 +8,27 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the inversion of colors to the type. + /// Adds extensions that allow the inversion of colors to the type. /// public static class InvertExtensions { /// /// Inverts the colors of the image. /// - /// The pixel format. /// The image this method extends. /// The . - public static IImageProcessingContext Invert(this IImageProcessingContext source) - where TPixel : struct, IPixel - => source.ApplyProcessor(new InvertProcessor(1F)); + public static IImageProcessingContext Invert(this IImageProcessingContext source) + => source.ApplyProcessor(new InvertProcessor(1F)); /// /// Inverts the colors of the image. /// - /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext Invert(this IImageProcessingContext source, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new InvertProcessor(1F), rectangle); + public static IImageProcessingContext Invert(this IImageProcessingContext source, Rectangle rectangle) + => source.ApplyProcessor(new InvertProcessor(1F), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/KodachromeExtensions.cs b/src/ImageSharp/Processing/KodachromeExtensions.cs index e438b131ed..24bd51e016 100644 --- a/src/ImageSharp/Processing/KodachromeExtensions.cs +++ b/src/ImageSharp/Processing/KodachromeExtensions.cs @@ -8,31 +8,27 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the recreation of an old Kodachrome camera effect to the type. + /// Adds extensions that allow the recreation of an old Kodachrome camera effect to the type. /// public static class KodachromeExtensions { /// /// Alters the colors of the image recreating an old Kodachrome camera effect. /// - /// The pixel format. /// The image this method extends. /// The . - public static IImageProcessingContext Kodachrome(this IImageProcessingContext source) - where TPixel : struct, IPixel - => source.ApplyProcessor(new KodachromeProcessor()); + public static IImageProcessingContext Kodachrome(this IImageProcessingContext source) + => source.ApplyProcessor(new KodachromeProcessor()); /// /// Alters the colors of the image recreating an old Kodachrome camera effect. /// - /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext Kodachrome(this IImageProcessingContext source, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new KodachromeProcessor(), rectangle); + public static IImageProcessingContext Kodachrome(this IImageProcessingContext source, Rectangle rectangle) + => source.ApplyProcessor(new KodachromeProcessor(), rectangle); } } diff --git a/src/ImageSharp/Processing/LomographExtensions.cs b/src/ImageSharp/Processing/LomographExtensions.cs index 7dff164026..a3fd3d3b50 100644 --- a/src/ImageSharp/Processing/LomographExtensions.cs +++ b/src/ImageSharp/Processing/LomographExtensions.cs @@ -8,31 +8,27 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the recreation of an old Lomograph camera effect to the type. + /// Adds extensions that allow the recreation of an old Lomograph camera effect to the type. /// public static class LomographExtensions { /// /// Alters the colors of the image recreating an old Lomograph camera effect. /// - /// The pixel format. /// The image this method extends. /// The . - public static IImageProcessingContext Lomograph(this IImageProcessingContext source) - where TPixel : struct, IPixel - => source.ApplyProcessor(new LomographProcessor()); + public static IImageProcessingContext Lomograph(this IImageProcessingContext source) + => source.ApplyProcessor(new LomographProcessor()); /// /// Alters the colors of the image recreating an old Lomograph camera effect. /// - /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext Lomograph(this IImageProcessingContext source, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new LomographProcessor(), rectangle); + public static IImageProcessingContext Lomograph(this IImageProcessingContext source, Rectangle rectangle) + => source.ApplyProcessor(new LomographProcessor(), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/PolaroidExtensions.cs b/src/ImageSharp/Processing/PolaroidExtensions.cs index 5d4beee221..4172c1d615 100644 --- a/src/ImageSharp/Processing/PolaroidExtensions.cs +++ b/src/ImageSharp/Processing/PolaroidExtensions.cs @@ -8,19 +8,17 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the recreation of an old Polaroid camera effect to the type. + /// Adds extensions that allow the recreation of an old Polaroid camera effect to the type. /// public static class PolaroidExtensions { /// /// Alters the colors of the image recreating an old Polaroid camera effect. /// - /// The pixel format. /// The image this method extends. /// The . - public static IImageProcessingContext Polaroid(this IImageProcessingContext source) - where TPixel : struct, IPixel - => source.ApplyProcessor(new PolaroidProcessor()); + public static IImageProcessingContext Polaroid(this IImageProcessingContext source) + => source.ApplyProcessor(new PolaroidProcessor()); /// /// Alters the colors of the image recreating an old Polaroid camera effect. @@ -31,8 +29,7 @@ namespace SixLabors.ImageSharp.Processing /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext Polaroid(this IImageProcessingContext source, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new PolaroidProcessor(), rectangle); + public static IImageProcessingContext Polaroid(this IImageProcessingContext source, Rectangle rectangle) + => source.ApplyProcessor(new PolaroidProcessor(), rectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs index d2e9630dc0..c08a176618 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs @@ -51,7 +51,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { if (this.Grayscale) { - new GrayscaleBt709Processor(1F).Apply(source, sourceRectangle, configuration); + new GrayscaleBt709Processor(1F).ApplyToFrame(source, sourceRectangle, configuration); } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs index 73f92fae38..aa33699f5d 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs @@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { if (this.Grayscale) { - new GrayscaleBt709Processor(1F).Apply(source, sourceRectangle, configuration); + new GrayscaleBt709Processor(1F).ApplyToFrame(source, sourceRectangle, configuration); } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs index edc7ec4ccd..9ec79da9eb 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs @@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { if (this.Grayscale) { - new GrayscaleBt709Processor(1F).Apply(source, sourceRectangle, configuration); + new GrayscaleBt709Processor(1F).ApplyToFrame(source, sourceRectangle, configuration); } } diff --git a/src/ImageSharp/Processing/Processors/Filters/AchromatomalyProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/AchromatomalyProcessor.cs index 57c1bad39b..2dac6b827d 100644 --- a/src/ImageSharp/Processing/Processors/Filters/AchromatomalyProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/AchromatomalyProcessor.cs @@ -8,12 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating Achromatomaly (Color desensitivity) color blindness. /// - /// The pixel format. - internal class AchromatomalyProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class AchromatomalyProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public AchromatomalyProcessor() : base(KnownFilterMatrices.AchromatomalyFilter) diff --git a/src/ImageSharp/Processing/Processors/Filters/AchromatopsiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/AchromatopsiaProcessor.cs index 696a854ab8..4df94a7603 100644 --- a/src/ImageSharp/Processing/Processors/Filters/AchromatopsiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/AchromatopsiaProcessor.cs @@ -8,12 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating Achromatopsia (Monochrome) color blindness. /// - /// The pixel format. - internal class AchromatopsiaProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class AchromatopsiaProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public AchromatopsiaProcessor() : base(KnownFilterMatrices.AchromatopsiaFilter) diff --git a/src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs index b1b8ad7478..6a1f3869ae 100644 --- a/src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs @@ -8,12 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a brightness filter matrix using the given amount. /// - /// The pixel format. - internal class BrightnessProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class BrightnessProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// A value of 0 will create an image that is completely black. A value of 1 leaves the input unchanged. diff --git a/src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs index ebec464d5c..3bea695b99 100644 --- a/src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs @@ -8,12 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a contrast filter matrix using the given amount. /// - /// The pixel format. - internal class ContrastProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class ContrastProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// A value of 0 will create an image that is completely gray. A value of 1 leaves the input unchanged. diff --git a/src/ImageSharp/Processing/Processors/Filters/DeuteranomalyProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/DeuteranomalyProcessor.cs index 0d1b1da902..395489d563 100644 --- a/src/ImageSharp/Processing/Processors/Filters/DeuteranomalyProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/DeuteranomalyProcessor.cs @@ -8,12 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating Deuteranomaly (Green-Weak) color blindness. /// - /// The pixel format. - internal class DeuteranomalyProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class DeuteranomalyProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public DeuteranomalyProcessor() : base(KnownFilterMatrices.DeuteranomalyFilter) diff --git a/src/ImageSharp/Processing/Processors/Filters/DeuteranopiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/DeuteranopiaProcessor.cs index ae0727048e..51f8361e6c 100644 --- a/src/ImageSharp/Processing/Processors/Filters/DeuteranopiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/DeuteranopiaProcessor.cs @@ -8,12 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating Deuteranopia (Green-Blind) color blindness. /// - /// The pixel format. - internal class DeuteranopiaProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class DeuteranopiaProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public DeuteranopiaProcessor() : base(KnownFilterMatrices.DeuteranopiaFilter) diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs index 3f9f586877..5637e97700 100644 --- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// public ColorMatrix Matrix { get; } - public IImageProcessor CreatePixelSpecificProcessor() + public virtual IImageProcessor CreatePixelSpecificProcessor() where TPixel : struct, IPixel { return new FilterProcessorImplementation(this); diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessorImplementation.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessorImplementation.cs index 1eaaf1ea5b..8fea81564c 100644 --- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessorImplementation.cs +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessorImplementation.cs @@ -18,11 +18,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters internal class FilterProcessorImplementation : ImageProcessor where TPixel : struct, IPixel { - private readonly FilterProcessor paramterSource; + private readonly FilterProcessor definition; - public FilterProcessorImplementation(FilterProcessor paramterSource) + public FilterProcessorImplementation(FilterProcessor definition) { - this.paramterSource = paramterSource; + this.definition = definition; } /// @@ -31,7 +31,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters var interest = Rectangle.Intersect(sourceRectangle, source.Bounds()); int startX = interest.X; - ColorMatrix matrix = this.paramterSource.Matrix; + ColorMatrix matrix = this.definition.Matrix; ParallelHelper.IterateRowsWithTempBuffer( interest, diff --git a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs index c933d4858f..b39e01b9a8 100644 --- a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs @@ -8,12 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a grayscale filter matrix using the given amount and the formula as specified by ITU-R Recommendation BT.601 /// - /// The pixel format. - internal class GrayscaleBt601Processor : FilterProcessor - where TPixel : struct, IPixel + internal class GrayscaleBt601Processor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The proportion of the conversion. Must be between 0 and 1. public GrayscaleBt601Processor(float amount) diff --git a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs index 1716773b4c..038a2885bc 100644 --- a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs @@ -2,18 +2,17 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Applies a grayscale filter matrix using the given amount and the formula as specified by ITU-R Recommendation BT.709 /// - /// The pixel format. - internal class GrayscaleBt709Processor : FilterProcessor - where TPixel : struct, IPixel + internal class GrayscaleBt709Processor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The proportion of the conversion. Must be between 0 and 1. public GrayscaleBt709Processor(float amount) @@ -23,8 +22,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters } /// - /// Gets the proportion of the conversion + /// Gets the proportion of the conversion. /// public float Amount { get; } + + // TODO: Move this to an appropriate extension method if possible. + internal void ApplyToFrame(ImageFrame frame, Rectangle sourceRectangle, Configuration configuration) + where TPixel : struct, IPixel + { + var processorImpl = new FilterProcessorImplementation(new GrayscaleBt709Processor(1F)); + processorImpl.Apply(frame, sourceRectangle, configuration); + } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs index 4c3a0c73ed..a69fd50757 100644 --- a/src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs @@ -8,11 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a hue filter matrix using the given angle of rotation in degrees /// - internal class HueProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class HueProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The angle of rotation in degrees public HueProcessor(float degrees) diff --git a/src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs index 462c420707..d7631970db 100644 --- a/src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs @@ -8,12 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a filter matrix that inverts the colors of an image /// - /// The pixel format. - internal class InvertProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class InvertProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The proportion of the conversion. Must be between 0 and 1. public InvertProcessor(float amount) diff --git a/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs index 003766e8ab..5570f23d7d 100644 --- a/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs @@ -8,12 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a filter matrix recreating an old Kodachrome camera effect matrix to the image /// - /// The pixel format. - internal class KodachromeProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class KodachromeProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public KodachromeProcessor() : base(KnownFilterMatrices.KodachromeFilter) diff --git a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs index 737ebf6188..345d829ac6 100644 --- a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs @@ -1,33 +1,22 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors.Overlays; -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Converts the colors of the image recreating an old Lomograph effect. /// - /// The pixel format. - internal class LomographProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class LomographProcessor : FilterProcessor { - private static readonly TPixel VeryDarkGreen = ColorBuilder.FromRGBA(0, 10, 0, 255); - /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public LomographProcessor() : base(KnownFilterMatrices.LomographFilter) { } - /// - protected override void AfterFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - { - new VignetteProcessor(VeryDarkGreen).Apply(source, sourceRectangle, configuration); - } + public override IImageProcessor CreatePixelSpecificProcessor() => + new LomographProcessorImplementation(this); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/LomographProcessorImplementation.cs b/src/ImageSharp/Processing/Processors/Filters/LomographProcessorImplementation.cs new file mode 100644 index 0000000000..d851b98e06 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Filters/LomographProcessorImplementation.cs @@ -0,0 +1,26 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Overlays; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Filters +{ + internal class LomographProcessorImplementation : FilterProcessorImplementation + where TPixel : struct, IPixel + { + private static readonly TPixel VeryDarkGreen = ColorBuilder.FromRGBA(0, 10, 0, 255); + + /// + protected override void AfterFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + new VignetteProcessor(VeryDarkGreen).Apply(source, sourceRectangle, configuration); + } + + public LomographProcessorImplementation(FilterProcessor definition) + : base(definition) + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs index fb065ac176..af16d9eab2 100644 --- a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs @@ -10,13 +10,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating an old Polaroid effect. /// - /// The pixel format. - internal class PolaroidProcessor : FilterProcessor - where TPixel : struct, IPixel + public class PolaroidProcessor : FilterProcessor { - private static readonly TPixel VeryDarkOrange = ColorBuilder.FromRGB(102, 34, 0); - private static readonly TPixel LightOrange = ColorBuilder.FromRGBA(255, 153, 102, 128); - /// /// Initializes a new instance of the class. /// @@ -25,11 +20,26 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters { } + public override IImageProcessor CreatePixelSpecificProcessor() => + new PolaroidProcessorImplementation(this); + } + + internal class PolaroidProcessorImplementation : FilterProcessorImplementation + where TPixel : struct, IPixel + { + private static readonly TPixel VeryDarkOrange = ColorBuilder.FromRGB(102, 34, 0); + private static readonly TPixel LightOrange = ColorBuilder.FromRGBA(255, 153, 102, 128); + /// protected override void AfterFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { new VignetteProcessor(VeryDarkOrange).Apply(source, sourceRectangle, configuration); new GlowProcessor(LightOrange, source.Width / 4F).Apply(source, sourceRectangle, configuration); } + + public PolaroidProcessorImplementation(FilterProcessor definition) + : base(definition) + { + } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/ProtanomalyProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ProtanomalyProcessor.cs index 79eb708518..95e369b371 100644 --- a/src/ImageSharp/Processing/Processors/Filters/ProtanomalyProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/ProtanomalyProcessor.cs @@ -8,12 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating Protanomaly (Red-Weak) color blindness. /// - /// The pixel format. - internal class ProtanomalyProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class ProtanomalyProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public ProtanomalyProcessor() : base(KnownFilterMatrices.ProtanomalyFilter) diff --git a/src/ImageSharp/Processing/Processors/Filters/ProtanopiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ProtanopiaProcessor.cs index c6a01439a2..94eeaae475 100644 --- a/src/ImageSharp/Processing/Processors/Filters/ProtanopiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/ProtanopiaProcessor.cs @@ -9,11 +9,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// Converts the colors of the image recreating Protanopia (Red-Blind) color blindness. /// /// The pixel format. - internal class ProtanopiaProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class ProtanopiaProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public ProtanopiaProcessor() : base(KnownFilterMatrices.ProtanopiaFilter) diff --git a/src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs index 75e956071e..424eeb461c 100644 --- a/src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs @@ -8,12 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a saturation filter matrix using the given amount. /// - /// The pixel format. - internal class SaturateProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class SaturateProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// A value of 0 is completely un-saturated. A value of 1 leaves the input unchanged. diff --git a/src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs index 2009dccd56..538db4b52c 100644 --- a/src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs @@ -8,12 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a sepia filter matrix using the given amount. /// - /// The pixel format. - internal class SepiaProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class SepiaProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The proportion of the conversion. Must be between 0 and 1. public SepiaProcessor(float amount) diff --git a/src/ImageSharp/Processing/Processors/Filters/TritanomalyProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/TritanomalyProcessor.cs index 593f7f5b01..3eda05a115 100644 --- a/src/ImageSharp/Processing/Processors/Filters/TritanomalyProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/TritanomalyProcessor.cs @@ -8,12 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating Tritanomaly (Blue-Weak) color blindness. /// - /// The pixel format. - internal class TritanomalyProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class TritanomalyProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public TritanomalyProcessor() : base(KnownFilterMatrices.TritanomalyFilter) diff --git a/src/ImageSharp/Processing/Processors/Filters/TritanopiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/TritanopiaProcessor.cs index 153ad5559a..f5ed26c833 100644 --- a/src/ImageSharp/Processing/Processors/Filters/TritanopiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/TritanopiaProcessor.cs @@ -8,12 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating Tritanopia (Blue-Blind) color blindness. /// - /// The pixel format. - internal class TritanopiaProcessor : FilterProcessor - where TPixel : struct, IPixel + internal class TritanopiaProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public TritanopiaProcessor() : base(KnownFilterMatrices.TritanopiaFilter) diff --git a/src/ImageSharp/Processing/Processors/IImageProcessor.cs b/src/ImageSharp/Processing/Processors/IImageProcessor.cs index 78800affbc..68db453806 100644 --- a/src/ImageSharp/Processing/Processors/IImageProcessor.cs +++ b/src/ImageSharp/Processing/Processors/IImageProcessor.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Processing.Processors IImageProcessor CreatePixelSpecificProcessor() where TPixel : struct, IPixel; } - + /// /// Encapsulates methods to alter the pixels of an image. /// @@ -34,4 +34,33 @@ namespace SixLabors.ImageSharp.Processing.Processors /// void Apply(Image source, Rectangle sourceRectangle); } + + internal static class ImageProcessorExtensions + { + public static void Apply(this IImageProcessor processor, Image source, Rectangle sourceRectangle) + { + var visitor = new ApplyVisitor(processor, sourceRectangle); + source.AcceptVisitor(visitor); + } + + private class ApplyVisitor : IImageVisitor + { + private readonly IImageProcessor processor; + + private readonly Rectangle sourceRectangle; + + public ApplyVisitor(IImageProcessor processor, Rectangle sourceRectangle) + { + this.processor = processor; + this.sourceRectangle = sourceRectangle; + } + + public void Visit(Image image) + where TPixel : struct, IPixel + { + var processorImpl = processor.CreatePixelSpecificProcessor(); + processorImpl.Apply(image, this.sourceRectangle); + } + } + } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/SaturateExtensions.cs b/src/ImageSharp/Processing/SaturateExtensions.cs index ba45ae12c9..fe2cf5f348 100644 --- a/src/ImageSharp/Processing/SaturateExtensions.cs +++ b/src/ImageSharp/Processing/SaturateExtensions.cs @@ -8,7 +8,7 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the alteration of the saturation component to the type. + /// Adds extensions that allow the alteration of the saturation component to the type. /// public static class SaturateExtensions { @@ -19,13 +19,11 @@ namespace SixLabors.ImageSharp.Processing /// A value of 0 is completely un-saturated. A value of 1 leaves the input unchanged. /// Other values are linear multipliers on the effect. Values of amount over 1 are allowed, providing super-saturated results /// - /// The pixel format. /// The image this method extends. /// The proportion of the conversion. Must be greater than or equal to 0. /// The . - public static IImageProcessingContext Saturate(this IImageProcessingContext source, float amount) - where TPixel : struct, IPixel - => source.ApplyProcessor(new SaturateProcessor(amount)); + public static IImageProcessingContext Saturate(this IImageProcessingContext source, float amount) + => source.ApplyProcessor(new SaturateProcessor(amount)); /// /// Alters the saturation component of the image. @@ -34,15 +32,13 @@ namespace SixLabors.ImageSharp.Processing /// A value of 0 is completely un-saturated. A value of 1 leaves the input unchanged. /// Other values are linear multipliers on the effect. Values of amount over 1 are allowed, providing super-saturated results /// - /// The pixel format. /// The image this method extends. /// The proportion of the conversion. Must be greater than or equal to 0. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext Saturate(this IImageProcessingContext source, float amount, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new SaturateProcessor(amount), rectangle); + public static IImageProcessingContext Saturate(this IImageProcessingContext source, float amount, Rectangle rectangle) + => source.ApplyProcessor(new SaturateProcessor(amount), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/SepiaExtensions.cs b/src/ImageSharp/Processing/SepiaExtensions.cs index 08676ee62a..5b23ec11af 100644 --- a/src/ImageSharp/Processing/SepiaExtensions.cs +++ b/src/ImageSharp/Processing/SepiaExtensions.cs @@ -8,56 +8,48 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of sepia toning to the type. + /// Adds extensions that allow the application of sepia toning to the type. /// public static class SepiaExtensions { /// /// Applies sepia toning to the image. /// - /// The pixel format. /// The image this method extends. /// The . - public static IImageProcessingContext Sepia(this IImageProcessingContext source) - where TPixel : struct, IPixel + public static IImageProcessingContext Sepia(this IImageProcessingContext source) => Sepia(source, 1F); /// /// Applies sepia toning to the image using the given amount. /// - /// The pixel format. /// The image this method extends. /// The proportion of the conversion. Must be between 0 and 1. /// The . - public static IImageProcessingContext Sepia(this IImageProcessingContext source, float amount) - where TPixel : struct, IPixel - => source.ApplyProcessor(new SepiaProcessor(amount)); + public static IImageProcessingContext Sepia(this IImageProcessingContext source, float amount) + => source.ApplyProcessor(new SepiaProcessor(amount)); /// /// Applies sepia toning to the image. /// - /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext Sepia(this IImageProcessingContext source, Rectangle rectangle) - where TPixel : struct, IPixel + public static IImageProcessingContext Sepia(this IImageProcessingContext source, Rectangle rectangle) => Sepia(source, 1F, rectangle); /// /// Applies sepia toning to the image. /// - /// The pixel format. /// The image this method extends. /// The proportion of the conversion. Must be between 0 and 1. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext Sepia(this IImageProcessingContext source, float amount, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new SepiaProcessor(amount), rectangle); + public static IImageProcessingContext Sepia(this IImageProcessingContext source, float amount, Rectangle rectangle) + => source.ApplyProcessor(new SepiaProcessor(amount), rectangle); } } \ No newline at end of file From 59845dcbd7d64756c6b9dc9ae81edf3dbf09fc6b Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 01:08:48 +0200 Subject: [PATCH 08/31] reached a fully compiling state --- .../ImageSharp.Drawing.csproj | 4 +- .../Formats/Gif/GifEncoderTests.cs | 6 +-- .../Formats/Jpg/JpegDecoderTests.cs | 4 +- .../ImageSharp.Tests/ImageSharp.Tests.csproj | 3 +- .../Processing/Filters/BlackWhiteTest.cs | 27 ----------- .../Processing/Filters/BrightnessTest.cs | 31 ------------ .../Processing/Filters/ColorBlindnessTest.cs | 46 ------------------ .../Processing/Filters/ContrastTest.cs | 33 ------------- .../Processing/Filters/FilterTest.cs | 4 +- .../Processing/Filters/GrayscaleTest.cs | 47 ------------------- .../Processing/Filters/HueTest.cs | 32 ------------- .../Processing/Filters/InvertTest.cs | 27 ----------- .../Processing/Filters/KodachromeTest.cs | 27 ----------- .../Processing/Filters/LomographTest.cs | 31 ------------ .../Processing/Filters/OpacityTest.cs | 31 ------------ .../Processing/Filters/PolaroidTest.cs | 28 ----------- .../Processing/Filters/SaturateTest.cs | 31 ------------ .../Processing/Filters/SepiaTest.cs | 27 ----------- .../Processing/Transforms/PadTest.cs | 2 +- .../Processing/Transforms/ResizeTests.cs | 8 ++-- 20 files changed, 17 insertions(+), 432 deletions(-) delete mode 100644 tests/ImageSharp.Tests/Processing/Filters/BlackWhiteTest.cs delete mode 100644 tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs delete mode 100644 tests/ImageSharp.Tests/Processing/Filters/ColorBlindnessTest.cs delete mode 100644 tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs delete mode 100644 tests/ImageSharp.Tests/Processing/Filters/GrayscaleTest.cs delete mode 100644 tests/ImageSharp.Tests/Processing/Filters/HueTest.cs delete mode 100644 tests/ImageSharp.Tests/Processing/Filters/InvertTest.cs delete mode 100644 tests/ImageSharp.Tests/Processing/Filters/KodachromeTest.cs delete mode 100644 tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs delete mode 100644 tests/ImageSharp.Tests/Processing/Filters/OpacityTest.cs delete mode 100644 tests/ImageSharp.Tests/Processing/Filters/PolaroidTest.cs delete mode 100644 tests/ImageSharp.Tests/Processing/Filters/SaturateTest.cs delete mode 100644 tests/ImageSharp.Tests/Processing/Filters/SepiaTest.cs diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj index 6f5cabb09b..1734c48de2 100644 --- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj +++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj @@ -10,7 +10,9 @@ $(packageversion) 0.0.1 - netcoreapp2.1;netstandard1.3;netstandard2.0 + + netcoreapp2.1 + 7.3 true true diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs index cac4030d5f..5da1c3bb78 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs @@ -45,7 +45,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif // Compare encoded result string path = provider.Utility.GetTestOutputFileName("gif", null, true); - using (var encoded = Image.Load(path)) + using (var encoded = Image.Load(path)) { encoded.CompareToReferenceOutput(ValidatorComparer, provider, null, "gif"); } @@ -180,7 +180,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif { inStream.Position = 0; - var image = Image.Load(inStream); + var image = Image.Load(inStream); GifMetadata metaData = image.Metadata.GetFormatMetadata(GifFormat.Instance); GifFrameMetadata frameMetaData = image.Frames.RootFrame.Metadata.GetFormatMetadata(GifFormat.Instance); GifColorTableMode colorMode = metaData.ColorTableMode; @@ -194,7 +194,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif outStream.Position = 0; outStream.Position = 0; - var clone = Image.Load(outStream); + var clone = Image.Load(outStream); GifMetadata cloneMetaData = clone.Metadata.GetFormatMetadata(GifFormat.Instance); Assert.Equal(metaData.ColorTableMode, cloneMetaData.ColorTableMode); diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs index 15f7f92a83..e316fe67eb 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.cs @@ -147,8 +147,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var comparer = ImageComparer.Tolerant(0, 0); using (Image expectedImage = provider.GetReferenceOutputImage(appendPixelTypeToFileName: false)) - using (var pdfJsOriginalResult = Image.Load(pdfJsOriginalResultPath)) - using (var pdfJsPortResult = Image.Load(sourceBytes, JpegDecoder)) + using (var pdfJsOriginalResult = Image.Load(pdfJsOriginalResultPath)) + using (var pdfJsPortResult = Image.Load(sourceBytes, JpegDecoder)) { ImageSimilarityReport originalReport = comparer.CompareImagesOrFrames(expectedImage, pdfJsOriginalResult); ImageSimilarityReport portReport = comparer.CompareImagesOrFrames(expectedImage, pdfJsPortResult); diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index 0b727f30ce..efdf89964c 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -1,6 +1,7 @@  - netcoreapp2.1;net462;net472 + + netcoreapp2.1 True latest full diff --git a/tests/ImageSharp.Tests/Processing/Filters/BlackWhiteTest.cs b/tests/ImageSharp.Tests/Processing/Filters/BlackWhiteTest.cs deleted file mode 100644 index d651f2f04e..0000000000 --- a/tests/ImageSharp.Tests/Processing/Filters/BlackWhiteTest.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Filters; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Processing.Filters -{ - public class BlackWhiteTest : BaseImageOperationsExtensionTest - { - [Fact] - public void BlackWhite_CorrectProcessor() - { - this.operations.BlackWhite(); - BlackWhiteProcessor p = this.Verify>(); - } - - [Fact] - public void BlackWhite_rect_CorrectProcessor() - { - this.operations.BlackWhite(this.rect); - BlackWhiteProcessor p = this.Verify>(this.rect); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs b/tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs deleted file mode 100644 index e210450a8c..0000000000 --- a/tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Filters; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Processing.Effects -{ - public class BrightnessTest : BaseImageOperationsExtensionTest - { - [Fact] - public void Brightness_amount_BrightnessProcessorDefaultsSet() - { - this.operations.Brightness(1.5F); - BrightnessProcessor processor = this.Verify>(); - - Assert.Equal(1.5F, processor.Amount); - } - - [Fact] - public void Brightness_amount_rect_BrightnessProcessorDefaultsSet() - { - this.operations.Brightness(1.5F, this.rect); - BrightnessProcessor processor = this.Verify>(this.rect); - - Assert.Equal(1.5F, processor.Amount); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/ColorBlindnessTest.cs b/tests/ImageSharp.Tests/Processing/Filters/ColorBlindnessTest.cs deleted file mode 100644 index aeafe5fe1d..0000000000 --- a/tests/ImageSharp.Tests/Processing/Filters/ColorBlindnessTest.cs +++ /dev/null @@ -1,46 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Collections.Generic; - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Filters; -using SixLabors.ImageSharp.Processing.Processors; -using SixLabors.ImageSharp.Tests.TestUtilities; - -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Processing.Filters -{ - public class ColorBlindnessTest : BaseImageOperationsExtensionTest - { - public static IEnumerable TheoryData = new[] { - new object[]{ new TestType>(), ColorBlindnessMode.Achromatomaly }, - new object[]{ new TestType>(), ColorBlindnessMode.Achromatopsia }, - new object[]{ new TestType>(), ColorBlindnessMode.Deuteranomaly }, - new object[]{ new TestType>(), ColorBlindnessMode.Deuteranopia }, - new object[]{ new TestType>(), ColorBlindnessMode.Protanomaly }, - new object[]{ new TestType>(), ColorBlindnessMode.Protanopia }, - new object[]{ new TestType>(), ColorBlindnessMode.Tritanomaly }, - new object[]{ new TestType>(), ColorBlindnessMode.Tritanopia } - }; - - [Theory] - [MemberData(nameof(TheoryData))] - public void ColorBlindness_CorrectProcessor(TestType testType, ColorBlindnessMode colorBlindness) - where T : IImageProcessor - { - this.operations.ColorBlindness(colorBlindness); - T p = this.Verify(); - } - [Theory] - [MemberData(nameof(TheoryData))] - public void ColorBlindness_rect_CorrectProcessor(TestType testType, ColorBlindnessMode colorBlindness) - where T : IImageProcessor - { - this.operations.ColorBlindness(colorBlindness, this.rect); - T p = this.Verify(this.rect); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs b/tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs deleted file mode 100644 index 21a552e6af..0000000000 --- a/tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Processing.Effects -{ - using SixLabors.ImageSharp.Processing; - using SixLabors.ImageSharp.Processing.Processors.Filters; - - public class ContrastTest : BaseImageOperationsExtensionTest - { - [Fact] - public void Contrast_amount_ContrastProcessorDefaultsSet() - { - this.operations.Contrast(1.5F); - ContrastProcessor processor = this.Verify>(); - - Assert.Equal(1.5F, processor.Amount); - } - - [Fact] - public void Contrast_amount_rect_ContrastProcessorDefaultsSet() - { - this.operations.Contrast(1.5F, this.rect); - ContrastProcessor processor = this.Verify>(this.rect); - - Assert.Equal(1.5F, processor.Amount); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs b/tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs index 414a0d74e4..f4efb12748 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs @@ -17,14 +17,14 @@ namespace SixLabors.ImageSharp.Tests.Processing.Filters public void Filter_CorrectProcessor() { this.operations.Filter(KnownFilterMatrices.AchromatomalyFilter * KnownFilterMatrices.CreateHueFilter(90F)); - FilterProcessor p = this.Verify>(); + FilterProcessorImplementation p = this.Verify>(); } [Fact] public void Filter_rect_CorrectProcessor() { this.operations.Filter(KnownFilterMatrices.AchromatomalyFilter * KnownFilterMatrices.CreateHueFilter(90F), this.rect); - FilterProcessor p = this.Verify>(this.rect); + FilterProcessorImplementation p = this.Verify>(this.rect); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/GrayscaleTest.cs b/tests/ImageSharp.Tests/Processing/Filters/GrayscaleTest.cs deleted file mode 100644 index d63d978207..0000000000 --- a/tests/ImageSharp.Tests/Processing/Filters/GrayscaleTest.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.Collections.Generic; - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Filters; -using SixLabors.ImageSharp.Processing.Processors; -using SixLabors.ImageSharp.Tests.TestUtilities; - -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Processing.Filters -{ - public class GrayscaleTest : BaseImageOperationsExtensionTest - { - public static IEnumerable ModeTheoryData = new[] { - new object[]{ new TestType>(), GrayscaleMode.Bt709 } - }; - - [Theory] - [MemberData(nameof(ModeTheoryData))] - public void Grayscale_mode_CorrectProcessor(TestType testType, GrayscaleMode mode) - where T : IImageProcessor - { - this.operations.Grayscale(mode); - var p = this.Verify(); - } - - [Theory] - [MemberData(nameof(ModeTheoryData))] - public void Grayscale_mode_rect_CorrectProcessor(TestType testType, GrayscaleMode mode) - where T : IImageProcessor - { - this.operations.Grayscale(mode, this.rect); - this.Verify(this.rect); - } - - [Fact] - public void Grayscale_rect_CorrectProcessor() - { - this.operations.Grayscale(this.rect); - this.Verify>(this.rect); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/HueTest.cs b/tests/ImageSharp.Tests/Processing/Filters/HueTest.cs deleted file mode 100644 index f56578dd68..0000000000 --- a/tests/ImageSharp.Tests/Processing/Filters/HueTest.cs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Filters; - -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Processing.Filters -{ - public class HueTest : BaseImageOperationsExtensionTest - { - [Fact] - public void Hue_amount_HueProcessorDefaultsSet() - { - this.operations.Hue(34f); - var processor = this.Verify>(); - - Assert.Equal(34f, processor.Degrees); - } - - [Fact] - public void Hue_amount_rect_HueProcessorDefaultsSet() - { - this.operations.Hue(5f, this.rect); - var processor = this.Verify>(this.rect); - - Assert.Equal(5f, processor.Degrees); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/InvertTest.cs b/tests/ImageSharp.Tests/Processing/Filters/InvertTest.cs deleted file mode 100644 index c93afc9427..0000000000 --- a/tests/ImageSharp.Tests/Processing/Filters/InvertTest.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Filters; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Processing.Effects -{ - public class InvertTest : BaseImageOperationsExtensionTest - { - [Fact] - public void Invert_InvertProcessorDefaultsSet() - { - this.operations.Invert(); - var processor = this.Verify>(); - } - - [Fact] - public void Pixelate_rect_PixelateProcessorDefaultsSet() - { - this.operations.Invert(this.rect); - var processor = this.Verify>(this.rect); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/KodachromeTest.cs b/tests/ImageSharp.Tests/Processing/Filters/KodachromeTest.cs deleted file mode 100644 index a982521404..0000000000 --- a/tests/ImageSharp.Tests/Processing/Filters/KodachromeTest.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Filters; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Processing.Filters -{ - public class KodachromeTest : BaseImageOperationsExtensionTest - { - [Fact] - public void Kodachrome_amount_KodachromeProcessorDefaultsSet() - { - this.operations.Kodachrome(); - var processor = this.Verify>(); - } - - [Fact] - public void Kodachrome_amount_rect_KodachromeProcessorDefaultsSet() - { - this.operations.Kodachrome(this.rect); - var processor = this.Verify>(this.rect); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs b/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs deleted file mode 100644 index c104f8c252..0000000000 --- a/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.IO; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors; -using SixLabors.Primitives; -using Xunit; - -namespace SixLabors.ImageSharp.Tests -{ - using SixLabors.ImageSharp.Processing; - using SixLabors.ImageSharp.Processing.Processors.Filters; - - public class LomographTest : BaseImageOperationsExtensionTest - { - [Fact] - public void Lomograph_amount_LomographProcessorDefaultsSet() - { - this.operations.Lomograph(); - var processor = this.Verify>(); - } - - [Fact] - public void Lomograph_amount_rect_LomographProcessorDefaultsSet() - { - this.operations.Lomograph(this.rect); - var processor = this.Verify>(this.rect); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/OpacityTest.cs b/tests/ImageSharp.Tests/Processing/Filters/OpacityTest.cs deleted file mode 100644 index adbb8cf295..0000000000 --- a/tests/ImageSharp.Tests/Processing/Filters/OpacityTest.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Filters; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Processing.Effects -{ - public class OpacityTest : BaseImageOperationsExtensionTest - { - [Fact] - public void Alpha_amount_AlphaProcessorDefaultsSet() - { - this.operations.Opacity(0.2f); - OpacityProcessor processor = this.Verify>(); - - Assert.Equal(.2f, processor.Amount); - } - - [Fact] - public void Alpha_amount_rect_AlphaProcessorDefaultsSet() - { - this.operations.Opacity(0.6f, this.rect); - OpacityProcessor processor = this.Verify>(this.rect); - - Assert.Equal(.6f, processor.Amount); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/PolaroidTest.cs b/tests/ImageSharp.Tests/Processing/Filters/PolaroidTest.cs deleted file mode 100644 index f28827b716..0000000000 --- a/tests/ImageSharp.Tests/Processing/Filters/PolaroidTest.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Filters; - -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Processing.Filters -{ - public class PolaroidTest : BaseImageOperationsExtensionTest - { - [Fact] - public void Polaroid_amount_PolaroidProcessorDefaultsSet() - { - this.operations.Polaroid(); - var processor = this.Verify>(); - } - - [Fact] - public void Polaroid_amount_rect_PolaroidProcessorDefaultsSet() - { - this.operations.Polaroid(this.rect); - var processor = this.Verify>(this.rect); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/SaturateTest.cs b/tests/ImageSharp.Tests/Processing/Filters/SaturateTest.cs deleted file mode 100644 index 4b8e80881c..0000000000 --- a/tests/ImageSharp.Tests/Processing/Filters/SaturateTest.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Filters; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Processing.Filters -{ - public class SaturateTest : BaseImageOperationsExtensionTest - { - [Fact] - public void Saturation_amount_SaturationProcessorDefaultsSet() - { - this.operations.Saturate(34); - SaturateProcessor processor = this.Verify>(); - - Assert.Equal(34, processor.Amount); - } - - [Fact] - public void Saturation_amount_rect_SaturationProcessorDefaultsSet() - { - this.operations.Saturate(5, this.rect); - SaturateProcessor processor = this.Verify>(this.rect); - - Assert.Equal(5, processor.Amount); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/SepiaTest.cs b/tests/ImageSharp.Tests/Processing/Filters/SepiaTest.cs deleted file mode 100644 index 9351c8443f..0000000000 --- a/tests/ImageSharp.Tests/Processing/Filters/SepiaTest.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Processing.Processors.Filters; -using Xunit; - -namespace SixLabors.ImageSharp.Tests.Processing.Filters -{ - public class SepiaTest : BaseImageOperationsExtensionTest - { - [Fact] - public void Sepia_amount_SepiaProcessorDefaultsSet() - { - this.operations.Sepia(); - var processor = this.Verify>(); - } - - [Fact] - public void Sepia_amount_rect_SepiaProcessorDefaultsSet() - { - this.operations.Sepia(this.rect); - var processor = this.Verify>(this.rect); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs index 82d7682558..eca1a458b1 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms IResampler sampler = KnownResamplers.NearestNeighbor; this.operations.Pad(width, height); - ResizeProcessor resizeProcessor = this.Verify>(); + ResizeProcessorImplementation resizeProcessor = this.Verify>(); Assert.Equal(width, resizeProcessor.Width); Assert.Equal(height, resizeProcessor.Height); diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs index 948c79d8dd..01c43a0a45 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms int width = 50; int height = 100; this.operations.Resize(width, height); - ResizeProcessor resizeProcessor = this.Verify>(); + ResizeProcessorImplementation resizeProcessor = this.Verify>(); Assert.Equal(width, resizeProcessor.Width); Assert.Equal(height, resizeProcessor.Height); @@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms int height = 100; IResampler sampler = KnownResamplers.Lanczos3; this.operations.Resize(width, height, sampler); - ResizeProcessor resizeProcessor = this.Verify>(); + ResizeProcessorImplementation resizeProcessor = this.Verify>(); Assert.Equal(width, resizeProcessor.Width); Assert.Equal(height, resizeProcessor.Height); @@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms // ReSharper disable once ConditionIsAlwaysTrueOrFalse this.operations.Resize(width, height, sampler, compand); - ResizeProcessor resizeProcessor = this.Verify>(); + ResizeProcessorImplementation resizeProcessor = this.Verify>(); Assert.Equal(width, resizeProcessor.Width); Assert.Equal(height, resizeProcessor.Height); @@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms }; this.operations.Resize(resizeOptions); - ResizeProcessor resizeProcessor = this.Verify>(); + ResizeProcessorImplementation resizeProcessor = this.Verify>(); Assert.Equal(width, resizeProcessor.Width); Assert.Equal(height, resizeProcessor.Height); From 475cf843bbfe04cfbd54db3cadd9d80f98ff9a3d Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 01:21:06 +0200 Subject: [PATCH 09/31] fix processor invocation tests --- .../BaseImageOperationsExtensionTest.cs | 16 ++++++++++++-- .../FakeImageOperationsProvider.cs | 21 ++++++++++++++----- tests/ImageSharp.Tests/ImageOperationTests.cs | 14 ++++++------- .../Processing/Filters/FilterTest.cs | 4 ++-- .../Processing/Transforms/ResizeTests.cs | 8 +++---- 5 files changed, 43 insertions(+), 20 deletions(-) diff --git a/tests/ImageSharp.Tests/BaseImageOperationsExtensionTest.cs b/tests/ImageSharp.Tests/BaseImageOperationsExtensionTest.cs index 7adbefb346..857c19d87f 100644 --- a/tests/ImageSharp.Tests/BaseImageOperationsExtensionTest.cs +++ b/tests/ImageSharp.Tests/BaseImageOperationsExtensionTest.cs @@ -3,6 +3,7 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors; using SixLabors.Primitives; using Xunit; @@ -33,7 +34,12 @@ namespace SixLabors.ImageSharp.Tests FakeImageOperationsProvider.FakeImageOperations.AppliedOperation operation = this.internalOperations.Applied[index]; - return Assert.IsType(operation.Processor); + if (operation.NonGenericProcessor != null) + { + return Assert.IsType(operation.NonGenericProcessor); + } + + return Assert.IsType(operation.GenericProcessor); } public T Verify(Rectangle rect, int index = 0) @@ -43,7 +49,13 @@ namespace SixLabors.ImageSharp.Tests FakeImageOperationsProvider.FakeImageOperations.AppliedOperation operation = this.internalOperations.Applied[index]; Assert.Equal(rect, operation.Rectangle); - return Assert.IsType(operation.Processor); + + if (operation.NonGenericProcessor != null) + { + return Assert.IsType(operation.NonGenericProcessor); + } + + return Assert.IsType(operation.GenericProcessor); } } } diff --git a/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs b/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs index 276cc5da85..c91cb16d46 100644 --- a/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs +++ b/tests/ImageSharp.Tests/FakeImageOperationsProvider.cs @@ -69,19 +69,28 @@ namespace SixLabors.ImageSharp.Tests public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle) { - throw new System.NotImplementedException(); + this.Applied.Add(new AppliedOperation() + { + Rectangle = rectangle, + NonGenericProcessor = processor + }); + return this; } public IImageProcessingContext ApplyProcessor(IImageProcessor processor) { - throw new System.NotImplementedException(); + this.Applied.Add(new AppliedOperation() + { + NonGenericProcessor = processor + }); + return this; } public IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle) { this.Applied.Add(new AppliedOperation { - Processor = processor, + GenericProcessor = processor, Rectangle = rectangle }); return this; @@ -91,7 +100,7 @@ namespace SixLabors.ImageSharp.Tests { this.Applied.Add(new AppliedOperation { - Processor = processor + GenericProcessor = processor }); return this; } @@ -99,7 +108,9 @@ namespace SixLabors.ImageSharp.Tests public struct AppliedOperation { public Rectangle? Rectangle { get; set; } - public IImageProcessor Processor { get; set; } + public IImageProcessor GenericProcessor { get; set; } + + public IImageProcessor NonGenericProcessor { get; set; } } } } diff --git a/tests/ImageSharp.Tests/ImageOperationTests.cs b/tests/ImageSharp.Tests/ImageOperationTests.cs index 869882f672..e694f0b644 100644 --- a/tests/ImageSharp.Tests/ImageOperationTests.cs +++ b/tests/ImageSharp.Tests/ImageOperationTests.cs @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Tests this.image.Mutate(x => x.ApplyProcessor(this.processor)); Assert.True(this.provider.HasCreated(this.image)); - Assert.Contains(this.processor, this.provider.AppliedOperations(this.image).Select(x => x.Processor)); + Assert.Contains(this.processor, this.provider.AppliedOperations(this.image).Select(x => x.GenericProcessor)); } [Fact] @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Tests this.image.Mutate(this.processor); Assert.True(this.provider.HasCreated(this.image)); - Assert.Contains(this.processor, this.provider.AppliedOperations(this.image).Select(x => x.Processor)); + Assert.Contains(this.processor, this.provider.AppliedOperations(this.image).Select(x => x.GenericProcessor)); } [Fact] @@ -59,7 +59,7 @@ namespace SixLabors.ImageSharp.Tests Image returned = this.image.Clone(x => x.ApplyProcessor(this.processor)); Assert.True(this.provider.HasCreated(returned)); - Assert.Contains(this.processor, this.provider.AppliedOperations(returned).Select(x => x.Processor)); + Assert.Contains(this.processor, this.provider.AppliedOperations(returned).Select(x => x.GenericProcessor)); } [Fact] @@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Tests Image returned = this.image.Clone(this.processor); Assert.True(this.provider.HasCreated(returned)); - Assert.Contains(this.processor, this.provider.AppliedOperations(returned).Select(x => x.Processor)); + Assert.Contains(this.processor, this.provider.AppliedOperations(returned).Select(x => x.GenericProcessor)); } [Fact] @@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Tests { Image returned = this.image.Clone(x => x.ApplyProcessor(this.processor)); Assert.False(this.provider.HasCreated(this.image)); - Assert.DoesNotContain(this.processor, this.provider.AppliedOperations(this.image).Select(x => x.Processor)); + Assert.DoesNotContain(this.processor, this.provider.AppliedOperations(this.image).Select(x => x.GenericProcessor)); } [Fact] @@ -84,7 +84,7 @@ namespace SixLabors.ImageSharp.Tests { Image returned = this.image.Clone(this.processor); Assert.False(this.provider.HasCreated(this.image)); - Assert.DoesNotContain(this.processor, this.provider.AppliedOperations(this.image).Select(x => x.Processor)); + Assert.DoesNotContain(this.processor, this.provider.AppliedOperations(this.image).Select(x => x.GenericProcessor)); } [Fact] @@ -92,7 +92,7 @@ namespace SixLabors.ImageSharp.Tests { var operations = new FakeImageOperationsProvider.FakeImageOperations(null, false); operations.ApplyProcessors(this.processor); - Assert.Contains(this.processor, operations.Applied.Select(x => x.Processor)); + Assert.Contains(this.processor, operations.Applied.Select(x => x.GenericProcessor)); } public void Dispose() => this.image.Dispose(); diff --git a/tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs b/tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs index f4efb12748..d598f0ac88 100644 --- a/tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs +++ b/tests/ImageSharp.Tests/Processing/Filters/FilterTest.cs @@ -17,14 +17,14 @@ namespace SixLabors.ImageSharp.Tests.Processing.Filters public void Filter_CorrectProcessor() { this.operations.Filter(KnownFilterMatrices.AchromatomalyFilter * KnownFilterMatrices.CreateHueFilter(90F)); - FilterProcessorImplementation p = this.Verify>(); + FilterProcessor p = this.Verify(); } [Fact] public void Filter_rect_CorrectProcessor() { this.operations.Filter(KnownFilterMatrices.AchromatomalyFilter * KnownFilterMatrices.CreateHueFilter(90F), this.rect); - FilterProcessorImplementation p = this.Verify>(this.rect); + FilterProcessor p = this.Verify(this.rect); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs index 01c43a0a45..570ac4e2cd 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/ResizeTests.cs @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms int width = 50; int height = 100; this.operations.Resize(width, height); - ResizeProcessorImplementation resizeProcessor = this.Verify>(); + ResizeProcessor resizeProcessor = this.Verify(); Assert.Equal(width, resizeProcessor.Width); Assert.Equal(height, resizeProcessor.Height); @@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms int height = 100; IResampler sampler = KnownResamplers.Lanczos3; this.operations.Resize(width, height, sampler); - ResizeProcessorImplementation resizeProcessor = this.Verify>(); + ResizeProcessor resizeProcessor = this.Verify(); Assert.Equal(width, resizeProcessor.Width); Assert.Equal(height, resizeProcessor.Height); @@ -47,7 +47,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms // ReSharper disable once ConditionIsAlwaysTrueOrFalse this.operations.Resize(width, height, sampler, compand); - ResizeProcessorImplementation resizeProcessor = this.Verify>(); + ResizeProcessor resizeProcessor = this.Verify(); Assert.Equal(width, resizeProcessor.Width); Assert.Equal(height, resizeProcessor.Height); @@ -73,7 +73,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms }; this.operations.Resize(resizeOptions); - ResizeProcessorImplementation resizeProcessor = this.Verify>(); + ResizeProcessor resizeProcessor = this.Verify(); Assert.Equal(width, resizeProcessor.Width); Assert.Equal(height, resizeProcessor.Height); From 134d4845e237d1864a56b0b77c13ecc1a66e5a3f Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 01:26:10 +0200 Subject: [PATCH 10/31] re-add and fix filter invocation tests --- .../Processing/Filters/BlackWhiteTest.cs | 27 +++++++++++ .../Processing/Filters/BrightnessTest.cs | 31 ++++++++++++ .../Processing/Filters/ColorBlindnessTest.cs | 46 ++++++++++++++++++ .../Processing/Filters/ContrastTest.cs | 33 +++++++++++++ .../Processing/Filters/GrayscaleTest.cs | 47 +++++++++++++++++++ .../Processing/Filters/HueTest.cs | 32 +++++++++++++ .../Processing/Filters/InvertTest.cs | 27 +++++++++++ .../Processing/Filters/KodachromeTest.cs | 27 +++++++++++ .../Processing/Filters/LomographTest.cs | 31 ++++++++++++ .../Processing/Filters/OpacityTest.cs | 31 ++++++++++++ .../Processing/Filters/PolaroidTest.cs | 28 +++++++++++ .../Processing/Filters/SaturateTest.cs | 31 ++++++++++++ .../Processing/Filters/SepiaTest.cs | 27 +++++++++++ 13 files changed, 418 insertions(+) create mode 100644 tests/ImageSharp.Tests/Processing/Filters/BlackWhiteTest.cs create mode 100644 tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs create mode 100644 tests/ImageSharp.Tests/Processing/Filters/ColorBlindnessTest.cs create mode 100644 tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs create mode 100644 tests/ImageSharp.Tests/Processing/Filters/GrayscaleTest.cs create mode 100644 tests/ImageSharp.Tests/Processing/Filters/HueTest.cs create mode 100644 tests/ImageSharp.Tests/Processing/Filters/InvertTest.cs create mode 100644 tests/ImageSharp.Tests/Processing/Filters/KodachromeTest.cs create mode 100644 tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs create mode 100644 tests/ImageSharp.Tests/Processing/Filters/OpacityTest.cs create mode 100644 tests/ImageSharp.Tests/Processing/Filters/PolaroidTest.cs create mode 100644 tests/ImageSharp.Tests/Processing/Filters/SaturateTest.cs create mode 100644 tests/ImageSharp.Tests/Processing/Filters/SepiaTest.cs diff --git a/tests/ImageSharp.Tests/Processing/Filters/BlackWhiteTest.cs b/tests/ImageSharp.Tests/Processing/Filters/BlackWhiteTest.cs new file mode 100644 index 0000000000..f913068e35 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/BlackWhiteTest.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.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Filters; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Filters +{ + public class BlackWhiteTest : BaseImageOperationsExtensionTest + { + [Fact] + public void BlackWhite_CorrectProcessor() + { + this.operations.BlackWhite(); + BlackWhiteProcessor p = this.Verify(); + } + + [Fact] + public void BlackWhite_rect_CorrectProcessor() + { + this.operations.BlackWhite(this.rect); + BlackWhiteProcessor p = this.Verify(this.rect); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs b/tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.cs new file mode 100644 index 0000000000..c26524880a --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/BrightnessTest.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.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Filters; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Effects +{ + public class BrightnessTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Brightness_amount_BrightnessProcessorDefaultsSet() + { + this.operations.Brightness(1.5F); + BrightnessProcessor processor = this.Verify(); + + Assert.Equal(1.5F, processor.Amount); + } + + [Fact] + public void Brightness_amount_rect_BrightnessProcessorDefaultsSet() + { + this.operations.Brightness(1.5F, this.rect); + BrightnessProcessor processor = this.Verify(this.rect); + + Assert.Equal(1.5F, processor.Amount); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/ColorBlindnessTest.cs b/tests/ImageSharp.Tests/Processing/Filters/ColorBlindnessTest.cs new file mode 100644 index 0000000000..7dd894403d --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/ColorBlindnessTest.cs @@ -0,0 +1,46 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Collections.Generic; + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Filters; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.ImageSharp.Tests.TestUtilities; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Filters +{ + public class ColorBlindnessTest : BaseImageOperationsExtensionTest + { + public static IEnumerable TheoryData = new[] { + new object[]{ new TestType(), ColorBlindnessMode.Achromatomaly }, + new object[]{ new TestType(), ColorBlindnessMode.Achromatopsia }, + new object[]{ new TestType(), ColorBlindnessMode.Deuteranomaly }, + new object[]{ new TestType(), ColorBlindnessMode.Deuteranopia }, + new object[]{ new TestType(), ColorBlindnessMode.Protanomaly }, + new object[]{ new TestType(), ColorBlindnessMode.Protanopia }, + new object[]{ new TestType(), ColorBlindnessMode.Tritanomaly }, + new object[]{ new TestType(), ColorBlindnessMode.Tritanopia } + }; + + [Theory] + [MemberData(nameof(TheoryData))] + public void ColorBlindness_CorrectProcessor(TestType testType, ColorBlindnessMode colorBlindness) + where T : IImageProcessor + { + this.operations.ColorBlindness(colorBlindness); + T p = this.Verify(); + } + [Theory] + [MemberData(nameof(TheoryData))] + public void ColorBlindness_rect_CorrectProcessor(TestType testType, ColorBlindnessMode colorBlindness) + where T : IImageProcessor + { + this.operations.ColorBlindness(colorBlindness, this.rect); + T p = this.Verify(this.rect); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs b/tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs new file mode 100644 index 0000000000..ff2b1c702d --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/ContrastTest.cs @@ -0,0 +1,33 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Effects +{ + using SixLabors.ImageSharp.Processing; + using SixLabors.ImageSharp.Processing.Processors.Filters; + + public class ContrastTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Contrast_amount_ContrastProcessorDefaultsSet() + { + this.operations.Contrast(1.5F); + ContrastProcessor processor = this.Verify(); + + Assert.Equal(1.5F, processor.Amount); + } + + [Fact] + public void Contrast_amount_rect_ContrastProcessorDefaultsSet() + { + this.operations.Contrast(1.5F, this.rect); + ContrastProcessor processor = this.Verify(this.rect); + + Assert.Equal(1.5F, processor.Amount); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/GrayscaleTest.cs b/tests/ImageSharp.Tests/Processing/Filters/GrayscaleTest.cs new file mode 100644 index 0000000000..94e1b00a71 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/GrayscaleTest.cs @@ -0,0 +1,47 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.Collections.Generic; + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Filters; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.ImageSharp.Tests.TestUtilities; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Filters +{ + public class GrayscaleTest : BaseImageOperationsExtensionTest + { + public static IEnumerable ModeTheoryData = new[] { + new object[]{ new TestType(), GrayscaleMode.Bt709 } + }; + + [Theory] + [MemberData(nameof(ModeTheoryData))] + public void Grayscale_mode_CorrectProcessor(TestType testType, GrayscaleMode mode) + where T : IImageProcessor + { + this.operations.Grayscale(mode); + var p = this.Verify(); + } + + [Theory] + [MemberData(nameof(ModeTheoryData))] + public void Grayscale_mode_rect_CorrectProcessor(TestType testType, GrayscaleMode mode) + where T : IImageProcessor + { + this.operations.Grayscale(mode, this.rect); + this.Verify(this.rect); + } + + [Fact] + public void Grayscale_rect_CorrectProcessor() + { + this.operations.Grayscale(this.rect); + this.Verify(this.rect); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/HueTest.cs b/tests/ImageSharp.Tests/Processing/Filters/HueTest.cs new file mode 100644 index 0000000000..0eb25043c7 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/HueTest.cs @@ -0,0 +1,32 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Filters; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Filters +{ + public class HueTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Hue_amount_HueProcessorDefaultsSet() + { + this.operations.Hue(34f); + var processor = this.Verify(); + + Assert.Equal(34f, processor.Degrees); + } + + [Fact] + public void Hue_amount_rect_HueProcessorDefaultsSet() + { + this.operations.Hue(5f, this.rect); + var processor = this.Verify(this.rect); + + Assert.Equal(5f, processor.Degrees); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/InvertTest.cs b/tests/ImageSharp.Tests/Processing/Filters/InvertTest.cs new file mode 100644 index 0000000000..ff59eab054 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/InvertTest.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.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Filters; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Effects +{ + public class InvertTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Invert_InvertProcessorDefaultsSet() + { + this.operations.Invert(); + var processor = this.Verify(); + } + + [Fact] + public void Pixelate_rect_PixelateProcessorDefaultsSet() + { + this.operations.Invert(this.rect); + var processor = this.Verify(this.rect); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/KodachromeTest.cs b/tests/ImageSharp.Tests/Processing/Filters/KodachromeTest.cs new file mode 100644 index 0000000000..a06a9d65c0 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/KodachromeTest.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.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Filters; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Filters +{ + public class KodachromeTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Kodachrome_amount_KodachromeProcessorDefaultsSet() + { + this.operations.Kodachrome(); + var processor = this.Verify(); + } + + [Fact] + public void Kodachrome_amount_rect_KodachromeProcessorDefaultsSet() + { + this.operations.Kodachrome(this.rect); + var processor = this.Verify(this.rect); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs b/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs new file mode 100644 index 0000000000..446cfa1c33 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/LomographTest.cs @@ -0,0 +1,31 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Primitives; +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + using SixLabors.ImageSharp.Processing; + using SixLabors.ImageSharp.Processing.Processors.Filters; + + public class LomographTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Lomograph_amount_LomographProcessorDefaultsSet() + { + this.operations.Lomograph(); + var processor = this.Verify(); + } + + [Fact] + public void Lomograph_amount_rect_LomographProcessorDefaultsSet() + { + this.operations.Lomograph(this.rect); + var processor = this.Verify(this.rect); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/OpacityTest.cs b/tests/ImageSharp.Tests/Processing/Filters/OpacityTest.cs new file mode 100644 index 0000000000..95ea2c23e9 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/OpacityTest.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.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Filters; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Effects +{ + public class OpacityTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Alpha_amount_AlphaProcessorDefaultsSet() + { + this.operations.Opacity(0.2f); + OpacityProcessor processor = this.Verify(); + + Assert.Equal(.2f, processor.Amount); + } + + [Fact] + public void Alpha_amount_rect_AlphaProcessorDefaultsSet() + { + this.operations.Opacity(0.6f, this.rect); + OpacityProcessor processor = this.Verify(this.rect); + + Assert.Equal(.6f, processor.Amount); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/PolaroidTest.cs b/tests/ImageSharp.Tests/Processing/Filters/PolaroidTest.cs new file mode 100644 index 0000000000..f63b892bc9 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/PolaroidTest.cs @@ -0,0 +1,28 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Filters; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Filters +{ + public class PolaroidTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Polaroid_amount_PolaroidProcessorDefaultsSet() + { + this.operations.Polaroid(); + var processor = this.Verify(); + } + + [Fact] + public void Polaroid_amount_rect_PolaroidProcessorDefaultsSet() + { + this.operations.Polaroid(this.rect); + var processor = this.Verify(this.rect); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/SaturateTest.cs b/tests/ImageSharp.Tests/Processing/Filters/SaturateTest.cs new file mode 100644 index 0000000000..b1583b4aca --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/SaturateTest.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.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Filters; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Filters +{ + public class SaturateTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Saturation_amount_SaturationProcessorDefaultsSet() + { + this.operations.Saturate(34); + SaturateProcessor processor = this.Verify(); + + Assert.Equal(34, processor.Amount); + } + + [Fact] + public void Saturation_amount_rect_SaturationProcessorDefaultsSet() + { + this.operations.Saturate(5, this.rect); + SaturateProcessor processor = this.Verify(this.rect); + + Assert.Equal(5, processor.Amount); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Filters/SepiaTest.cs b/tests/ImageSharp.Tests/Processing/Filters/SepiaTest.cs new file mode 100644 index 0000000000..441c9739d7 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Filters/SepiaTest.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.ImageSharp.Processing; +using SixLabors.ImageSharp.Processing.Processors.Filters; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Filters +{ + public class SepiaTest : BaseImageOperationsExtensionTest + { + [Fact] + public void Sepia_amount_SepiaProcessorDefaultsSet() + { + this.operations.Sepia(); + var processor = this.Verify(); + } + + [Fact] + public void Sepia_amount_rect_SepiaProcessorDefaultsSet() + { + this.operations.Sepia(this.rect); + var processor = this.Verify(this.rect); + } + } +} \ No newline at end of file From d8b918f14861d9d459599c9f0eadd30864e2835a Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 02:17:33 +0200 Subject: [PATCH 11/31] fix warnings and improve xmldocs --- .../Advanced/AdvancedImageExtensions.cs | 1 - src/ImageSharp/Formats/Bmp/BmpDecoder.cs | 1 + src/ImageSharp/Formats/Gif/GifDecoder.cs | 3 +- src/ImageSharp/Formats/IImageDecoder.cs | 11 ++- src/ImageSharp/Formats/Jpeg/JpegDecoder.cs | 3 +- src/ImageSharp/Formats/Png/PngDecoder.cs | 3 +- src/ImageSharp/IImageVisitor.cs | 22 +++++ src/ImageSharp/Image.Decode.cs | 4 +- src/ImageSharp/Image.FromFile.cs | 13 ++- src/ImageSharp/Image.FromStream.cs | 1 + src/ImageSharp/Image.WrapMemory.cs | 8 +- src/ImageSharp/Image.cs | 43 ++++++---- src/ImageSharp/ImageExtensions.cs | 3 - src/ImageSharp/Image{TPixel}.cs | 25 ++---- .../Processing/IImageProcessingContext.cs | 42 +++++++++ .../IImageProcessingContext{TPixel}.cs | 26 +----- src/ImageSharp/Processing/PadExtensions.cs | 3 +- .../Processing/PolaroidExtensions.cs | 1 - .../Processing/ProcessingExtensions.cs | 85 +++++++++++-------- .../Processors/Filters/BlackWhiteProcessor.cs | 3 +- .../Processors/Filters/FilterProcessor.cs | 4 + .../LomographProcessorImplementation.cs | 13 +-- .../Processors/Filters/OpacityProcessor.cs | 3 +- .../Processors/Filters/PolaroidProcessor.cs | 26 +----- .../PolaroidProcessorImplementation.cs | 31 +++++++ .../Processors/Filters/ProtanopiaProcessor.cs | 1 - .../Processing/Processors/IImageProcessor.cs | 13 ++- .../Transforms/Resize/ResizeProcessor.cs | 72 +++++++++------- .../Resize/ResizeProcessorImplementation.cs | 6 +- src/ImageSharp/Processing/ResizeExtensions.cs | 10 --- .../ImageSharp.Benchmarks.csproj | 3 +- 31 files changed, 287 insertions(+), 196 deletions(-) create mode 100644 src/ImageSharp/IImageVisitor.cs create mode 100644 src/ImageSharp/Processing/IImageProcessingContext.cs create mode 100644 src/ImageSharp/Processing/Processors/Filters/PolaroidProcessorImplementation.cs diff --git a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs index 378b6e8b96..e0d7a4b184 100644 --- a/src/ImageSharp/Advanced/AdvancedImageExtensions.cs +++ b/src/ImageSharp/Advanced/AdvancedImageExtensions.cs @@ -18,7 +18,6 @@ namespace SixLabors.ImageSharp.Advanced /// /// Gets the configuration for the image. /// - /// The Pixel format. /// The source image. /// Returns the configuration. public static Configuration GetConfiguration(this Image source) diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs index 4922a92003..ebb7ffdf3c 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoder.cs @@ -30,6 +30,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp return new BmpDecoderCore(configuration, this).Decode(stream); } + /// public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); /// diff --git a/src/ImageSharp/Formats/Gif/GifDecoder.cs b/src/ImageSharp/Formats/Gif/GifDecoder.cs index 6498f4aa27..1addcd0abf 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoder.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoder.cs @@ -44,7 +44,8 @@ namespace SixLabors.ImageSharp.Formats.Gif var decoder = new GifDecoderCore(configuration, this); return decoder.Identify(stream); } - + + /// public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); } } diff --git a/src/ImageSharp/Formats/IImageDecoder.cs b/src/ImageSharp/Formats/IImageDecoder.cs index 625c4efb6f..8dafdac795 100644 --- a/src/ImageSharp/Formats/IImageDecoder.cs +++ b/src/ImageSharp/Formats/IImageDecoder.cs @@ -12,15 +12,22 @@ namespace SixLabors.ImageSharp.Formats public interface IImageDecoder { /// - /// Decodes the image from the specified stream to the . + /// Decodes the image from the specified stream to an of a specific pixel type. /// /// The pixel format. /// The configuration for the image. /// The containing image data. - /// The decoded image + /// The decoded image of a given pixel type. Image Decode(Configuration configuration, Stream stream) where TPixel : struct, IPixel; + /// + /// Decodes the image from the specified stream to an . + /// The decoder is free to choose the pixel type. + /// + /// The configuration for the image. + /// The containing image data. + /// The decoded image of a pixel type chosen by the decoder. Image Decode(Configuration configuration, Stream stream); } } diff --git a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs index 7459abec56..a1bf048521 100644 --- a/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs +++ b/src/ImageSharp/Formats/Jpeg/JpegDecoder.cs @@ -38,7 +38,8 @@ namespace SixLabors.ImageSharp.Formats.Jpeg return decoder.Identify(stream); } } - + + /// public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Png/PngDecoder.cs b/src/ImageSharp/Formats/Png/PngDecoder.cs index e8c5ac8e81..040da94737 100644 --- a/src/ImageSharp/Formats/Png/PngDecoder.cs +++ b/src/ImageSharp/Formats/Png/PngDecoder.cs @@ -59,7 +59,8 @@ namespace SixLabors.ImageSharp.Formats.Png var decoder = new PngDecoderCore(configuration, this); return decoder.Identify(stream); } - + + /// public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); } } \ No newline at end of file diff --git a/src/ImageSharp/IImageVisitor.cs b/src/ImageSharp/IImageVisitor.cs new file mode 100644 index 0000000000..971c4d37cb --- /dev/null +++ b/src/ImageSharp/IImageVisitor.cs @@ -0,0 +1,22 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp +{ + /// + /// A visitor to implement double-dispatch pattern in order to apply pixel-specific operations + /// on non-generic instances. The operation is dispatched by . + /// + internal interface IImageVisitor + { + /// + /// Provides a pixel-specific implementation for a given operation. + /// + /// The image. + /// The pixel type. + void Visit(Image image) + where TPixel : struct, IPixel; + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image.Decode.cs b/src/ImageSharp/Image.Decode.cs index 17a15a9160..8d0df599ea 100644 --- a/src/ImageSharp/Image.Decode.cs +++ b/src/ImageSharp/Image.Decode.cs @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp /// The image might be filled with memory garbage. /// /// The pixel type - /// The + /// The /// The width of the image /// The height of the image /// The @@ -102,7 +102,7 @@ namespace SixLabors.ImageSharp Image img = decoder.Decode(config, stream); return (img, format); } - + private static (Image img, IImageFormat format) Decode(Stream stream, Configuration config) { IImageDecoder decoder = DiscoverDecoder(stream, config, out IImageFormat format); diff --git a/src/ImageSharp/Image.FromFile.cs b/src/ImageSharp/Image.FromFile.cs index 0b49668862..61500e20d7 100644 --- a/src/ImageSharp/Image.FromFile.cs +++ b/src/ImageSharp/Image.FromFile.cs @@ -162,7 +162,18 @@ namespace SixLabors.ImageSharp return Load(config, stream, out format); } } - + + /// + /// Create a new instance of the class from the given file. + /// The pixel type is selected by the decoder. + /// + /// The configuration options. + /// The file path to the image. + /// The mime type of the decoded image. + /// + /// Thrown if the stream is not readable nor seekable. + /// + /// A new . public static Image Load(Configuration config, string path, out IImageFormat format) { using (Stream stream = config.FileSystem.OpenRead(path)) diff --git a/src/ImageSharp/Image.FromStream.cs b/src/ImageSharp/Image.FromStream.cs index b16501b0f2..a4a9eeec75 100644 --- a/src/ImageSharp/Image.FromStream.cs +++ b/src/ImageSharp/Image.FromStream.cs @@ -182,6 +182,7 @@ namespace SixLabors.ImageSharp throw new NotSupportedException(sb.ToString()); } + private static Image Load(Configuration config, Stream stream, out IImageFormat format) { config = config ?? Configuration.Default; diff --git a/src/ImageSharp/Image.WrapMemory.cs b/src/ImageSharp/Image.WrapMemory.cs index 49bc99e9ee..095991b076 100644 --- a/src/ImageSharp/Image.WrapMemory.cs +++ b/src/ImageSharp/Image.WrapMemory.cs @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp /// allowing to view/manipulate it as an ImageSharp instance. /// /// The pixel type - /// The + /// The /// The pixel memory. /// The width of the memory image. /// The height of the memory image. @@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp /// allowing to view/manipulate it as an ImageSharp instance. /// /// The pixel type - /// The + /// The /// The pixel memory. /// The width of the memory image. /// The height of the memory image. @@ -85,7 +85,7 @@ namespace SixLabors.ImageSharp /// It will be disposed together with the result image. /// /// The pixel type - /// The + /// The /// The that is being transferred to the image /// The width of the memory image. /// The height of the memory image. @@ -111,7 +111,7 @@ namespace SixLabors.ImageSharp /// It will be disposed together with the result image. /// /// The pixel type. - /// The + /// The /// The that is being transferred to the image. /// The width of the memory image. /// The height of the memory image. diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs index a27f16ab30..d0a1c4319d 100644 --- a/src/ImageSharp/Image.cs +++ b/src/ImageSharp/Image.cs @@ -10,15 +10,30 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp { - internal interface IImageVisitor - { - void Visit(Image image) - where TPixel : struct, IPixel; - } - + /// + /// Encapsulates an image, which consists of the pixel data for a graphics image and its attributes. + /// For the non-generic type, the pixel type is only known at runtime. + /// is always implemented by a pixel-specific instance. + /// public abstract partial class Image : IImage, IConfigurable { - protected readonly Configuration configuration; + /// + /// Initializes a new instance of the class. + /// + /// The . + /// The . + /// The . + protected Image(Configuration configuration, PixelTypeInfo pixelType, ImageMetadata metadata) + { + this.Configuration = configuration ?? Configuration.Default; + this.PixelType = pixelType; + this.Metadata = metadata ?? new ImageMetadata(); + } + + /// + /// Gets the . + /// + protected Configuration Configuration { get; } /// public PixelTypeInfo PixelType { get; } @@ -28,22 +43,16 @@ namespace SixLabors.ImageSharp /// public abstract int Height { get; } - + /// public ImageMetadata Metadata { get; } /// /// Gets the pixel buffer. /// - Configuration IConfigurable.Configuration => this.configuration; - - protected Image(Configuration configuration, PixelTypeInfo pixelType, ImageMetadata metadata) - { - this.configuration = configuration ?? Configuration.Default; - this.PixelType = pixelType; - this.Metadata = metadata ?? new ImageMetadata(); - } + Configuration IConfigurable.Configuration => this.Configuration; + /// public abstract void Dispose(); internal abstract void AcceptVisitor(IImageVisitor visitor); @@ -63,7 +72,7 @@ namespace SixLabors.ImageSharp this.AcceptVisitor(visitor); } - class EncodeVisitor : IImageVisitor + private class EncodeVisitor : IImageVisitor { private readonly IImageEncoder encoder; diff --git a/src/ImageSharp/ImageExtensions.cs b/src/ImageSharp/ImageExtensions.cs index 3bd60694d5..ec4c364d83 100644 --- a/src/ImageSharp/ImageExtensions.cs +++ b/src/ImageSharp/ImageExtensions.cs @@ -19,7 +19,6 @@ namespace SixLabors.ImageSharp /// /// Writes the image to the given stream using the currently loaded image format. /// - /// The pixel format. /// The source image. /// The file path to save the image to. /// Thrown if the stream is null. @@ -61,7 +60,6 @@ namespace SixLabors.ImageSharp /// /// Writes the image to the given stream using the currently loaded image format. /// - /// The pixel format. /// The source image. /// The file path to save the image to. /// The encoder to save the image with. @@ -78,7 +76,6 @@ namespace SixLabors.ImageSharp /// /// Writes the image to the given stream using the currently loaded image format. /// - /// The Pixel format. /// The source image. /// The stream to save the image to. /// The format to save the image in. diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index ea6d0beb25..243e2912be 100644 --- a/src/ImageSharp/Image{TPixel}.cs +++ b/src/ImageSharp/Image{TPixel}.cs @@ -16,6 +16,7 @@ namespace SixLabors.ImageSharp { /// /// Encapsulates an image, which consists of the pixel data for a graphics image and its attributes. + /// For generic -s the pixel type is known at compile time. /// /// The pixel format. public sealed class Image : Image @@ -68,8 +69,6 @@ namespace SixLabors.ImageSharp internal Image(Configuration configuration, int width, int height, ImageMetadata metadata) : base(configuration, PixelTypeInfo.Create(), metadata) { - this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8); - this.Metadata = metadata ?? new ImageMetadata(); this.Frames = new ImageFrameCollection(this, width, height, default(TPixel)); } @@ -85,8 +84,6 @@ namespace SixLabors.ImageSharp internal Image(Configuration configuration, MemorySource memorySource, int width, int height, ImageMetadata metadata) : base(configuration, PixelTypeInfo.Create(), metadata) { - this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8); - this.Metadata = metadata; this.Frames = new ImageFrameCollection(this, width, height, memorySource); } @@ -102,8 +99,6 @@ namespace SixLabors.ImageSharp internal Image(Configuration configuration, int width, int height, TPixel backgroundColor, ImageMetadata metadata) : base(configuration, PixelTypeInfo.Create(), metadata) { - this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8); - this.Metadata = metadata ?? new ImageMetadata(); this.Frames = new ImageFrameCollection(this, width, height, backgroundColor); } @@ -115,27 +110,18 @@ namespace SixLabors.ImageSharp /// The images metadata. /// The frames that will be owned by this image instance. internal Image(Configuration configuration, ImageMetadata metadata, IEnumerable> frames) - : base(configuration,PixelTypeInfo.Create(), metadata) + : base(configuration, PixelTypeInfo.Create(), metadata) { - this.PixelType = new PixelTypeInfo(Unsafe.SizeOf() * 8); - this.Metadata = metadata ?? new ImageMetadata(); - this.Frames = new ImageFrameCollection(this, frames); } - - /// - public PixelTypeInfo PixelType { get; } - /// public override int Width => this.Frames.RootFrame.Width; /// public override int Height => this.Frames.RootFrame.Height; - /// - public ImageMetadata Metadata { get; } - + /// /// Gets the frames. /// public ImageFrameCollection Frames { get; } @@ -154,7 +140,6 @@ namespace SixLabors.ImageSharp public TPixel this[int x, int y] { get => this.PixelSource.PixelBuffer[x, y]; - set => this.PixelSource.PixelBuffer[x, y] = value; } @@ -162,7 +147,7 @@ namespace SixLabors.ImageSharp /// Clones the current image /// /// Returns a new image with all the same metadata as the original. - public Image Clone() => this.Clone(this.configuration); + public Image Clone() => this.Clone(this.Configuration); /// /// Clones the current image with the given configuration. @@ -181,7 +166,7 @@ namespace SixLabors.ImageSharp /// The pixel format. /// The public Image CloneAs() - where TPixel2 : struct, IPixel => this.CloneAs(this.configuration); + where TPixel2 : struct, IPixel => this.CloneAs(this.Configuration); /// /// Returns a copy of the image in the given pixel format. diff --git a/src/ImageSharp/Processing/IImageProcessingContext.cs b/src/ImageSharp/Processing/IImageProcessingContext.cs new file mode 100644 index 0000000000..509b1313d9 --- /dev/null +++ b/src/ImageSharp/Processing/IImageProcessingContext.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Processing.Processors; +using SixLabors.Memory; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing +{ + /// + /// A pixel-agnostic interface to queue up image operations to apply to an image. + /// + public interface IImageProcessingContext + { + /// + /// Gets a reference to the used to allocate buffers + /// for this context. + /// + MemoryAllocator MemoryAllocator { get; } + + /// + /// Gets the image dimensions at the current point in the processing pipeline. + /// + /// The . + Size GetCurrentSize(); + + /// + /// Adds the processor to the current set of image operations to be applied. + /// + /// The processor to apply. + /// The area to apply it to. + /// The current operations class to allow chaining of operations. + IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle); + + /// + /// Adds the processor to the current set of image operations to be applied. + /// + /// The processor to apply. + /// The current operations class to allow chaining of operations. + IImageProcessingContext ApplyProcessor(IImageProcessor processor); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs b/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs index fefa973f7c..b49cfe2151 100644 --- a/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs +++ b/src/ImageSharp/Processing/IImageProcessingContext{TPixel}.cs @@ -3,32 +3,12 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors; -using SixLabors.Memory; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { - public interface IImageProcessingContext - { - /// - /// Gets a reference to the used to allocate buffers - /// for this context. - /// - MemoryAllocator MemoryAllocator { get; } - - /// - /// Gets the image dimensions at the current point in the processing pipeline. - /// - /// The - Size GetCurrentSize(); - - IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle); - - IImageProcessingContext ApplyProcessor(IImageProcessor processor); - } - /// - /// An interface to queue up image operations to apply to an image. + /// A pixel-specific interface to queue up image operations to apply to an image. /// /// The pixel format public interface IImageProcessingContext : IImageProcessingContext @@ -37,8 +17,8 @@ namespace SixLabors.ImageSharp.Processing /// /// Adds the processor to the current set of image operations to be applied. /// - /// The processor to apply - /// The area to apply it to + /// The processor to apply. + /// The area to apply it to. /// The current operations class to allow chaining of operations. IImageProcessingContext ApplyProcessor(IImageProcessor processor, Rectangle rectangle); diff --git a/src/ImageSharp/Processing/PadExtensions.cs b/src/ImageSharp/Processing/PadExtensions.cs index 2db219795f..0422f7c59d 100644 --- a/src/ImageSharp/Processing/PadExtensions.cs +++ b/src/ImageSharp/Processing/PadExtensions.cs @@ -14,7 +14,6 @@ namespace SixLabors.ImageSharp.Processing /// /// Evenly pads an image to fit the new dimensions. /// - /// The pixel format. /// The source image to pad. /// The new width. /// The new height. @@ -25,7 +24,7 @@ namespace SixLabors.ImageSharp.Processing { Size = new Size(width, height), Mode = ResizeMode.BoxPad, - Sampler = KnownResamplers.NearestNeighbor + Sampler = KnownResamplers.NearestNeighbor, }; return source.Resize(options); diff --git a/src/ImageSharp/Processing/PolaroidExtensions.cs b/src/ImageSharp/Processing/PolaroidExtensions.cs index 4172c1d615..4e78411689 100644 --- a/src/ImageSharp/Processing/PolaroidExtensions.cs +++ b/src/ImageSharp/Processing/PolaroidExtensions.cs @@ -23,7 +23,6 @@ namespace SixLabors.ImageSharp.Processing /// /// Alters the colors of the image recreating an old Polaroid camera effect. /// - /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. diff --git a/src/ImageSharp/Processing/ProcessingExtensions.cs b/src/ImageSharp/Processing/ProcessingExtensions.cs index a1acf3aa0b..c72e8cfb77 100644 --- a/src/ImageSharp/Processing/ProcessingExtensions.cs +++ b/src/ImageSharp/Processing/ProcessingExtensions.cs @@ -13,43 +13,6 @@ namespace SixLabors.ImageSharp.Processing /// public static class ProcessingExtensions { - class ProcessingVisitor : IImageVisitor - { - private readonly Action operation; - - private readonly bool mutate; - - public Image ResultImage { get; private set; } - - public ProcessingVisitor(Action operation, bool mutate) - { - this.operation = operation; - this.mutate = mutate; - } - - public void Visit(Image image) - where TPixel : struct, IPixel - { - IInternalImageProcessingContext operationsRunner = image.GetConfiguration() - .ImageOperationsProvider.CreateImageProcessingContext(image, this.mutate); - this.operation(operationsRunner); - this.ResultImage = operationsRunner.Apply(); - } - } - - public static void Mutate(this Image source, Action operation) - { - ProcessingVisitor visitor = new ProcessingVisitor(operation, true); - source.AcceptVisitor(visitor); - } - - public static Image Clone(this Image source, Action operation) - { - ProcessingVisitor visitor = new ProcessingVisitor(operation, false); - source.AcceptVisitor(visitor); - return visitor.ResultImage; - } - /// /// Applies the given operation to the mutable image. /// Useful when we need to extract information like Width/Height to parametrize the next operation working on the chain. @@ -62,6 +25,17 @@ namespace SixLabors.ImageSharp.Processing public static IImageProcessingContext Apply(this IImageProcessingContext source, Action> operation) where TPixel : struct, IPixel => source.ApplyProcessor(new DelegateProcessor(operation)); + /// + /// Mutates the source image by applying the image operation to it. + /// + /// The image to mutate. + /// The operation to perform on the source. + public static void Mutate(this Image source, Action operation) + { + ProcessingVisitor visitor = new ProcessingVisitor(operation, true); + source.AcceptVisitor(visitor); + } + /// /// Mutates the source image by applying the image operation to it. /// @@ -96,6 +70,19 @@ namespace SixLabors.ImageSharp.Processing operationsRunner.Apply(); } + /// + /// Creates a deep clone of the current image. The clone is then mutated by the given operation. + /// + /// The image to clone. + /// The operation to perform on the clone. + /// The new . + public static Image Clone(this Image source, Action operation) + { + ProcessingVisitor visitor = new ProcessingVisitor(operation, false); + source.AcceptVisitor(visitor); + return visitor.ResultImage; + } + /// /// Creates a deep clone of the current image. The clone is then mutated by the given operation. /// @@ -149,5 +136,29 @@ namespace SixLabors.ImageSharp.Processing return source; } + + private class ProcessingVisitor : IImageVisitor + { + private readonly Action operation; + + private readonly bool mutate; + + public ProcessingVisitor(Action operation, bool mutate) + { + this.operation = operation; + this.mutate = mutate; + } + + public Image ResultImage { get; private set; } + + public void Visit(Image image) + where TPixel : struct, IPixel + { + IInternalImageProcessingContext operationsRunner = image.GetConfiguration() + .ImageOperationsProvider.CreateImageProcessingContext(image, this.mutate); + this.operation(operationsRunner); + this.ResultImage = operationsRunner.Apply(); + } + } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs index 8bf925cf0f..698f11cbf0 100644 --- a/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs @@ -8,11 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a black and white filter matrix to the image /// - /// The pixel format. internal class BlackWhiteProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public BlackWhiteProcessor() : base(KnownFilterMatrices.BlackWhiteFilter) diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs index 5637e97700..886b4e0962 100644 --- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs @@ -6,6 +6,9 @@ using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Filters { + /// + /// Provides methods that accept a matrix to apply free-form filters to images. + /// public class FilterProcessor : IImageProcessor { /// @@ -19,6 +22,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// public ColorMatrix Matrix { get; } + /// public virtual IImageProcessor CreatePixelSpecificProcessor() where TPixel : struct, IPixel { diff --git a/src/ImageSharp/Processing/Processors/Filters/LomographProcessorImplementation.cs b/src/ImageSharp/Processing/Processors/Filters/LomographProcessorImplementation.cs index d851b98e06..a922e71256 100644 --- a/src/ImageSharp/Processing/Processors/Filters/LomographProcessorImplementation.cs +++ b/src/ImageSharp/Processing/Processors/Filters/LomographProcessorImplementation.cs @@ -7,20 +7,23 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Filters { + /// + /// Converts the colors of the image recreating an old Lomograph effect. + /// internal class LomographProcessorImplementation : FilterProcessorImplementation where TPixel : struct, IPixel { private static readonly TPixel VeryDarkGreen = ColorBuilder.FromRGBA(0, 10, 0, 255); - /// - protected override void AfterFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + public LomographProcessorImplementation(LomographProcessor definition) + : base(definition) { - new VignetteProcessor(VeryDarkGreen).Apply(source, sourceRectangle, configuration); } - public LomographProcessorImplementation(FilterProcessor definition) - : base(definition) + /// + protected override void AfterFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { + new VignetteProcessor(VeryDarkGreen).Apply(source, sourceRectangle, configuration); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs index 2d6bf6e833..e790753f3b 100644 --- a/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs @@ -8,11 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies an opacity filter matrix using the given amount. /// - /// The pixel format. internal class OpacityProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The proportion of the conversion. Must be between 0 and 1. public OpacityProcessor(float amount) diff --git a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs index af16d9eab2..0b272c3396 100644 --- a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs @@ -1,10 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors.Overlays; -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// @@ -13,33 +9,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters public class PolaroidProcessor : FilterProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public PolaroidProcessor() : base(KnownFilterMatrices.PolaroidFilter) { } + /// public override IImageProcessor CreatePixelSpecificProcessor() => new PolaroidProcessorImplementation(this); } - - internal class PolaroidProcessorImplementation : FilterProcessorImplementation - where TPixel : struct, IPixel - { - private static readonly TPixel VeryDarkOrange = ColorBuilder.FromRGB(102, 34, 0); - private static readonly TPixel LightOrange = ColorBuilder.FromRGBA(255, 153, 102, 128); - - /// - protected override void AfterFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - { - new VignetteProcessor(VeryDarkOrange).Apply(source, sourceRectangle, configuration); - new GlowProcessor(LightOrange, source.Width / 4F).Apply(source, sourceRectangle, configuration); - } - - public PolaroidProcessorImplementation(FilterProcessor definition) - : base(definition) - { - } - } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessorImplementation.cs b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessorImplementation.cs new file mode 100644 index 0000000000..ec3c5ed26b --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessorImplementation.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.ImageSharp.Processing.Processors.Overlays; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Filters +{ + /// + /// Converts the colors of the image recreating an old Polaroid effect. + /// + internal class PolaroidProcessorImplementation : FilterProcessorImplementation + where TPixel : struct, IPixel + { + private static readonly TPixel VeryDarkOrange = ColorBuilder.FromRGB(102, 34, 0); + private static readonly TPixel LightOrange = ColorBuilder.FromRGBA(255, 153, 102, 128); + + public PolaroidProcessorImplementation(FilterProcessor definition) + : base(definition) + { + } + + /// + protected override void AfterFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + new VignetteProcessor(VeryDarkOrange).Apply(source, sourceRectangle, configuration); + new GlowProcessor(LightOrange, source.Width / 4F).Apply(source, sourceRectangle, configuration); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/ProtanopiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ProtanopiaProcessor.cs index 94eeaae475..bd92579130 100644 --- a/src/ImageSharp/Processing/Processors/Filters/ProtanopiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/ProtanopiaProcessor.cs @@ -8,7 +8,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating Protanopia (Red-Blind) color blindness. /// - /// The pixel format. internal class ProtanopiaProcessor : FilterProcessor { /// diff --git a/src/ImageSharp/Processing/Processors/IImageProcessor.cs b/src/ImageSharp/Processing/Processors/IImageProcessor.cs index 68db453806..e9b2c9c0e3 100644 --- a/src/ImageSharp/Processing/Processors/IImageProcessor.cs +++ b/src/ImageSharp/Processing/Processors/IImageProcessor.cs @@ -6,14 +6,23 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors { + /// + /// Defines an algorithm to alter the pixels of an image. + /// public interface IImageProcessor { + /// + /// Creates a pixel specific that is capable for executing + /// the processing algorithm on an . + /// + /// The pixel type. + /// The IImageProcessor CreatePixelSpecificProcessor() where TPixel : struct, IPixel; } /// - /// Encapsulates methods to alter the pixels of an image. + /// Implements an algorithm to alter the pixels of an image. /// /// The pixel format. public interface IImageProcessor @@ -58,7 +67,7 @@ namespace SixLabors.ImageSharp.Processing.Processors public void Visit(Image image) where TPixel : struct, IPixel { - var processorImpl = processor.CreatePixelSpecificProcessor(); + var processorImpl = this.processor.CreatePixelSpecificProcessor(); processorImpl.Apply(image, this.sourceRectangle); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index d3077b7e65..021451b5c7 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -8,36 +8,20 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { - // The non-generic processor is responsible for: - // - Encapsulating the parameters of the processor - // - Implementing a factory method to create the pixel-specific processor that contains the implementation + /// + /// Implements resizing of images using various resamplers. + /// public class ResizeProcessor : IImageProcessor { /// - /// Gets the sampler to perform the resize operation. + /// Initializes a new instance of the class. /// - public IResampler Sampler { get; } - - /// - /// Gets the target width. - /// - public int Width { get; } - - /// - /// Gets the target height. - /// - public int Height { get; } - - /// - /// Gets the resize rectangle. - /// - public Rectangle TargetRectangle { get; } - - /// - /// Gets a value indicating whether to compress or expand individual pixel color values on processing. - /// - public bool Compand { get; } - + /// The . + /// The width. + /// The height. + /// The size of the source image. + /// The target rectangle to resize into. + /// A value indicating whether to apply RGBA companding. public ResizeProcessor(IResampler sampler, int width, int height, Size sourceSize, Rectangle targetRectangle, bool compand) { Guard.NotNull(sampler, nameof(sampler)); @@ -67,12 +51,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.TargetRectangle = targetRectangle; this.Compand = compand; } - + /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The resize options - /// The source image size + /// The resize options. + /// The source image size. public ResizeProcessor(ResizeOptions options, Size sourceSize) { Guard.NotNull(options, nameof(options)); @@ -109,7 +93,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The sampler to perform the resize operation. /// The target width. @@ -120,6 +104,32 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms { } + /// + /// Gets the sampler to perform the resize operation. + /// + public IResampler Sampler { get; } + + /// + /// Gets the target width. + /// + public int Width { get; } + + /// + /// Gets the target height. + /// + public int Height { get; } + + /// + /// Gets the resize rectangle. + /// + public Rectangle TargetRectangle { get; } + + /// + /// Gets a value indicating whether to compress or expand individual pixel color values on processing. + /// + public bool Compand { get; } + + /// public IImageProcessor CreatePixelSpecificProcessor() where TPixel : struct, IPixel { diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessorImplementation.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessorImplementation.cs index 1a8bb931e0..36a054c54e 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessorImplementation.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessorImplementation.cs @@ -19,9 +19,11 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { /// - /// Provides methods that allow the resizing of images using various algorithms. - /// Adapted from + /// Implements resizing of images using various resamplers. /// + /// + /// The original code has been adapted from . + /// /// The pixel format. internal class ResizeProcessorImplementation : TransformProcessorBase where TPixel : struct, IPixel diff --git a/src/ImageSharp/Processing/ResizeExtensions.cs b/src/ImageSharp/Processing/ResizeExtensions.cs index cf029eb152..57dbcb1d16 100644 --- a/src/ImageSharp/Processing/ResizeExtensions.cs +++ b/src/ImageSharp/Processing/ResizeExtensions.cs @@ -15,7 +15,6 @@ namespace SixLabors.ImageSharp.Processing /// /// Resizes an image in accordance with the given . /// - /// The pixel format. /// The image to resize. /// The resize options. /// The @@ -26,7 +25,6 @@ namespace SixLabors.ImageSharp.Processing /// /// Resizes an image to the given . /// - /// The pixel format. /// The image to resize. /// The target image size. /// The @@ -37,7 +35,6 @@ namespace SixLabors.ImageSharp.Processing /// /// Resizes an image to the given . /// - /// The pixel format. /// The image to resize. /// The target image size. /// Whether to compress and expand the image color-space to gamma correct the image during processing. @@ -49,7 +46,6 @@ namespace SixLabors.ImageSharp.Processing /// /// Resizes an image to the given width and height. /// - /// The pixel format. /// The image to resize. /// The target image width. /// The target image height. @@ -61,7 +57,6 @@ namespace SixLabors.ImageSharp.Processing /// /// Resizes an image to the given width and height. /// - /// The pixel format. /// The image to resize. /// The target image width. /// The target image height. @@ -74,7 +69,6 @@ namespace SixLabors.ImageSharp.Processing /// /// Resizes an image to the given width and height with the given sampler. /// - /// The pixel format. /// The image to resize. /// The target image width. /// The target image height. @@ -87,7 +81,6 @@ namespace SixLabors.ImageSharp.Processing /// /// Resizes an image to the given width and height with the given sampler. /// - /// The pixel format. /// The image to resize. /// The target image size. /// The to perform the resampling. @@ -100,7 +93,6 @@ namespace SixLabors.ImageSharp.Processing /// /// Resizes an image to the given width and height with the given sampler. /// - /// The pixel format. /// The image to resize. /// The target image width. /// The target image height. @@ -115,7 +107,6 @@ namespace SixLabors.ImageSharp.Processing /// Resizes an image to the given width and height with the given sampler and /// source rectangle. /// - /// The pixel format. /// The image to resize. /// The target image width. /// The target image height. @@ -142,7 +133,6 @@ namespace SixLabors.ImageSharp.Processing /// /// Resizes an image to the given width and height with the given sampler and source rectangle. /// - /// The pixel format. /// The image to resize. /// The target image width. /// The target image height. diff --git a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj index 9af4d57cff..2a5408da17 100644 --- a/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj +++ b/tests/ImageSharp.Benchmarks/ImageSharp.Benchmarks.csproj @@ -1,6 +1,7 @@  - netcoreapp2.1;net472 + + netcoreapp2.1 Exe True SixLabors.ImageSharp.Benchmarks From 2995f596ef77a260ead68c506f91126f6ee8f733 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 14:34:11 +0200 Subject: [PATCH 12/31] *ProcessorImplementation ===> *Processor, add suppression of SA1413 to AssemblyInfo.cs --- .../Processing/Processors/Filters/FilterProcessor.cs | 2 +- ...sorImplementation.cs => FilterProcessor{TPixel}.cs} | 4 ++-- .../Processors/Filters/GrayscaleBt709Processor.cs | 2 +- .../Processors/Filters/LomographProcessor.cs | 2 +- ...Implementation.cs => LomographProcessor{TPixel}.cs} | 4 ++-- .../Processing/Processors/Filters/PolaroidProcessor.cs | 2 +- ...rImplementation.cs => PolaroidProcessor{TPixel}.cs} | 4 ++-- .../Processors/Transforms/Resize/ResizeProcessor.cs | 2 +- ...sorImplementation.cs => ResizeProcessor{TPixel}.cs} | 4 ++-- src/ImageSharp/Properties/AssemblyInfo.cs | 10 +++++++++- .../ImageSharp.Tests/Processing/Transforms/PadTest.cs | 2 +- 11 files changed, 23 insertions(+), 15 deletions(-) rename src/ImageSharp/Processing/Processors/Filters/{FilterProcessorImplementation.cs => FilterProcessor{TPixel}.cs} (92%) rename src/ImageSharp/Processing/Processors/Filters/{LomographProcessorImplementation.cs => LomographProcessor{TPixel}.cs} (83%) rename src/ImageSharp/Processing/Processors/Filters/{PolaroidProcessorImplementation.cs => PolaroidProcessor{TPixel}.cs} (86%) rename src/ImageSharp/Processing/Processors/Transforms/Resize/{ResizeProcessorImplementation.cs => ResizeProcessor{TPixel}.cs} (97%) diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs index 886b4e0962..b615b7bc60 100644 --- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs @@ -26,7 +26,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters public virtual IImageProcessor CreatePixelSpecificProcessor() where TPixel : struct, IPixel { - return new FilterProcessorImplementation(this); + return new FilterProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessorImplementation.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs similarity index 92% rename from src/ImageSharp/Processing/Processors/Filters/FilterProcessorImplementation.cs rename to src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs index 8fea81564c..ab60a42793 100644 --- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessorImplementation.cs +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor{TPixel}.cs @@ -15,12 +15,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// Provides methods that accept a matrix to apply free-form filters to images. /// /// The pixel format. - internal class FilterProcessorImplementation : ImageProcessor + internal class FilterProcessor : ImageProcessor where TPixel : struct, IPixel { private readonly FilterProcessor definition; - public FilterProcessorImplementation(FilterProcessor definition) + public FilterProcessor(FilterProcessor definition) { this.definition = definition; } diff --git a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs index 038a2885bc..7bbd915564 100644 --- a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs @@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters internal void ApplyToFrame(ImageFrame frame, Rectangle sourceRectangle, Configuration configuration) where TPixel : struct, IPixel { - var processorImpl = new FilterProcessorImplementation(new GrayscaleBt709Processor(1F)); + var processorImpl = new FilterProcessor(new GrayscaleBt709Processor(1F)); processorImpl.Apply(frame, sourceRectangle, configuration); } } diff --git a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs index 345d829ac6..b102f4daf7 100644 --- a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs @@ -17,6 +17,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters } public override IImageProcessor CreatePixelSpecificProcessor() => - new LomographProcessorImplementation(this); + new LomographProcessor(this); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/LomographProcessorImplementation.cs b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs similarity index 83% rename from src/ImageSharp/Processing/Processors/Filters/LomographProcessorImplementation.cs rename to src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs index a922e71256..8ec4ec258a 100644 --- a/src/ImageSharp/Processing/Processors/Filters/LomographProcessorImplementation.cs +++ b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs @@ -10,12 +10,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating an old Lomograph effect. /// - internal class LomographProcessorImplementation : FilterProcessorImplementation + internal class LomographProcessor : FilterProcessor where TPixel : struct, IPixel { private static readonly TPixel VeryDarkGreen = ColorBuilder.FromRGBA(0, 10, 0, 255); - public LomographProcessorImplementation(LomographProcessor definition) + public LomographProcessor(LomographProcessor definition) : base(definition) { } diff --git a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs index 0b272c3396..341c02acfc 100644 --- a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs @@ -18,6 +18,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// public override IImageProcessor CreatePixelSpecificProcessor() => - new PolaroidProcessorImplementation(this); + new PolaroidProcessor(this); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessorImplementation.cs b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs similarity index 86% rename from src/ImageSharp/Processing/Processors/Filters/PolaroidProcessorImplementation.cs rename to src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs index ec3c5ed26b..a73aa28de3 100644 --- a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessorImplementation.cs +++ b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs @@ -10,13 +10,13 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating an old Polaroid effect. /// - internal class PolaroidProcessorImplementation : FilterProcessorImplementation + internal class PolaroidProcessor : FilterProcessor where TPixel : struct, IPixel { private static readonly TPixel VeryDarkOrange = ColorBuilder.FromRGB(102, 34, 0); private static readonly TPixel LightOrange = ColorBuilder.FromRGBA(255, 153, 102, 128); - public PolaroidProcessorImplementation(FilterProcessor definition) + public PolaroidProcessor(FilterProcessor definition) : base(definition) { } diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index 021451b5c7..5710c4bd8c 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -133,7 +133,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public IImageProcessor CreatePixelSpecificProcessor() where TPixel : struct, IPixel { - return new ResizeProcessorImplementation(this); + return new ResizeProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessorImplementation.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs similarity index 97% rename from src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessorImplementation.cs rename to src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs index 36a054c54e..cf99f28dd7 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessorImplementation.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor{TPixel}.cs @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// The original code has been adapted from . /// /// The pixel format. - internal class ResizeProcessorImplementation : TransformProcessorBase + internal class ResizeProcessor : TransformProcessorBase where TPixel : struct, IPixel { // The following fields are not immutable but are optionally created on demand. @@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private readonly ResizeProcessor parameterSource; - public ResizeProcessorImplementation(ResizeProcessor parameterSource) + public ResizeProcessor(ResizeProcessor parameterSource) { this.parameterSource = parameterSource; } diff --git a/src/ImageSharp/Properties/AssemblyInfo.cs b/src/ImageSharp/Properties/AssemblyInfo.cs index 7b8f933b0c..f4459887f0 100644 --- a/src/ImageSharp/Properties/AssemblyInfo.cs +++ b/src/ImageSharp/Properties/AssemblyInfo.cs @@ -1,6 +1,14 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. + using System.Runtime.CompilerServices; // Ensure the other projects can see the internal helpers -[assembly: InternalsVisibleTo("SixLabors.ImageSharp.Drawing")] \ No newline at end of file +[assembly: InternalsVisibleTo("SixLabors.ImageSharp.Drawing")] + +// Redundant suppressing of SA1413 for Rider. +[assembly: + System.Diagnostics.CodeAnalysis.SuppressMessage( + "StyleCop.CSharp.MaintainabilityRules", + "SA1413:UseTrailingCommasInMultiLineInitializers", + Justification = "Follows SixLabors.ruleset")] \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs index eca1a458b1..82d7682558 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs @@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms IResampler sampler = KnownResamplers.NearestNeighbor; this.operations.Pad(width, height); - ResizeProcessorImplementation resizeProcessor = this.Verify>(); + ResizeProcessor resizeProcessor = this.Verify>(); Assert.Equal(width, resizeProcessor.Width); Assert.Equal(height, resizeProcessor.Height); From 8b0416f13817012696211bf309b8c778e85d7867 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 17:02:46 +0200 Subject: [PATCH 13/31] validating tests for Convolution processors --- src/ImageSharp/Common/Helpers/Vector4Utils.cs | 2 +- .../Convolution/EdgeDetector2DProcessor.cs | 2 +- .../Basic1ParameterConvolutionTests.cs | 56 +++++++++++++++++++ .../Processors/Convolution/BoxBlurTest.cs | 44 ++------------- .../Processors/Convolution/DetectEdgesTest.cs | 19 ++++--- .../Convolution/GaussianBlurTest.cs | 35 ++---------- .../Convolution/GaussianSharpenTest.cs | 40 ++----------- tests/Images/External | 2 +- 8 files changed, 85 insertions(+), 115 deletions(-) create mode 100644 tests/ImageSharp.Tests/Processing/Processors/Convolution/Basic1ParameterConvolutionTests.cs diff --git a/src/ImageSharp/Common/Helpers/Vector4Utils.cs b/src/ImageSharp/Common/Helpers/Vector4Utils.cs index 5c122217d1..a4e0921d0a 100644 --- a/src/ImageSharp/Common/Helpers/Vector4Utils.cs +++ b/src/ImageSharp/Common/Helpers/Vector4Utils.cs @@ -92,7 +92,7 @@ namespace SixLabors.ImageSharp } /// - /// Bulk variant of + /// Bulk variant of . /// /// The span of vectors /// The transformation matrix. diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs index c08a176618..b4ac89139a 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs @@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public DenseMatrix KernelY { get; } /// - public bool Grayscale { get; set; } + public bool Grayscale { get; } /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/Basic1ParameterConvolutionTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/Basic1ParameterConvolutionTests.cs new file mode 100644 index 0000000000..1f939a281b --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/Basic1ParameterConvolutionTests.cs @@ -0,0 +1,56 @@ +// // Copyright (c) Six Labors and contributors. +// // Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; +using SixLabors.Primitives; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution +{ + [GroupOutput("Convolution")] + public abstract class Basic1ParameterConvolutionTests + { + private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.05F); + + public static readonly TheoryData Values = new TheoryData { 3, 5 }; + + public static readonly string[] InputImages = + { + TestImages.Bmp.Car, + TestImages.Png.CalliphoraPartial + }; + + [Theory] + [WithFileCollection(nameof(InputImages), nameof(Values), PixelTypes.Rgba32)] + public void OnFullImage(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + provider.Utility.TestGroupName = this.GetType().Name; + provider.RunValidatingProcessorTest( + x => this.Apply(x, value), + value, + ValidatorComparer); + } + + [Theory] + [WithFileCollection(nameof(InputImages), nameof(Values), PixelTypes.Rgba32)] + public void InBox(TestImageProvider provider, int value) + where TPixel : struct, IPixel + { + provider.Utility.TestGroupName = this.GetType().Name; + provider.RunRectangleConstrainedValidatingProcessorTest( + (x, rect) => this.Apply(x, value, rect), + value, + ValidatorComparer); + } + + protected abstract void Apply(IImageProcessingContext ctx, int value) + where TPixel : struct, IPixel; + + protected abstract void Apply(IImageProcessingContext ctx, int value, Rectangle bounds) + where TPixel : struct, IPixel; + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs index 1f0d12cf7f..923f9d6161 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/BoxBlurTest.cs @@ -1,51 +1,17 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing; -using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; - using SixLabors.Primitives; -using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution { - public class BoxBlurTest : FileTestBase + [GroupOutput("Convolution")] + public class BoxBlurTest : Basic1ParameterConvolutionTests { - public static readonly TheoryData BoxBlurValues - = new TheoryData - { - 3, - 5 - }; - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(BoxBlurValues), DefaultPixelType)] - public void ImageShouldApplyBoxBlurFilter(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Mutate(x => x.BoxBlur(value)); - image.DebugSave(provider, value); - } - } - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(BoxBlurValues), DefaultPixelType)] - public void ImageShouldApplyBoxBlurFilterInBox(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image source = provider.GetImage()) - using (Image image = source.Clone()) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Mutate(x => x.BoxBlur(value, bounds)); - image.DebugSave(provider, value); + protected override void Apply(IImageProcessingContext ctx, int value) => ctx.BoxBlur(value); - ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); - } - } + protected override void Apply(IImageProcessingContext ctx, int value, Rectangle bounds) => + ctx.BoxBlur(value, bounds); } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs index b6a7741b3c..05524b20b0 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/DetectEdgesTest.cs @@ -10,13 +10,16 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution { - public class DetectEdgesTest : FileTestBase + [GroupOutput("Convolution")] + public class DetectEdgesTest { // I think our comparison is not accurate enough (nor can be) for RgbaVector. // The image pixels are identical according to BeyondCompare. private static readonly ImageComparer ValidatorComparer = ImageComparer.TolerantPercentage(0.0456F); - public static readonly string[] CommonTestImages = { TestImages.Png.Bike }; + public static readonly string[] TestImages = { Tests.TestImages.Png.Bike }; + + public const PixelTypes CommonNonDefaultPixelTypes = PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.RgbaVector; public static readonly TheoryData DetectEdgesFilters = new TheoryData { @@ -33,7 +36,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution }; [Theory] - [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] + [WithFileCollection(nameof(TestImages), PixelTypes.Rgba32)] public void DetectEdges_WorksOnWrappedMemoryImage(TestImageProvider provider) where TPixel : struct, IPixel { @@ -49,8 +52,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution } [Theory] - [WithTestPatternImages(nameof(DetectEdgesFilters), 100, 100, DefaultPixelType)] - [WithFileCollection(nameof(CommonTestImages), nameof(DetectEdgesFilters), DefaultPixelType)] + [WithTestPatternImages(nameof(DetectEdgesFilters), 100, 100, PixelTypes.Rgba32)] + [WithFileCollection(nameof(TestImages), nameof(DetectEdgesFilters), PixelTypes.Rgba32)] public void DetectEdges_WorksWithAllFilters(TestImageProvider provider, EdgeDetectionOperators detector) where TPixel : struct, IPixel { @@ -63,7 +66,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution } [Theory] - [WithFileCollection(nameof(CommonTestImages), CommonNonDefaultPixelTypes)] + [WithFileCollection(nameof(TestImages), CommonNonDefaultPixelTypes)] public void DetectEdges_IsNotBoundToSinglePixelType(TestImageProvider provider) where TPixel : struct, IPixel { @@ -76,7 +79,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution } [Theory] - [WithFile(TestImages.Gif.Giphy, DefaultPixelType)] + [WithFile(Tests.TestImages.Gif.Giphy, PixelTypes.Rgba32)] public void DetectEdges_IsAppliedToAllFrames(TestImageProvider provider) where TPixel : struct, IPixel { @@ -88,7 +91,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution } [Theory] - [WithFileCollection(nameof(CommonTestImages), DefaultPixelType)] + [WithFileCollection(nameof(TestImages), PixelTypes.Rgba32)] public void DetectEdges_InBox(TestImageProvider provider) where TPixel : struct, IPixel { diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs index 6bd3b34bb1..6307a1c51d 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianBlurTest.cs @@ -10,37 +10,12 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution { - public class GaussianBlurTest : FileTestBase + [GroupOutput("Convolution")] + public class GaussianBlurTest : Basic1ParameterConvolutionTests { - public static readonly TheoryData GaussianBlurValues = new TheoryData { 3, 5 }; + protected override void Apply(IImageProcessingContext ctx, int value) => ctx.GaussianBlur(value); - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(GaussianBlurValues), DefaultPixelType)] - public void ImageShouldApplyGaussianBlurFilter(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Mutate(x => x.GaussianBlur(value)); - image.DebugSave(provider, value); - } - } - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(GaussianBlurValues), DefaultPixelType)] - public void ImageShouldApplyGaussianBlurFilterInBox(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image source = provider.GetImage()) - using (Image image = source.Clone()) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Mutate(x => x.GaussianBlur(value, bounds)); - image.DebugSave(provider, value); - - ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); - } - } + protected override void Apply(IImageProcessingContext ctx, int value, Rectangle bounds) => + ctx.GaussianBlur(value, bounds); } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs index 8eb1f85eb2..29a1643b0c 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Convolution/GaussianSharpenTest.cs @@ -9,42 +9,12 @@ using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Convolution { - public class GaussianSharpenTest : FileTestBase + [GroupOutput("Convolution")] + public class GaussianSharpenTest : Basic1ParameterConvolutionTests { - public static readonly TheoryData GaussianSharpenValues - = new TheoryData - { - 3, - 5 - }; + protected override void Apply(IImageProcessingContext ctx, int value) => ctx.GaussianSharpen(value); - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(GaussianSharpenValues), DefaultPixelType)] - public void ImageShouldApplyGaussianSharpenFilter(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image image = provider.GetImage()) - { - image.Mutate(x => x.GaussianSharpen(value)); - image.DebugSave(provider, value); - } - } - - [Theory] - [WithFileCollection(nameof(DefaultFiles), nameof(GaussianSharpenValues), DefaultPixelType)] - public void ImageShouldApplyGaussianSharpenFilterInBox(TestImageProvider provider, int value) - where TPixel : struct, IPixel - { - using (Image source = provider.GetImage()) - using (var image = source.Clone()) - { - var bounds = new Rectangle(10, 10, image.Width / 2, image.Height / 2); - - image.Mutate(x => x.GaussianSharpen(value, bounds)); - image.DebugSave(provider, value); - - ImageComparer.Tolerant().VerifySimilarityIgnoreRegion(source, image, bounds); - } - } + protected override void Apply(IImageProcessingContext ctx, int value, Rectangle bounds) => + ctx.GaussianSharpen(value, bounds); } } \ No newline at end of file diff --git a/tests/Images/External b/tests/Images/External index 1ca5154996..2a3247c6da 160000 --- a/tests/Images/External +++ b/tests/Images/External @@ -1 +1 @@ -Subproject commit 1ca515499663e8b0b7c924a49b8d212f7447bdb0 +Subproject commit 2a3247c6da18b8a96cc71e1f2862ac03f2a42315 From 604a8008fb3ab9d0fe173dc20d61471a69d8474a Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 23:16:11 +0200 Subject: [PATCH 14/31] refactor BoxBlur and GaussianBlur --- .../Processing/BoxBlurExtensions.cs | 20 ++--- .../Processing/GaussianBlurExtensions.cs | 20 ++--- .../Convolution/BoxBlurProcessor.cs | 53 ++++-------- .../Convolution/BoxBlurProcessor{TPixel}.cs | 64 +++++++++++++++ .../Convolution/GaussianBlurProcessor.cs | 82 +++++++------------ .../GaussianBlurProcessor{TPixel}.cs | 79 ++++++++++++++++++ .../Processing/Convolution/BoxBlurTest.cs | 6 +- .../Convolution/GaussianBlurTest.cs | 6 +- 8 files changed, 210 insertions(+), 120 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs diff --git a/src/ImageSharp/Processing/BoxBlurExtensions.cs b/src/ImageSharp/Processing/BoxBlurExtensions.cs index 624da239bb..bc6e06dd67 100644 --- a/src/ImageSharp/Processing/BoxBlurExtensions.cs +++ b/src/ImageSharp/Processing/BoxBlurExtensions.cs @@ -8,43 +8,37 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds box blurring extensions to the type. + /// Adds box blurring extensions to the type. /// public static class BoxBlurExtensions { /// /// Applies a box blur to the image. /// - /// The pixel format. /// The image this method extends. /// The . - public static IImageProcessingContext BoxBlur(this IImageProcessingContext source) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BoxBlurProcessor()); + public static IImageProcessingContext BoxBlur(this IImageProcessingContext source) + => source.ApplyProcessor(new BoxBlurProcessor()); /// /// Applies a box blur to the image. /// - /// The pixel format. /// The image this method extends. /// The 'radius' value representing the size of the area to sample. /// The . - public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BoxBlurProcessor(radius)); + public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius) + => source.ApplyProcessor(new BoxBlurProcessor(radius)); /// /// Applies a box blur to the image. /// - /// The pixel format. /// The image this method extends. /// The 'radius' value representing the size of the area to sample. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new BoxBlurProcessor(radius), rectangle); + public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius, Rectangle rectangle) + => source.ApplyProcessor(new BoxBlurProcessor(radius), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/GaussianBlurExtensions.cs b/src/ImageSharp/Processing/GaussianBlurExtensions.cs index 165c4ce1a6..1dab054579 100644 --- a/src/ImageSharp/Processing/GaussianBlurExtensions.cs +++ b/src/ImageSharp/Processing/GaussianBlurExtensions.cs @@ -8,43 +8,37 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds Gaussian blurring extensions to the type. + /// Adds Gaussian blurring extensions to the type. /// public static class GaussianBlurExtensions { /// /// Applies a Gaussian blur to the image. /// - /// The pixel format. /// The image this method extends. /// The . - public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source) - where TPixel : struct, IPixel - => source.ApplyProcessor(new GaussianBlurProcessor()); + public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source) + => source.ApplyProcessor(new GaussianBlurProcessor()); /// /// Applies a Gaussian blur to the image. /// - /// The pixel format. /// The image this method extends. /// The 'sigma' value representing the weight of the blur. /// The . - public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma) - where TPixel : struct, IPixel - => source.ApplyProcessor(new GaussianBlurProcessor(sigma)); + public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma) + => source.ApplyProcessor(new GaussianBlurProcessor(sigma)); /// /// Applies a Gaussian blur to the image. /// - /// The pixel format. /// The image this method extends. /// The 'sigma' value representing the weight of the blur. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new GaussianBlurProcessor(sigma), rectangle); + public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma, Rectangle rectangle) + => source.ApplyProcessor(new GaussianBlurProcessor(sigma), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs index 3d5bdc42a7..bb37489e13 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs @@ -3,67 +3,48 @@ using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// Applies box blur processing to the image. + /// Defines a box blur processor of a given Radius. /// - /// The pixel format. - internal class BoxBlurProcessor : ImageProcessor - where TPixel : struct, IPixel + public class BoxBlurProcessor : IImageProcessor { /// - /// The maximum size of the kernel in either direction. + /// The default radius used by the parameterless constructor. /// - private readonly int kernelSize; + public const int DefaultRadius = 7; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The 'radius' value representing the size of the area to sample. /// - public BoxBlurProcessor(int radius = 7) + public BoxBlurProcessor(int radius) { this.Radius = radius; - this.kernelSize = (radius * 2) + 1; - this.KernelX = this.CreateBoxKernel(); - this.KernelY = this.KernelX.Transpose(); } /// - /// Gets the Radius + /// Initializes a new instance of the class. /// - public int Radius { get; } - - /// - /// Gets the horizontal gradient operator. - /// - public DenseMatrix KernelX { get; } + public BoxBlurProcessor() + : this(DefaultRadius) + { + } /// - /// Gets the vertical gradient operator. + /// Gets the Radius. /// - public DenseMatrix KernelY { get; } - - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - => new Convolution2PassProcessor(this.KernelX, this.KernelY, false).Apply(source, sourceRectangle, configuration); + public int Radius { get; } - /// - /// Create a 1 dimensional Box kernel. - /// - /// The - private DenseMatrix CreateBoxKernel() + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - int size = this.kernelSize; - var kernel = new DenseMatrix(size, 1); - - kernel.Fill(1F / size); - - return kernel; + return new BoxBlurProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs new file mode 100644 index 0000000000..ba092cb424 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs @@ -0,0 +1,64 @@ +// // Copyright (c) Six Labors and contributors. +// // Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Convolution +{ + /// + /// Applies box blur processing to the image. + /// + /// The pixel format. + internal class BoxBlurProcessor : ImageProcessor + where TPixel : struct, IPixel + { + private readonly BoxBlurProcessor definition; + + /// + /// Initializes a new instance of the class. + /// + /// The defining the processor parameters. + public BoxBlurProcessor(BoxBlurProcessor definition) + { + this.definition = definition; + int kernelSize = (definition.Radius * 2) + 1; + this.KernelX = CreateBoxKernel(kernelSize); + this.KernelY = this.KernelX.Transpose(); + } + + /// + /// Gets the horizontal gradient operator. + /// + public DenseMatrix KernelX { get; } + + /// + /// Gets the vertical gradient operator. + /// + public DenseMatrix KernelY { get; } + + + /// + protected override void OnFrameApply( + ImageFrame source, + Rectangle sourceRectangle, + Configuration configuration) => + new Convolution2PassProcessor(this.KernelX, this.KernelY, false).Apply( + source, + sourceRectangle, + configuration); + + /// + /// Create a 1 dimensional Box kernel. + /// + /// The maximum size of the kernel in either direction. + /// The . + private static DenseMatrix CreateBoxKernel(int kernelSize) + { + var kernel = new DenseMatrix(kernelSize, 1); + kernel.Fill(1F / kernelSize); + return kernel; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs index 0fc822d57c..33e2ef955d 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs @@ -4,35 +4,38 @@ using System; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// Applies Gaussian blur processing to the image. + /// Defines a gaussian blur processor with a (Sigma, Radius) pair. /// - /// The pixel format. - internal class GaussianBlurProcessor : ImageProcessor - where TPixel : struct, IPixel + public class GaussianBlurProcessor : IImageProcessor { /// - /// The maximum size of the kernel in either direction. + /// The default value for . /// - private readonly int kernelSize; + public const float DefaultSigma = 3f; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. + /// + public GaussianBlurProcessor() + : this(DefaultSigma, CalculateDefaultRadius(DefaultSigma)) + { + } + + /// + /// Initializes a new instance of the class. /// /// The 'sigma' value representing the weight of the blur. - public GaussianBlurProcessor(float sigma = 3F) - : this(sigma, (int)MathF.Ceiling(sigma * 3)) + public GaussianBlurProcessor(float sigma) + : this(sigma, CalculateDefaultRadius(sigma)) { - // Kernel radius is calculated using the minimum viable value. - // http://chemaguerra.com/gaussian-filter-radius/ } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The 'radius' value representing the size of the area to sample. @@ -43,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The 'sigma' value representing the weight of the blur. @@ -54,10 +57,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// public GaussianBlurProcessor(float sigma, int radius) { - this.kernelSize = (radius * 2) + 1; this.Sigma = sigma; - this.KernelX = this.CreateGaussianKernel(); - this.KernelY = this.KernelX.Transpose(); + this.Radius = radius; } /// @@ -66,47 +67,24 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public float Sigma { get; } /// - /// Gets the horizontal gradient operator. - /// - public DenseMatrix KernelX { get; } - - /// - /// Gets the vertical gradient operator. + /// Gets the radius defining the size of the area to sample. /// - public DenseMatrix KernelY { get; } + public int Radius { get; } - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - => new Convolution2PassProcessor(this.KernelX, this.KernelY, false).Apply(source, sourceRectangle, configuration); + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel + { + return new GaussianBlurProcessor(this); + } /// - /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function + /// Kernel radius is calculated using the minimum viable value. + /// . /// - /// The - private DenseMatrix CreateGaussianKernel() + private static int CalculateDefaultRadius(float sigma) { - int size = this.kernelSize; - float weight = this.Sigma; - var kernel = new DenseMatrix(size, 1); - - float sum = 0F; - float midpoint = (size - 1) / 2F; - - for (int i = 0; i < size; i++) - { - float x = i - midpoint; - float gx = ImageMaths.Gaussian(x, weight); - sum += gx; - kernel[0, i] = gx; - } - - // Normalize kernel so that the sum of all weights equals 1 - for (int i = 0; i < size; i++) - { - kernel[0, i] /= sum; - } - - return kernel; + return (int)MathF.Ceiling(sigma * 3); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs new file mode 100644 index 0000000000..9b60fb2d2e --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs @@ -0,0 +1,79 @@ +// // Copyright (c) Six Labors and contributors. +// // Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Convolution +{ + /// + /// Applies Gaussian blur processing to an image. + /// + /// The pixel format. + internal class GaussianBlurProcessor : ImageProcessor + where TPixel : struct, IPixel + { + private readonly GaussianBlurProcessor definition; + + /// + /// Initializes a new instance of the class. + /// + /// The defining the processor parameters. + public GaussianBlurProcessor(GaussianBlurProcessor definition) + { + this.definition = definition; + int kernelSize = (definition.Radius * 2) + 1; + this.KernelX = CreateGaussianKernel(kernelSize, definition.Sigma); + this.KernelY = this.KernelX.Transpose(); + } + + /// + /// Gets the horizontal gradient operator. + /// + public DenseMatrix KernelX { get; } + + /// + /// Gets the vertical gradient operator. + /// + public DenseMatrix KernelY { get; } + + /// + protected override void OnFrameApply( + ImageFrame source, + Rectangle sourceRectangle, + Configuration configuration) => + new Convolution2PassProcessor(this.KernelX, this.KernelY, false).Apply( + source, + sourceRectangle, + configuration); + + /// + /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function + /// + /// The + private static DenseMatrix CreateGaussianKernel(int size, float weight) + { + var kernel = new DenseMatrix(size, 1); + + float sum = 0F; + float midpoint = (size - 1) / 2F; + + for (int i = 0; i < size; i++) + { + float x = i - midpoint; + float gx = ImageMaths.Gaussian(x, weight); + sum += gx; + kernel[0, i] = gx; + } + + // Normalize kernel so that the sum of all weights equals 1 + for (int i = 0; i < size; i++) + { + kernel[0, i] /= sum; + } + + return kernel; + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs index e425b63151..8c659848fc 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/BoxBlurTest.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution public void BoxBlur_BoxBlurProcessorDefaultsSet() { this.operations.BoxBlur(); - var processor = this.Verify>(); + var processor = this.Verify(); Assert.Equal(7, processor.Radius); } @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution public void BoxBlur_amount_BoxBlurProcessorDefaultsSet() { this.operations.BoxBlur(34); - var processor = this.Verify>(); + var processor = this.Verify(); Assert.Equal(34, processor.Radius); } @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution public void BoxBlur_amount_rect_BoxBlurProcessorDefaultsSet() { this.operations.BoxBlur(5, this.rect); - var processor = this.Verify>(this.rect); + var processor = this.Verify(this.rect); Assert.Equal(5, processor.Radius); } diff --git a/tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs index c87a834eb6..0f64ebbeb4 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/GaussianBlurTest.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution public void GaussianBlur_GaussianBlurProcessorDefaultsSet() { this.operations.GaussianBlur(); - var processor = this.Verify>(); + var processor = this.Verify(); Assert.Equal(3f, processor.Sigma); } @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution public void GaussianBlur_amount_GaussianBlurProcessorDefaultsSet() { this.operations.GaussianBlur(0.2f); - var processor = this.Verify>(); + var processor = this.Verify(); Assert.Equal(.2f, processor.Sigma); } @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution public void GaussianBlur_amount_rect_GaussianBlurProcessorDefaultsSet() { this.operations.GaussianBlur(0.6f, this.rect); - var processor = this.Verify>(this.rect); + var processor = this.Verify(this.rect); Assert.Equal(.6f, processor.Sigma); } From 5fc4374fd9d0645fb7b808b2858321c3c6ce51d8 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 23:27:28 +0200 Subject: [PATCH 15/31] making filters public --- .../Filters/AchromatomalyProcessor.cs | 2 +- .../Filters/AchromatopsiaProcessor.cs | 2 +- .../Processors/Filters/BlackWhiteProcessor.cs | 4 +- .../Processors/Filters/BrightnessProcessor.cs | 2 +- .../Processors/Filters/ContrastProcessor.cs | 4 +- .../Filters/DeuteranomalyProcessor.cs | 2 +- .../Filters/DeuteranopiaProcessor.cs | 2 +- .../Processors/Filters/FilterProcessor.cs | 4 +- .../Filters/GrayscaleBt601Processor.cs | 2 +- .../Filters/GrayscaleBt709Processor.cs | 2 +- .../Processors/Filters/HueProcessor.cs | 2 +- .../Processors/Filters/InvertProcessor.cs | 2 +- .../Processors/Filters/KodachromeProcessor.cs | 2 +- .../Processors/Filters/LomographProcessor.cs | 3 +- .../Filters/LomographProcessor{TPixel}.cs | 4 ++ .../Processors/Filters/OpacityProcessor.cs | 4 +- .../Filters/PolaroidProcessor{TPixel}.cs | 6 +- .../Filters/ProtanomalyProcessor.cs | 2 +- .../Processors/Filters/ProtanopiaProcessor.cs | 2 +- .../Processors/Filters/SaturateProcessor.cs | 2 +- .../Processors/Filters/SepiaProcessor.cs | 2 +- .../Filters/TritanomalyProcessor.cs | 2 +- .../Processors/Filters/TritanopiaProcessor.cs | 2 +- .../Processing/Processors/IImageProcessor.cs | 56 +------------------ .../Processors/IImageProcessor{TPixel}.cs | 31 ++++++++++ .../Processors/ImageProcessorExtensions.cs | 37 ++++++++++++ 26 files changed, 106 insertions(+), 79 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs diff --git a/src/ImageSharp/Processing/Processors/Filters/AchromatomalyProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/AchromatomalyProcessor.cs index 2dac6b827d..7c3789c388 100644 --- a/src/ImageSharp/Processing/Processors/Filters/AchromatomalyProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/AchromatomalyProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating Achromatomaly (Color desensitivity) color blindness. /// - internal class AchromatomalyProcessor : FilterProcessor + public class AchromatomalyProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/AchromatopsiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/AchromatopsiaProcessor.cs index 4df94a7603..ab04a30ba1 100644 --- a/src/ImageSharp/Processing/Processors/Filters/AchromatopsiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/AchromatopsiaProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating Achromatopsia (Monochrome) color blindness. /// - internal class AchromatopsiaProcessor : FilterProcessor + public class AchromatopsiaProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs index 698f11cbf0..7e2e7f2f47 100644 --- a/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs @@ -6,9 +6,9 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// - /// Applies a black and white filter matrix to the image + /// Applies a black and white filter matrix to the image. /// - internal class BlackWhiteProcessor : FilterProcessor + public class BlackWhiteProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs index 6a1f3869ae..94a7a394a6 100644 --- a/src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a brightness filter matrix using the given amount. /// - internal class BrightnessProcessor : FilterProcessor + public class BrightnessProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs index 3bea695b99..1f74a1d642 100644 --- a/src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a contrast filter matrix using the given amount. /// - internal class ContrastProcessor : FilterProcessor + public class ContrastProcessor : FilterProcessor { /// /// Initializes a new instance of the class. @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters } /// - /// Gets the proportion of the conversion + /// Gets the proportion of the conversion. /// public float Amount { get; } } diff --git a/src/ImageSharp/Processing/Processors/Filters/DeuteranomalyProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/DeuteranomalyProcessor.cs index 395489d563..7ad0630aac 100644 --- a/src/ImageSharp/Processing/Processors/Filters/DeuteranomalyProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/DeuteranomalyProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating Deuteranomaly (Green-Weak) color blindness. /// - internal class DeuteranomalyProcessor : FilterProcessor + public class DeuteranomalyProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/DeuteranopiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/DeuteranopiaProcessor.cs index 51f8361e6c..188d02dc8b 100644 --- a/src/ImageSharp/Processing/Processors/Filters/DeuteranopiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/DeuteranopiaProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating Deuteranopia (Green-Blind) color blindness. /// - internal class DeuteranopiaProcessor : FilterProcessor + public class DeuteranopiaProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs index b615b7bc60..36c43d8446 100644 --- a/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/FilterProcessor.cs @@ -1,4 +1,4 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; @@ -7,7 +7,7 @@ using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// - /// Provides methods that accept a matrix to apply free-form filters to images. + /// Defines a free-form color filter by a . /// public class FilterProcessor : IImageProcessor { diff --git a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs index b39e01b9a8..c24992b441 100644 --- a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a grayscale filter matrix using the given amount and the formula as specified by ITU-R Recommendation BT.601 /// - internal class GrayscaleBt601Processor : FilterProcessor + public class GrayscaleBt601Processor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs index 7bbd915564..dc5294ccf0 100644 --- a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a grayscale filter matrix using the given amount and the formula as specified by ITU-R Recommendation BT.709 /// - internal class GrayscaleBt709Processor : FilterProcessor + public class GrayscaleBt709Processor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs index a69fd50757..65f7eef330 100644 --- a/src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a hue filter matrix using the given angle of rotation in degrees /// - internal class HueProcessor : FilterProcessor + public class HueProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs index d7631970db..cc41e65721 100644 --- a/src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a filter matrix that inverts the colors of an image /// - internal class InvertProcessor : FilterProcessor + public class InvertProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs index 5570f23d7d..a5215a434c 100644 --- a/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a filter matrix recreating an old Kodachrome camera effect matrix to the image /// - internal class KodachromeProcessor : FilterProcessor + public class KodachromeProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs index b102f4daf7..dae168e2cb 100644 --- a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating an old Lomograph effect. /// - internal class LomographProcessor : FilterProcessor + public class LomographProcessor : FilterProcessor { /// /// Initializes a new instance of the class. @@ -16,6 +16,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters { } + /// public override IImageProcessor CreatePixelSpecificProcessor() => new LomographProcessor(this); } diff --git a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs index 8ec4ec258a..c15cf6fc90 100644 --- a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor{TPixel}.cs @@ -15,6 +15,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters { private static readonly TPixel VeryDarkGreen = ColorBuilder.FromRGBA(0, 10, 0, 255); + /// + /// Initializes a new instance of the class. + /// + /// The defining the parameters. public LomographProcessor(LomographProcessor definition) : base(definition) { diff --git a/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs index e790753f3b..0ee5dd59e5 100644 --- a/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies an opacity filter matrix using the given amount. /// - internal class OpacityProcessor : FilterProcessor + public class OpacityProcessor : FilterProcessor { /// /// Initializes a new instance of the class. @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters } /// - /// Gets the proportion of the conversion + /// Gets the proportion of the conversion. /// public float Amount { get; } } diff --git a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs index a73aa28de3..befcfc1f42 100644 --- a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor{TPixel}.cs @@ -16,7 +16,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters private static readonly TPixel VeryDarkOrange = ColorBuilder.FromRGB(102, 34, 0); private static readonly TPixel LightOrange = ColorBuilder.FromRGBA(255, 153, 102, 128); - public PolaroidProcessor(FilterProcessor definition) + /// + /// Initializes a new instance of the class. + /// + /// The defining the parameters. + public PolaroidProcessor(PolaroidProcessor definition) : base(definition) { } diff --git a/src/ImageSharp/Processing/Processors/Filters/ProtanomalyProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ProtanomalyProcessor.cs index 95e369b371..755bc0f81a 100644 --- a/src/ImageSharp/Processing/Processors/Filters/ProtanomalyProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/ProtanomalyProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating Protanomaly (Red-Weak) color blindness. /// - internal class ProtanomalyProcessor : FilterProcessor + public class ProtanomalyProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/ProtanopiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ProtanopiaProcessor.cs index bd92579130..c4f4a76611 100644 --- a/src/ImageSharp/Processing/Processors/Filters/ProtanopiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/ProtanopiaProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating Protanopia (Red-Blind) color blindness. /// - internal class ProtanopiaProcessor : FilterProcessor + public class ProtanopiaProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs index 424eeb461c..770fb98277 100644 --- a/src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a saturation filter matrix using the given amount. /// - internal class SaturateProcessor : FilterProcessor + public class SaturateProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs index 538db4b52c..9da48993a2 100644 --- a/src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a sepia filter matrix using the given amount. /// - internal class SepiaProcessor : FilterProcessor + public class SepiaProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/TritanomalyProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/TritanomalyProcessor.cs index 3eda05a115..c64475b994 100644 --- a/src/ImageSharp/Processing/Processors/Filters/TritanomalyProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/TritanomalyProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating Tritanomaly (Blue-Weak) color blindness. /// - internal class TritanomalyProcessor : FilterProcessor + public class TritanomalyProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/TritanopiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/TritanopiaProcessor.cs index f5ed26c833..722840e743 100644 --- a/src/ImageSharp/Processing/Processors/Filters/TritanopiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/TritanopiaProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating Tritanopia (Blue-Blind) color blindness. /// - internal class TritanopiaProcessor : FilterProcessor + public class TritanopiaProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/IImageProcessor.cs b/src/ImageSharp/Processing/Processors/IImageProcessor.cs index e9b2c9c0e3..9b030a6fea 100644 --- a/src/ImageSharp/Processing/Processors/IImageProcessor.cs +++ b/src/ImageSharp/Processing/Processors/IImageProcessor.cs @@ -2,12 +2,14 @@ // 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 an image. + /// Non-generic implementations are responsible for: + /// 1. Encapsulating the parameters of the algorithm. + /// 2. Creating the generic instance to execute the algorithm. /// public interface IImageProcessor { @@ -20,56 +22,4 @@ namespace SixLabors.ImageSharp.Processing.Processors IImageProcessor CreatePixelSpecificProcessor() where TPixel : struct, IPixel; } - - /// - /// Implements an algorithm to alter the pixels of an image. - /// - /// The pixel format. - public interface IImageProcessor - where TPixel : struct, IPixel - { - /// - /// Applies the process to the specified portion of the specified . - /// - /// The source image. Cannot be null. - /// - /// The structure that specifies the portion of the image object to draw. - /// - /// - /// is null. - /// - /// - /// doesn't fit the dimension of the image. - /// - void Apply(Image source, Rectangle sourceRectangle); - } - - internal static class ImageProcessorExtensions - { - public static void Apply(this IImageProcessor processor, Image source, Rectangle sourceRectangle) - { - var visitor = new ApplyVisitor(processor, sourceRectangle); - source.AcceptVisitor(visitor); - } - - private class ApplyVisitor : IImageVisitor - { - private readonly IImageProcessor processor; - - private readonly Rectangle sourceRectangle; - - public ApplyVisitor(IImageProcessor processor, Rectangle sourceRectangle) - { - this.processor = processor; - this.sourceRectangle = sourceRectangle; - } - - public void Visit(Image image) - where TPixel : struct, IPixel - { - var processorImpl = this.processor.CreatePixelSpecificProcessor(); - processorImpl.Apply(image, this.sourceRectangle); - } - } - } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs new file mode 100644 index 0000000000..f2c7ef29c1 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.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 +{ + /// + /// Implements an algorithm to alter the pixels of an image. + /// + /// The pixel format. + public interface IImageProcessor + where TPixel : struct, IPixel + { + /// + /// Applies the process to the specified portion of the specified . + /// + /// The source image. Cannot be null. + /// + /// The structure that specifies the portion of the image object to draw. + /// + /// + /// is null. + /// + /// + /// doesn't fit the dimension of the image. + /// + void Apply(Image source, Rectangle sourceRectangle); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs b/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs new file mode 100644 index 0000000000..1e189b9c78 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs @@ -0,0 +1,37 @@ +// // 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 +{ + internal static class ImageProcessorExtensions + { + public static void Apply(this IImageProcessor processor, Image source, Rectangle sourceRectangle) + { + var visitor = new ApplyVisitor(processor, sourceRectangle); + source.AcceptVisitor(visitor); + } + + private class ApplyVisitor : IImageVisitor + { + private readonly IImageProcessor processor; + + private readonly Rectangle sourceRectangle; + + public ApplyVisitor(IImageProcessor processor, Rectangle sourceRectangle) + { + this.processor = processor; + this.sourceRectangle = sourceRectangle; + } + + public void Visit(Image image) + where TPixel : struct, IPixel + { + var processorImpl = this.processor.CreatePixelSpecificProcessor(); + processorImpl.Apply(image, this.sourceRectangle); + } + } + } +} \ No newline at end of file From 432d35ccc322010a4d7f92131353fe1f673c37d8 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sat, 4 May 2019 23:53:00 +0200 Subject: [PATCH 16/31] Further refactor on Gaussian stuff --- .../Processing/GaussianSharpenExtensions.cs | 26 ++--- .../ConvolutionProcessorHelpers.cs | 93 ++++++++++++++++ .../Convolution/GaussianBlurProcessor.cs | 16 +-- .../GaussianBlurProcessor{TPixel}.cs | 37 +------ .../Convolution/GaussianSharpenProcessor.cs | 101 +++++------------- .../GaussianSharpenProcessor{TPixel}.cs | 42 ++++++++ .../Convolution/GaussianSharpenTest.cs | 6 +- 7 files changed, 180 insertions(+), 141 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs create mode 100644 src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs diff --git a/src/ImageSharp/Processing/GaussianSharpenExtensions.cs b/src/ImageSharp/Processing/GaussianSharpenExtensions.cs index 675bbc142d..64aa7b5db3 100644 --- a/src/ImageSharp/Processing/GaussianSharpenExtensions.cs +++ b/src/ImageSharp/Processing/GaussianSharpenExtensions.cs @@ -1,50 +1,46 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Convolution; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds Gaussian sharpening extensions to the type. + /// Adds Gaussian sharpening extensions to the type. /// public static class GaussianSharpenExtensions { /// /// Applies a Gaussian sharpening filter to the image. /// - /// The pixel format. /// The image this method extends. /// The . - public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source) - where TPixel : struct, IPixel - => source.ApplyProcessor(new GaussianSharpenProcessor()); + public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source) => + source.ApplyProcessor(new GaussianSharpenProcessor()); /// /// Applies a Gaussian sharpening filter to the image. /// - /// The pixel format. /// The image this method extends. /// The 'sigma' value representing the weight of the blur. /// The . - public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source, float sigma) - where TPixel : struct, IPixel - => source.ApplyProcessor(new GaussianSharpenProcessor(sigma)); + public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source, float sigma) => + source.ApplyProcessor(new GaussianSharpenProcessor(sigma)); /// /// Applies a Gaussian sharpening filter to the image. /// - /// The pixel format. /// The image this method extends. /// The 'sigma' value representing the weight of the blur. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source, float sigma, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new GaussianSharpenProcessor(sigma), rectangle); + public static IImageProcessingContext GaussianSharpen( + this IImageProcessingContext source, + float sigma, + Rectangle rectangle) => + source.ApplyProcessor(new GaussianSharpenProcessor(sigma), rectangle); } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs new file mode 100644 index 0000000000..bb0072b82e --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs @@ -0,0 +1,93 @@ +// // Copyright (c) Six Labors and contributors. +// // Licensed under the Apache License, Version 2.0. + +using System; + +using SixLabors.ImageSharp.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Convolution +{ + internal static class ConvolutionProcessorHelpers + { + /// + /// Kernel radius is calculated using the minimum viable value. + /// . + /// + internal static int GetDefaultGaussianRadius(float sigma) + { + return (int)MathF.Ceiling(sigma * 3); + } + + /// + /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function + /// + /// The . + internal static DenseMatrix CreateGaussianBlurKernel(int size, float weight) + { + var kernel = new DenseMatrix(size, 1); + + float sum = 0F; + float midpoint = (size - 1) / 2F; + + for (int i = 0; i < size; i++) + { + float x = i - midpoint; + float gx = ImageMaths.Gaussian(x, weight); + sum += gx; + kernel[0, i] = gx; + } + + // Normalize kernel so that the sum of all weights equals 1 + for (int i = 0; i < size; i++) + { + kernel[0, i] /= sum; + } + + return kernel; + } + + /// + /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function + /// + /// The . + internal static DenseMatrix CreateGaussianSharpenKernel(int size, float weight) + { + var kernel = new DenseMatrix(size, 1); + + float sum = 0; + + float midpoint = (size - 1) / 2F; + for (int i = 0; i < size; i++) + { + float x = i - midpoint; + float gx = ImageMaths.Gaussian(x, weight); + sum += gx; + kernel[0, i] = gx; + } + + // Invert the kernel for sharpening. + int midpointRounded = (int)midpoint; + for (int i = 0; i < size; i++) + { + if (i == midpointRounded) + { + // Calculate central value + kernel[0, i] = (2F * sum) - kernel[0, i]; + } + else + { + // invert value + kernel[0, i] = -kernel[0, i]; + } + } + + // Normalize kernel so that the sum of all weights equals 1 + for (int i = 0; i < size; i++) + { + kernel[0, i] /= sum; + } + + return kernel; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs index 33e2ef955d..c64a024f9f 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs @@ -1,14 +1,13 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// Defines a gaussian blur processor with a (Sigma, Radius) pair. + /// Defines Gaussian blur by a (Sigma, Radius) pair. /// public class GaussianBlurProcessor : IImageProcessor { @@ -21,7 +20,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Initializes a new instance of the class. /// public GaussianBlurProcessor() - : this(DefaultSigma, CalculateDefaultRadius(DefaultSigma)) + : this(DefaultSigma, ConvolutionProcessorHelpers.GetDefaultGaussianRadius(DefaultSigma)) { } @@ -30,7 +29,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// The 'sigma' value representing the weight of the blur. public GaussianBlurProcessor(float sigma) - : this(sigma, CalculateDefaultRadius(sigma)) + : this(sigma, ConvolutionProcessorHelpers.GetDefaultGaussianRadius(sigma)) { } @@ -77,14 +76,5 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { return new GaussianBlurProcessor(this); } - - /// - /// Kernel radius is calculated using the minimum viable value. - /// . - /// - private static int CalculateDefaultRadius(float sigma) - { - return (int)MathF.Ceiling(sigma * 3); - } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs index 9b60fb2d2e..a129ff5473 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor{TPixel}.cs @@ -1,5 +1,5 @@ -// // Copyright (c) Six Labors and contributors. -// // Licensed under the Apache License, Version 2.0. +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; @@ -14,17 +14,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution internal class GaussianBlurProcessor : ImageProcessor where TPixel : struct, IPixel { - private readonly GaussianBlurProcessor definition; - /// /// Initializes a new instance of the class. /// /// The defining the processor parameters. public GaussianBlurProcessor(GaussianBlurProcessor definition) { - this.definition = definition; int kernelSize = (definition.Radius * 2) + 1; - this.KernelX = CreateGaussianKernel(kernelSize, definition.Sigma); + this.KernelX = ConvolutionProcessorHelpers.CreateGaussianBlurKernel(kernelSize, definition.Sigma); this.KernelY = this.KernelX.Transpose(); } @@ -47,33 +44,5 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution source, sourceRectangle, configuration); - - /// - /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function - /// - /// The - private static DenseMatrix CreateGaussianKernel(int size, float weight) - { - var kernel = new DenseMatrix(size, 1); - - float sum = 0F; - float midpoint = (size - 1) / 2F; - - for (int i = 0; i < size; i++) - { - float x = i - midpoint; - float gx = ImageMaths.Gaussian(x, weight); - sum += gx; - kernel[0, i] = gx; - } - - // Normalize kernel so that the sum of all weights equals 1 - for (int i = 0; i < size; i++) - { - kernel[0, i] /= sum; - } - - return kernel; - } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs index 001471720a..bc06ee6182 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs @@ -3,38 +3,38 @@ using System; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// Applies Gaussian sharpening processing to the image. + /// Defines Gaussian sharpening by a (Sigma, Radius) pair. /// - /// The pixel format. - internal class GaussianSharpenProcessor : ImageProcessor - where TPixel : struct, IPixel + public class GaussianSharpenProcessor : IImageProcessor { + /// + /// The default value for . + /// + public const float DefaultSigma = 3f; + /// - /// The maximum size of the kernel in either direction. + /// Initializes a new instance of the class. /// - private readonly int kernelSize; + public GaussianSharpenProcessor() + : this(DefaultSigma, ConvolutionProcessorHelpers.GetDefaultGaussianRadius(DefaultSigma)) + { + } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// - /// The 'sigma' value representing the weight of the sharpening. - /// - public GaussianSharpenProcessor(float sigma = 3F) - : this(sigma, (int)MathF.Ceiling(sigma * 3)) + /// The 'sigma' value representing the weight of the blur. + public GaussianSharpenProcessor(float sigma) + : this(sigma, ConvolutionProcessorHelpers.GetDefaultGaussianRadius(sigma)) { - // Kernel radius is calculated using the minimum viable value. - // http://chemaguerra.com/gaussian-filter-radius/ } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The 'radius' value representing the size of the area to sample. @@ -45,10 +45,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// - /// The 'sigma' value representing the weight of the sharpen. + /// The 'sigma' value representing the weight of the blur. /// /// /// The 'radius' value representing the size of the area to sample. @@ -56,10 +56,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// public GaussianSharpenProcessor(float sigma, int radius) { - this.kernelSize = (radius * 2) + 1; this.Sigma = sigma; - this.KernelX = this.CreateGaussianKernel(); - this.KernelY = this.KernelX.Transpose(); + this.Radius = radius; } /// @@ -68,63 +66,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public float Sigma { get; } /// - /// Gets the horizontal gradient operator. - /// - public DenseMatrix KernelX { get; } - - /// - /// Gets the vertical gradient operator. + /// Gets the radius defining the size of the area to sample. /// - public DenseMatrix KernelY { get; } + public int Radius { get; } - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - => new Convolution2PassProcessor(this.KernelX, this.KernelY, false).Apply(source, sourceRectangle, configuration); - - /// - /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function - /// - /// The - private DenseMatrix CreateGaussianKernel() + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - int size = this.kernelSize; - float weight = this.Sigma; - var kernel = new DenseMatrix(size, 1); - - float sum = 0; - - float midpoint = (size - 1) / 2F; - for (int i = 0; i < size; i++) - { - float x = i - midpoint; - float gx = ImageMaths.Gaussian(x, weight); - sum += gx; - kernel[0, i] = gx; - } - - // Invert the kernel for sharpening. - int midpointRounded = (int)midpoint; - for (int i = 0; i < size; i++) - { - if (i == midpointRounded) - { - // Calculate central value - kernel[0, i] = (2F * sum) - kernel[0, i]; - } - else - { - // invert value - kernel[0, i] = -kernel[0, i]; - } - } - - // Normalize kernel so that the sum of all weights equals 1 - for (int i = 0; i < size; i++) - { - kernel[0, i] /= sum; - } - - return kernel; + return new GaussianSharpenProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs new file mode 100644 index 0000000000..edde9f9e7f --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor{TPixel}.cs @@ -0,0 +1,42 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Convolution +{ + /// + /// Applies Gaussian sharpening processing to the image. + /// + /// The pixel format. + internal class GaussianSharpenProcessor : ImageProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// The defining the processor parameters. + public GaussianSharpenProcessor(GaussianSharpenProcessor definition) + { + int kernelSize = (definition.Radius * 2) + 1; + this.KernelX = ConvolutionProcessorHelpers.CreateGaussianSharpenKernel(kernelSize, definition.Sigma); + this.KernelY = this.KernelX.Transpose(); + } + + /// + /// Gets the horizontal gradient operator. + /// + public DenseMatrix KernelX { get; } + + /// + /// Gets the vertical gradient operator. + /// + public DenseMatrix KernelY { get; } + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + => new Convolution2PassProcessor(this.KernelX, this.KernelY, false).Apply(source, sourceRectangle, configuration); + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs index 675498745e..7c2205f4e4 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/GaussianSharpenTest.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution public void GaussianSharpen_GaussianSharpenProcessorDefaultsSet() { this.operations.GaussianSharpen(); - var processor = this.Verify>(); + var processor = this.Verify(); Assert.Equal(3f, processor.Sigma); } @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution public void GaussianSharpen_amount_GaussianSharpenProcessorDefaultsSet() { this.operations.GaussianSharpen(0.2f); - var processor = this.Verify>(); + var processor = this.Verify(); Assert.Equal(.2f, processor.Sigma); } @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution public void GaussianSharpen_amount_rect_GaussianSharpenProcessorDefaultsSet() { this.operations.GaussianSharpen(0.6f, this.rect); - var processor = this.Verify>(this.rect); + var processor = this.Verify(this.rect); Assert.Equal(.6f, processor.Sigma); } From 91d0f6fd02126b9bc952a749f9eac2d3cf75b425 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 May 2019 00:18:23 +0200 Subject: [PATCH 17/31] drop IEdgeDetectorProcessor --- .../Processing/DetectEdgesExtensions.cs | 9 +++--- .../Convolution/BoxBlurProcessor{TPixel}.cs | 5 ++-- .../ConvolutionProcessorHelpers.cs | 8 +++--- .../Convolution/EdgeDetector2DProcessor.cs | 2 +- .../EdgeDetectorCompassProcessor.cs | 2 +- .../Convolution/EdgeDetectorProcessor.cs | 2 +- .../Convolution/GaussianSharpenProcessor.cs | 1 + .../Convolution/IEdgeDetectorProcessor.cs | 28 ------------------- .../Processors/IImageProcessor{TPixel}.cs | 4 +-- .../Processors/ImageProcessorExtensions.cs | 4 +-- .../Transforms/Resize/ResizeProcessor.cs | 2 +- .../Processing/Convolution/DetectEdgesTest.cs | 4 +-- 12 files changed, 22 insertions(+), 49 deletions(-) delete mode 100644 src/ImageSharp/Processing/Processors/Convolution/IEdgeDetectorProcessor.cs diff --git a/src/ImageSharp/Processing/DetectEdgesExtensions.cs b/src/ImageSharp/Processing/DetectEdgesExtensions.cs index 5ac89df291..a3be298d0b 100644 --- a/src/ImageSharp/Processing/DetectEdgesExtensions.cs +++ b/src/ImageSharp/Processing/DetectEdgesExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors; using SixLabors.ImageSharp.Processing.Processors.Convolution; using SixLabors.Primitives; @@ -82,7 +83,7 @@ namespace SixLabors.ImageSharp.Processing /// The image this method extends. /// The filter for detecting edges. /// The . - public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, IEdgeDetectorProcessor filter) + private static IImageProcessingContext DetectEdges(this IImageProcessingContext source, IImageProcessor filter) where TPixel : struct, IPixel { return source.ApplyProcessor(filter); @@ -98,17 +99,17 @@ namespace SixLabors.ImageSharp.Processing /// /// The filter for detecting edges. /// The . - public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, Rectangle rectangle, IEdgeDetectorProcessor filter) + private static IImageProcessingContext DetectEdges(this IImageProcessingContext source, Rectangle rectangle, IImageProcessor filter) where TPixel : struct, IPixel { source.ApplyProcessor(filter, rectangle); return source; } - private static IEdgeDetectorProcessor GetProcessor(EdgeDetectionOperators filter, bool grayscale) + private static IImageProcessor GetProcessor(EdgeDetectionOperators filter, bool grayscale) where TPixel : struct, IPixel { - IEdgeDetectorProcessor processor; + IImageProcessor processor; switch (filter) { diff --git a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs index ba092cb424..f4252e840d 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor{TPixel}.cs @@ -1,5 +1,5 @@ -// // Copyright (c) Six Labors and contributors. -// // Licensed under the Apache License, Version 2.0. +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Primitives; @@ -38,7 +38,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// public DenseMatrix KernelY { get; } - /// protected override void OnFrameApply( ImageFrame source, diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs index bb0072b82e..1fb26c976a 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs @@ -1,5 +1,5 @@ -// // Copyright (c) Six Labors and contributors. -// // Licensed under the Apache License, Version 2.0. +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. using System; @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// /// Kernel radius is calculated using the minimum viable value. - /// . + /// See http://chemaguerra.com/gaussian-filter-radius/ . /// internal static int GetDefaultGaussianRadius(float sigma) { @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } /// - /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function + /// Create a 1 dimensional Gaussian kernel using the Gaussian G(x) function. /// /// The . internal static DenseMatrix CreateGaussianBlurKernel(int size, float weight) diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs index b4ac89139a..ef185a01f0 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Defines a processor that detects edges within an image using two one-dimensional matrices. /// /// The pixel format. - internal abstract class EdgeDetector2DProcessor : ImageProcessor, IEdgeDetectorProcessor + internal abstract class EdgeDetector2DProcessor : ImageProcessor where TPixel : struct, IPixel { /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs index aa33699f5d..1205b02dc0 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Defines a processor that detects edges within an image using a eight two dimensional matrices. /// /// The pixel format. - internal abstract class EdgeDetectorCompassProcessor : ImageProcessor, IEdgeDetectorProcessor + internal abstract class EdgeDetectorCompassProcessor : ImageProcessor where TPixel : struct, IPixel { /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs index 9ec79da9eb..b4d4763d03 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Defines a processor that detects edges within an image using a single two dimensional matrix. /// /// The pixel format. - internal abstract class EdgeDetectorProcessor : ImageProcessor, IEdgeDetectorProcessor + internal abstract class EdgeDetectorProcessor : ImageProcessor where TPixel : struct, IPixel { /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs index bc06ee6182..84e56869d3 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs @@ -70,6 +70,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// public int Radius { get; } + /// public IImageProcessor CreatePixelSpecificProcessor() where TPixel : struct, IPixel { diff --git a/src/ImageSharp/Processing/Processors/Convolution/IEdgeDetectorProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/IEdgeDetectorProcessor.cs deleted file mode 100644 index b2ecbf1158..0000000000 --- a/src/ImageSharp/Processing/Processors/Convolution/IEdgeDetectorProcessor.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors; - -namespace SixLabors.ImageSharp.Processing.Processors.Convolution -{ - /// - /// Provides properties and methods allowing the detection of edges within an image. - /// - /// The pixel format. - public interface IEdgeDetectorProcessor : IImageProcessor, IEdgeDetectorProcessor - where TPixel : struct, IPixel - { - } - - /// - /// Provides properties and methods allowing the detection of edges within an image. - /// - public interface IEdgeDetectorProcessor - { - /// - /// Gets a value indicating whether to convert the image to grayscale before performing edge detection. - /// - bool Grayscale { get; } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs index f2c7ef29c1..90dfaf8db5 100644 --- a/src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/IImageProcessor{TPixel}.cs @@ -1,5 +1,5 @@ -// // Copyright (c) Six Labors and contributors. -// // Licensed under the Apache License, Version 2.0. +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs b/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs index 1e189b9c78..762d761c6c 100644 --- a/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs +++ b/src/ImageSharp/Processing/Processors/ImageProcessorExtensions.cs @@ -1,5 +1,5 @@ -// // Copyright (c) Six Labors and contributors. -// // Licensed under the Apache License, Version 2.0. +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; diff --git a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs index 5710c4bd8c..8762d6b263 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/Resize/ResizeProcessor.cs @@ -9,7 +9,7 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { /// - /// Implements resizing of images using various resamplers. + /// Defines an image resizing operation with the given and dimensional parameters. /// public class ResizeProcessor : IImageProcessor { diff --git a/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs index 60fa19b490..cf5feac275 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution [Theory] [MemberData(nameof(EdgeDetectionTheoryData))] public void DetectEdges_filter_SobelProcessorDefaultsSet(TestType type, EdgeDetectionOperators filter) - where TProcessor : IEdgeDetectorProcessor + where TProcessor : IImageProcessor { this.operations.DetectEdges(filter); @@ -62,7 +62,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution [Theory] [MemberData(nameof(EdgeDetectionTheoryData))] public void DetectEdges_filter_grayscale_SobelProcessorDefaultsSet(TestType type, EdgeDetectionOperators filter) - where TProcessor : IEdgeDetectorProcessor + where TProcessor : IImageProcessor { bool grey = (int)filter % 2 == 0; this.operations.DetectEdges(filter, grey); From 9b451fab0cfeb017c58a7d475565538b63942a00 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 May 2019 01:10:24 +0200 Subject: [PATCH 18/31] Refactor edge detection --- src/ImageSharp/ImageSharp.csproj.DotSettings | 1 + .../Processing/DetectEdgesExtensions.cs | 96 +++++++++---------- .../ConvolutionProcessorHelpers.cs | 2 +- .../Convolution/EdgeDetector2DProcessor.cs | 5 +- .../EdgeDetectorCompassProcessor.cs | 54 ++--------- .../Convolution/EdgeDetectorProcessor.cs | 37 ++----- .../EdgeDetectorProcessor{TPixel}.cs | 49 ++++++++++ .../Convolution/KayyaliProcessor.cs | 22 +++-- .../Convolution/Kernels/CompassKernels.cs | 57 +++++++++++ .../{ => Kernels}/KayyaliKernels.cs | 0 .../{ => Kernels}/KirschKernels.cs | 18 ++-- .../{ => Kernels}/LaplacianKernelFactory.cs | 0 .../{ => Kernels}/LaplacianKernels.cs | 0 .../{ => Kernels}/PrewittKernels.cs | 0 .../{ => Kernels}/RobertsCrossKernels.cs | 0 .../{ => Kernels}/RobinsonKernels.cs | 20 ++-- .../{ => Kernels}/ScharrKernels.cs | 0 .../Convolution/{ => Kernels}/SobelKernels.cs | 0 .../Processors/Convolution/KirschProcessor.cs | 40 ++------ .../Convolution/Laplacian3x3Processor.cs | 14 ++- .../Convolution/Laplacian5x5Processor.cs | 20 ++-- .../LaplacianOfGaussianProcessor.cs | 18 ++-- .../Convolution/PrewittProcessor.cs | 20 ++-- .../Convolution/RobertsCrossProcessor.cs | 18 ++-- .../Convolution/RobinsonProcessor.cs | 38 ++------ .../Processors/Convolution/ScharrProcessor.cs | 16 ++-- .../Processors/Convolution/SobelProcessor.cs | 27 ++++-- .../Transforms/EntropyCropProcessor.cs | 2 +- .../Processing/Convolution/DetectEdgesTest.cs | 40 ++++---- 29 files changed, 329 insertions(+), 285 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Convolution/Kernels/CompassKernels.cs rename src/ImageSharp/Processing/Processors/Convolution/{ => Kernels}/KayyaliKernels.cs (100%) rename src/ImageSharp/Processing/Processors/Convolution/{ => Kernels}/KirschKernels.cs (81%) rename src/ImageSharp/Processing/Processors/Convolution/{ => Kernels}/LaplacianKernelFactory.cs (100%) rename src/ImageSharp/Processing/Processors/Convolution/{ => Kernels}/LaplacianKernels.cs (100%) rename src/ImageSharp/Processing/Processors/Convolution/{ => Kernels}/PrewittKernels.cs (100%) rename src/ImageSharp/Processing/Processors/Convolution/{ => Kernels}/RobertsCrossKernels.cs (100%) rename src/ImageSharp/Processing/Processors/Convolution/{ => Kernels}/RobinsonKernels.cs (78%) rename src/ImageSharp/Processing/Processors/Convolution/{ => Kernels}/ScharrKernels.cs (100%) rename src/ImageSharp/Processing/Processors/Convolution/{ => Kernels}/SobelKernels.cs (100%) diff --git a/src/ImageSharp/ImageSharp.csproj.DotSettings b/src/ImageSharp/ImageSharp.csproj.DotSettings index a7337240a5..e446893e94 100644 --- a/src/ImageSharp/ImageSharp.csproj.DotSettings +++ b/src/ImageSharp/ImageSharp.csproj.DotSettings @@ -6,5 +6,6 @@ True True True + True True True \ No newline at end of file diff --git a/src/ImageSharp/Processing/DetectEdgesExtensions.cs b/src/ImageSharp/Processing/DetectEdgesExtensions.cs index a3be298d0b..7527d601df 100644 --- a/src/ImageSharp/Processing/DetectEdgesExtensions.cs +++ b/src/ImageSharp/Processing/DetectEdgesExtensions.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.ImageSharp.Processing.Processors; using SixLabors.ImageSharp.Processing.Processors.Convolution; using SixLabors.Primitives; @@ -9,82 +8,79 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds edge detection extensions to the type. + /// Adds edge detection extensions to the type. /// public static class DetectEdgesExtensions { /// - /// Detects any edges within the image. Uses the filter + /// Detects any edges within the image. Uses the filter /// operating in grayscale mode. /// - /// The pixel format. /// The image this method extends. - /// The . - public static IImageProcessingContext DetectEdges(this IImageProcessingContext source) - where TPixel : struct, IPixel - => DetectEdges(source, new SobelProcessor(true)); + /// The . + public static IImageProcessingContext DetectEdges(this IImageProcessingContext source) => + DetectEdges(source, new SobelProcessor(true)); /// - /// Detects any edges within the image. Uses the filter + /// Detects any edges within the image. Uses the filter /// operating in grayscale mode. /// - /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// - /// The . - public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, Rectangle rectangle) - where TPixel : struct, IPixel - => DetectEdges(source, rectangle, new SobelProcessor(true)); + /// The . + public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, Rectangle rectangle) => + DetectEdges(source, rectangle, new SobelProcessor(true)); /// /// Detects any edges within the image. /// - /// The pixel format. /// The image this method extends. /// The filter for detecting edges. - /// The . - public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, EdgeDetectionOperators filter) - where TPixel : struct, IPixel - => DetectEdges(source, GetProcessor(filter, true)); + /// The . + public static IImageProcessingContext DetectEdges( + this IImageProcessingContext source, + EdgeDetectionOperators filter) => + DetectEdges(source, GetProcessor(filter, true)); /// /// Detects any edges within the image. /// - /// The pixel format. /// The image this method extends. /// The filter for detecting edges. /// Whether to convert the image to grayscale first. Defaults to true. - /// The . - public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, EdgeDetectionOperators filter, bool grayscale) - where TPixel : struct, IPixel - => DetectEdges(source, GetProcessor(filter, grayscale)); + /// The . + public static IImageProcessingContext DetectEdges( + this IImageProcessingContext source, + EdgeDetectionOperators filter, + bool grayscale) => + DetectEdges(source, GetProcessor(filter, grayscale)); /// /// Detects any edges within the image. /// - /// The pixel format. /// The image this method extends. /// The filter for detecting edges. /// /// The structure that specifies the portion of the image object to alter. /// /// Whether to convert the image to grayscale first. Defaults to true. - /// The . - public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, EdgeDetectionOperators filter, Rectangle rectangle, bool grayscale = true) - where TPixel : struct, IPixel - => DetectEdges(source, rectangle, GetProcessor(filter, grayscale)); + /// The . + public static IImageProcessingContext DetectEdges( + this IImageProcessingContext source, + EdgeDetectionOperators filter, + Rectangle rectangle, + bool grayscale = true) => + DetectEdges(source, rectangle, GetProcessor(filter, grayscale)); /// /// Detects any edges within the image. /// - /// The pixel format. /// The image this method extends. /// The filter for detecting edges. - /// The . - private static IImageProcessingContext DetectEdges(this IImageProcessingContext source, IImageProcessor filter) - where TPixel : struct, IPixel + /// The . + private static IImageProcessingContext DetectEdges(this IImageProcessingContext source, IImageProcessor filter) { return source.ApplyProcessor(filter); } @@ -92,65 +88,65 @@ namespace SixLabors.ImageSharp.Processing /// /// Detects any edges within the image. /// - /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// /// The filter for detecting edges. - /// The . - private static IImageProcessingContext DetectEdges(this IImageProcessingContext source, Rectangle rectangle, IImageProcessor filter) - where TPixel : struct, IPixel + /// The . + private static IImageProcessingContext DetectEdges( + this IImageProcessingContext source, + Rectangle rectangle, + IImageProcessor filter) { source.ApplyProcessor(filter, rectangle); return source; } - private static IImageProcessor GetProcessor(EdgeDetectionOperators filter, bool grayscale) - where TPixel : struct, IPixel + private static IImageProcessor GetProcessor(EdgeDetectionOperators filter, bool grayscale) { - IImageProcessor processor; + IImageProcessor processor; switch (filter) { case EdgeDetectionOperators.Kayyali: - processor = new KayyaliProcessor(grayscale); + processor = new KayyaliProcessor(grayscale); break; case EdgeDetectionOperators.Kirsch: - processor = new KirschProcessor(grayscale); + processor = new KirschProcessor(grayscale); break; case EdgeDetectionOperators.Laplacian3x3: - processor = new Laplacian3x3Processor(grayscale); + processor = new Laplacian3x3Processor(grayscale); break; case EdgeDetectionOperators.Laplacian5x5: - processor = new Laplacian5x5Processor(grayscale); + processor = new Laplacian5x5Processor(grayscale); break; case EdgeDetectionOperators.LaplacianOfGaussian: - processor = new LaplacianOfGaussianProcessor(grayscale); + processor = new LaplacianOfGaussianProcessor(grayscale); break; case EdgeDetectionOperators.Prewitt: - processor = new PrewittProcessor(grayscale); + processor = new PrewittProcessor(grayscale); break; case EdgeDetectionOperators.RobertsCross: - processor = new RobertsCrossProcessor(grayscale); + processor = new RobertsCrossProcessor(grayscale); break; case EdgeDetectionOperators.Robinson: - processor = new RobinsonProcessor(grayscale); + processor = new RobinsonProcessor(grayscale); break; case EdgeDetectionOperators.Scharr: - processor = new ScharrProcessor(grayscale); + processor = new ScharrProcessor(grayscale); break; default: - processor = new SobelProcessor(grayscale); + processor = new SobelProcessor(grayscale); break; } diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs index 1fb26c976a..661ab523db 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessorHelpers.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// /// Kernel radius is calculated using the minimum viable value. - /// See http://chemaguerra.com/gaussian-filter-radius/ . + /// See . /// internal static int GetDefaultGaussianRadius(float sigma) { diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs index ef185a01f0..5daf14fc32 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs @@ -12,7 +12,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Defines a processor that detects edges within an image using two one-dimensional matrices. /// /// The pixel format. - internal abstract class EdgeDetector2DProcessor : ImageProcessor + internal class EdgeDetector2DProcessor : ImageProcessor where TPixel : struct, IPixel { /// @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// The horizontal gradient operator. /// The vertical gradient operator. /// Whether to convert the image to grayscale before performing edge detection. - protected EdgeDetector2DProcessor(in DenseMatrix kernelX, in DenseMatrix kernelY, bool grayscale) + internal EdgeDetector2DProcessor(in DenseMatrix kernelX, in DenseMatrix kernelY, bool grayscale) { Guard.IsTrue(kernelX.Size.Equals(kernelY.Size), $"{nameof(kernelX)} {nameof(kernelY)}", "Kernel sizes must be the same."); this.KernelX = kernelX; @@ -39,7 +39,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// public DenseMatrix KernelY { get; } - /// public bool Grayscale { get; } /// diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs index 1205b02dc0..227003195d 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs @@ -19,57 +19,23 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Defines a processor that detects edges within an image using a eight two dimensional matrices. /// /// The pixel format. - internal abstract class EdgeDetectorCompassProcessor : ImageProcessor + internal class EdgeDetectorCompassProcessor : ImageProcessor where TPixel : struct, IPixel { /// /// Initializes a new instance of the class. /// + /// Gets the kernels to use. /// Whether to convert the image to grayscale before performing edge detection. - protected EdgeDetectorCompassProcessor(bool grayscale) => this.Grayscale = grayscale; - - /// - /// Gets the North gradient operator - /// - public abstract DenseMatrix North { get; } - - /// - /// Gets the NorthWest gradient operator - /// - public abstract DenseMatrix NorthWest { get; } - - /// - /// Gets the West gradient operator - /// - public abstract DenseMatrix West { get; } - - /// - /// Gets the SouthWest gradient operator - /// - public abstract DenseMatrix SouthWest { get; } - - /// - /// Gets the South gradient operator - /// - public abstract DenseMatrix South { get; } - - /// - /// Gets the SouthEast gradient operator - /// - public abstract DenseMatrix SouthEast { get; } - - /// - /// Gets the East gradient operator - /// - public abstract DenseMatrix East { get; } + internal EdgeDetectorCompassProcessor(CompassKernels kernels, bool grayscale) + { + this.Grayscale = grayscale; + this.Kernels = kernels; + } - /// - /// Gets the NorthEast gradient operator - /// - public abstract DenseMatrix NorthEast { get; } + private CompassKernels Kernels { get; } - /// - public bool Grayscale { get; } + private bool Grayscale { get; } /// protected override void BeforeFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) @@ -83,7 +49,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) { - DenseMatrix[] kernels = { this.North, this.NorthWest, this.West, this.SouthWest, this.South, this.SouthEast, this.East, this.NorthEast }; + DenseMatrix[] kernels = this.Kernels.Flatten(); int startY = sourceRectangle.Y; int endY = sourceRectangle.Bottom; diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs index b4d4763d03..75be15bcc7 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor.cs @@ -2,49 +2,30 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; -using SixLabors.ImageSharp.Processing.Processors.Filters; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// /// Defines a processor that detects edges within an image using a single two dimensional matrix. /// - /// The pixel format. - internal abstract class EdgeDetectorProcessor : ImageProcessor - where TPixel : struct, IPixel + public abstract class EdgeDetectorProcessor : IImageProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The 2d gradient operator. - /// Whether to convert the image to grayscale before performing edge detection. - protected EdgeDetectorProcessor(in DenseMatrix kernelXY, bool grayscale) + /// A value indicating whether to convert the image to grayscale before performing edge detection. + protected EdgeDetectorProcessor(bool grayscale) { - this.KernelXY = kernelXY; this.Grayscale = grayscale; } - /// - public bool Grayscale { get; } - /// - /// Gets the 2d gradient operator. + /// Gets a value indicating whether to convert the image to grayscale before performing edge detection. /// - public DenseMatrix KernelXY { get; } - - /// - protected override void BeforeFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - { - if (this.Grayscale) - { - new GrayscaleBt709Processor(1F).ApplyToFrame(source, sourceRectangle, configuration); - } - } + public bool Grayscale { get; } - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - => new ConvolutionProcessor(this.KernelXY, true).Apply(source, sourceRectangle, configuration); + /// + public abstract IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel; } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs new file mode 100644 index 0000000000..026313cc12 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorProcessor{TPixel}.cs @@ -0,0 +1,49 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Primitives; +using SixLabors.ImageSharp.Processing.Processors.Filters; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Convolution +{ + /// + /// Defines a processor that detects edges within an image using a single two dimensional matrix. + /// + /// The pixel format. + internal class EdgeDetectorProcessor : ImageProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// The 2d gradient operator. + /// Whether to convert the image to grayscale before performing edge detection. + public EdgeDetectorProcessor(in DenseMatrix kernelXY, bool grayscale) + { + this.KernelXY = kernelXY; + this.Grayscale = grayscale; + } + + public bool Grayscale { get; } + + /// + /// Gets the 2d gradient operator. + /// + public DenseMatrix KernelXY { get; } + + /// + protected override void BeforeFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + if (this.Grayscale) + { + new GrayscaleBt709Processor(1F).ApplyToFrame(source, sourceRectangle, configuration); + } + } + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + => new ConvolutionProcessor(this.KernelXY, true).Apply(source, sourceRectangle, configuration); + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/KayyaliProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/KayyaliProcessor.cs index 8652efa120..99dfb17ffb 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/KayyaliProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/KayyaliProcessor.cs @@ -1,24 +1,30 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// Applies edge detection processing to the image using the Kayyali operator filter. + /// Defines edge detection processing using the Kayyali operator filter. + /// See . /// - /// The pixel format. - internal class KayyaliProcessor : EdgeDetector2DProcessor - where TPixel : struct, IPixel + public class KayyaliProcessor : EdgeDetectorProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Whether to convert the image to grayscale before performing edge detection. public KayyaliProcessor(bool grayscale) - : base(KayyaliKernels.KayyaliX, KayyaliKernels.KayyaliY, grayscale) + : base(grayscale) + { + } + + /// + public override IImageProcessor CreatePixelSpecificProcessor() { + return new EdgeDetector2DProcessor( + KayyaliKernels.KayyaliX, + KayyaliKernels.KayyaliY, + this.Grayscale); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/Kernels/CompassKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/CompassKernels.cs new file mode 100644 index 0000000000..f44de9105b --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Convolution/Kernels/CompassKernels.cs @@ -0,0 +1,57 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Convolution +{ + internal abstract class CompassKernels + { + /// + /// Gets the North gradient operator. + /// + public abstract DenseMatrix North { get; } + + /// + /// Gets the NorthWest gradient operator. + /// + public abstract DenseMatrix NorthWest { get; } + + /// + /// Gets the West gradient operator. + /// + public abstract DenseMatrix West { get; } + + /// + /// Gets the SouthWest gradient operator. + /// + public abstract DenseMatrix SouthWest { get; } + + /// + /// Gets the South gradient operator. + /// + public abstract DenseMatrix South { get; } + + /// + /// Gets the SouthEast gradient operator. + /// + public abstract DenseMatrix SouthEast { get; } + + /// + /// Gets the East gradient operator. + /// + public abstract DenseMatrix East { get; } + + /// + /// Gets the NorthEast gradient operator. + /// + public abstract DenseMatrix NorthEast { get; } + + public DenseMatrix[] Flatten() => + new[] + { + this.North, this.NorthWest, this.West, this.SouthWest, + this.South, this.SouthEast, this.East, this.NorthEast + }; + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/KayyaliKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/KayyaliKernels.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Convolution/KayyaliKernels.cs rename to src/ImageSharp/Processing/Processors/Convolution/Kernels/KayyaliKernels.cs diff --git a/src/ImageSharp/Processing/Processors/Convolution/KirschKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/KirschKernels.cs similarity index 81% rename from src/ImageSharp/Processing/Processors/Convolution/KirschKernels.cs rename to src/ImageSharp/Processing/Processors/Convolution/Kernels/KirschKernels.cs index 86232e306a..882b87075b 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/KirschKernels.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Kernels/KirschKernels.cs @@ -8,12 +8,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Contains the eight matrices used for Kirsch edge detection /// - internal static class KirschKernels + internal class KirschKernels : CompassKernels { /// /// Gets the North gradient operator /// - public static DenseMatrix KirschNorth => + public override DenseMatrix North => new float[,] { { 5, 5, 5 }, @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the NorthWest gradient operator /// - public static DenseMatrix KirschNorthWest => + public override DenseMatrix NorthWest => new float[,] { { 5, 5, -3 }, @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the West gradient operator /// - public static DenseMatrix KirschWest => + public override DenseMatrix West => new float[,] { { 5, -3, -3 }, @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the SouthWest gradient operator /// - public static DenseMatrix KirschSouthWest => + public override DenseMatrix SouthWest => new float[,] { { -3, -3, -3 }, @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the South gradient operator /// - public static DenseMatrix KirschSouth => + public override DenseMatrix South => new float[,] { { -3, -3, -3 }, @@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the SouthEast gradient operator /// - public static DenseMatrix KirschSouthEast => + public override DenseMatrix SouthEast => new float[,] { { -3, -3, -3 }, @@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the East gradient operator /// - public static DenseMatrix KirschEast => + public override DenseMatrix East => new float[,] { { -3, -3, 5 }, @@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the NorthEast gradient operator /// - public static DenseMatrix KirschNorthEast => + public override DenseMatrix NorthEast => new float[,] { { -3, 5, 5 }, diff --git a/src/ImageSharp/Processing/Processors/Convolution/LaplacianKernelFactory.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/LaplacianKernelFactory.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Convolution/LaplacianKernelFactory.cs rename to src/ImageSharp/Processing/Processors/Convolution/Kernels/LaplacianKernelFactory.cs diff --git a/src/ImageSharp/Processing/Processors/Convolution/LaplacianKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/LaplacianKernels.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Convolution/LaplacianKernels.cs rename to src/ImageSharp/Processing/Processors/Convolution/Kernels/LaplacianKernels.cs diff --git a/src/ImageSharp/Processing/Processors/Convolution/PrewittKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/PrewittKernels.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Convolution/PrewittKernels.cs rename to src/ImageSharp/Processing/Processors/Convolution/Kernels/PrewittKernels.cs diff --git a/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/RobertsCrossKernels.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Convolution/RobertsCrossKernels.cs rename to src/ImageSharp/Processing/Processors/Convolution/Kernels/RobertsCrossKernels.cs diff --git a/src/ImageSharp/Processing/Processors/Convolution/RobinsonKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/RobinsonKernels.cs similarity index 78% rename from src/ImageSharp/Processing/Processors/Convolution/RobinsonKernels.cs rename to src/ImageSharp/Processing/Processors/Convolution/Kernels/RobinsonKernels.cs index 4f47184e30..699d669ec9 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/RobinsonKernels.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Kernels/RobinsonKernels.cs @@ -6,14 +6,14 @@ using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// Contains the kernels used for Robinson edge detection + /// Contains the kernels used for Robinson edge detection. /// - internal static class RobinsonKernels + internal class RobinsonKernels : CompassKernels { /// /// Gets the North gradient operator /// - public static DenseMatrix RobinsonNorth => + public override DenseMatrix North => new float[,] { { 1, 2, 1 }, @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the NorthWest gradient operator /// - public static DenseMatrix RobinsonNorthWest => + public override DenseMatrix NorthWest => new float[,] { { 2, 1, 0 }, @@ -35,7 +35,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the West gradient operator /// - public static DenseMatrix RobinsonWest => + public override DenseMatrix West => new float[,] { { 1, 0, -1 }, @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the SouthWest gradient operator /// - public static DenseMatrix RobinsonSouthWest => + public override DenseMatrix SouthWest => new float[,] { { 0, -1, -2 }, @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the South gradient operator /// - public static DenseMatrix RobinsonSouth => + public override DenseMatrix South => new float[,] { { -1, -2, -1 }, @@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the SouthEast gradient operator /// - public static DenseMatrix RobinsonSouthEast => + public override DenseMatrix SouthEast => new float[,] { { -2, -1, 0 }, @@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the East gradient operator /// - public static DenseMatrix RobinsonEast => + public override DenseMatrix East => new float[,] { { -1, 0, 1 }, @@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Gets the NorthEast gradient operator /// - public static DenseMatrix RobinsonNorthEast => + public override DenseMatrix NorthEast => new float[,] { { 0, 1, 2 }, diff --git a/src/ImageSharp/Processing/Processors/Convolution/ScharrKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/ScharrKernels.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Convolution/ScharrKernels.cs rename to src/ImageSharp/Processing/Processors/Convolution/Kernels/ScharrKernels.cs diff --git a/src/ImageSharp/Processing/Processors/Convolution/SobelKernels.cs b/src/ImageSharp/Processing/Processors/Convolution/Kernels/SobelKernels.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Convolution/SobelKernels.cs rename to src/ImageSharp/Processing/Processors/Convolution/Kernels/SobelKernels.cs diff --git a/src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs index c3188676f3..fc73903c7d 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs @@ -1,20 +1,16 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// Applies edge detection processing to the image using the Kirsch operator filter. + /// Defines edge detection using the Kirsch operator filter. + /// See . /// - /// The pixel format. - internal class KirschProcessor : EdgeDetectorCompassProcessor - where TPixel : struct, IPixel + public class KirschProcessor : EdgeDetectorProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Whether to convert the image to grayscale before performing edge detection. public KirschProcessor(bool grayscale) @@ -22,28 +18,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { } - /// - public override DenseMatrix North => KirschKernels.KirschNorth; - - /// - public override DenseMatrix NorthWest => KirschKernels.KirschNorthWest; - - /// - public override DenseMatrix West => KirschKernels.KirschWest; - - /// - public override DenseMatrix SouthWest => KirschKernels.KirschSouthWest; - - /// - public override DenseMatrix South => KirschKernels.KirschSouth; - - /// - public override DenseMatrix SouthEast => KirschKernels.KirschSouthEast; - - /// - public override DenseMatrix East => KirschKernels.KirschEast; - - /// - public override DenseMatrix NorthEast => KirschKernels.KirschNorthEast; + /// + public override IImageProcessor CreatePixelSpecificProcessor() + { + return new EdgeDetectorCompassProcessor(new KirschKernels(), this.Grayscale); + } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs b/src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs index f498d374cc..18a53cb131 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs @@ -9,17 +9,21 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Applies edge detection processing to the image using the Laplacian 3x3 operator filter. /// /// - /// The pixel format. - internal class Laplacian3x3Processor : EdgeDetectorProcessor - where TPixel : struct, IPixel + public class Laplacian3x3Processor : EdgeDetectorProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Whether to convert the image to grayscale before performing edge detection. public Laplacian3x3Processor(bool grayscale) - : base(LaplacianKernels.Laplacian3x3, grayscale) + : base(grayscale) { } + + /// + public override IImageProcessor CreatePixelSpecificProcessor() + { + return new EdgeDetectorProcessor(LaplacianKernels.Laplacian3x3, this.Grayscale); + } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/Laplacian5x5Processor.cs b/src/ImageSharp/Processing/Processors/Convolution/Laplacian5x5Processor.cs index 558acf7b35..1c95ca4903 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Laplacian5x5Processor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Laplacian5x5Processor.cs @@ -1,25 +1,27 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// Applies edge detection processing to the image using the Laplacian 5x5 operator filter. - /// + /// Defines edge detection processing using the Laplacian 5x5 operator filter. + /// . /// - /// The pixel format. - internal class Laplacian5x5Processor : EdgeDetectorProcessor - where TPixel : struct, IPixel + public class Laplacian5x5Processor : EdgeDetectorProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Whether to convert the image to grayscale before performing edge detection. public Laplacian5x5Processor(bool grayscale) - : base(LaplacianKernels.Laplacian5x5, grayscale) + : base(grayscale) + { + } + + /// + public override IImageProcessor CreatePixelSpecificProcessor() { + return new EdgeDetectorProcessor(LaplacianKernels.Laplacian5x5, this.Grayscale); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/LaplacianOfGaussianProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/LaplacianOfGaussianProcessor.cs index 6cc65dc587..d904d69b1b 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/LaplacianOfGaussianProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/LaplacianOfGaussianProcessor.cs @@ -1,25 +1,27 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// /// Applies edge detection processing to the image using the Laplacian of Gaussian operator filter. - /// + /// See . /// - /// The pixel format. - internal class LaplacianOfGaussianProcessor : EdgeDetectorProcessor - where TPixel : struct, IPixel + public class LaplacianOfGaussianProcessor : EdgeDetectorProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Whether to convert the image to grayscale before performing edge detection. public LaplacianOfGaussianProcessor(bool grayscale) - : base(LaplacianKernels.LaplacianOfGaussianXY, grayscale) + : base(grayscale) + { + } + + /// + public override IImageProcessor CreatePixelSpecificProcessor() { + return new EdgeDetectorProcessor(LaplacianKernels.LaplacianOfGaussianXY, this.Grayscale); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/PrewittProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/PrewittProcessor.cs index 75ef4dac62..939b1e0c52 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/PrewittProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/PrewittProcessor.cs @@ -1,25 +1,27 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// Applies edge detection processing to the image using the Prewitt operator filter. - /// + /// Defines edge detection using the Prewitt operator filter. + /// See . /// - /// The pixel format. - internal class PrewittProcessor : EdgeDetector2DProcessor - where TPixel : struct, IPixel + public class PrewittProcessor : EdgeDetectorProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Whether to convert the image to grayscale before performing edge detection. public PrewittProcessor(bool grayscale) - : base(PrewittKernels.PrewittX, PrewittKernels.PrewittY, grayscale) + : base(grayscale) + { + } + + /// + public override IImageProcessor CreatePixelSpecificProcessor() { + return new EdgeDetector2DProcessor(PrewittKernels.PrewittX, PrewittKernels.PrewittY, this.Grayscale); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs index d685860f62..ff41b6c694 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs @@ -6,20 +6,24 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// Applies edge detection processing to the image using the Roberts Cross operator filter. - /// + /// Defines edge detection processing using the Roberts Cross operator filter. + /// See . /// - /// The pixel format. - internal class RobertsCrossProcessor : EdgeDetector2DProcessor - where TPixel : struct, IPixel + public class RobertsCrossProcessor : EdgeDetectorProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Whether to convert the image to grayscale before performing edge detection. public RobertsCrossProcessor(bool grayscale) - : base(RobertsCrossKernels.RobertsCrossX, RobertsCrossKernels.RobertsCrossY, grayscale) + : base(grayscale) { } + + /// + public override IImageProcessor CreatePixelSpecificProcessor() + { + return new EdgeDetector2DProcessor(RobertsCrossKernels.RobertsCrossX, RobertsCrossKernels.RobertsCrossY, this.Grayscale); + } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs index 193c1008dd..603a81f676 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs @@ -7,15 +7,13 @@ using SixLabors.ImageSharp.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// Applies edge detection processing to the image using the Robinson operator filter. - /// + /// Defines edge detection using the Robinson operator filter. + /// See . /// - /// The pixel format. - internal class RobinsonProcessor : EdgeDetectorCompassProcessor - where TPixel : struct, IPixel + internal class RobinsonProcessor : EdgeDetectorProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Whether to convert the image to grayscale before performing edge detection. public RobinsonProcessor(bool grayscale) @@ -23,28 +21,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution { } - /// - public override DenseMatrix North => RobinsonKernels.RobinsonNorth; - - /// - public override DenseMatrix NorthWest => RobinsonKernels.RobinsonNorthWest; - - /// - public override DenseMatrix West => RobinsonKernels.RobinsonWest; - - /// - public override DenseMatrix SouthWest => RobinsonKernels.RobinsonSouthWest; - - /// - public override DenseMatrix South => RobinsonKernels.RobinsonSouth; - - /// - public override DenseMatrix SouthEast => RobinsonKernels.RobinsonSouthEast; - - /// - public override DenseMatrix East => RobinsonKernels.RobinsonEast; - - /// - public override DenseMatrix NorthEast => RobinsonKernels.RobinsonNorthEast; + /// + public override IImageProcessor CreatePixelSpecificProcessor() + { + return new EdgeDetectorCompassProcessor(new RobinsonKernels(), this.Grayscale); + } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs index 79fc0e79fc..d23ba53384 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs @@ -6,20 +6,24 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// Applies edge detection processing to the image using the Scharr operator filter. + /// Defines edge detection processing using the Scharr operator filter. /// /// - /// The pixel format. - internal class ScharrProcessor : EdgeDetector2DProcessor - where TPixel : struct, IPixel + public class ScharrProcessor : EdgeDetectorProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Whether to convert the image to grayscale before performing edge detection. public ScharrProcessor(bool grayscale) - : base(ScharrKernels.ScharrX, ScharrKernels.ScharrY, grayscale) + : base(grayscale) { } + + /// + public override IImageProcessor CreatePixelSpecificProcessor() + { + return new EdgeDetector2DProcessor(ScharrKernels.ScharrX, ScharrKernels.ScharrY, this.Grayscale); + } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs index 3ca53f6f0f..157bb62e50 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs @@ -2,24 +2,37 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Convolution { /// - /// The Sobel operator filter. - /// + /// Defines edge detection using the Sobel operator filter. + /// See . /// - /// The pixel format. - internal class SobelProcessor : EdgeDetector2DProcessor - where TPixel : struct, IPixel + public class SobelProcessor : EdgeDetectorProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// Whether to convert the image to grayscale before performing edge detection. public SobelProcessor(bool grayscale) - : base(SobelKernels.SobelX, SobelKernels.SobelY, grayscale) + : base(grayscale) { } + + /// + public override IImageProcessor CreatePixelSpecificProcessor() + { + return new EdgeDetector2DProcessor(SobelKernels.SobelX, SobelKernels.SobelY, this.Grayscale); + } + + // TODO: Move this to an appropriate extension method if possible. + internal void ApplyToFrame(ImageFrame frame, Rectangle sourceRectangle, Configuration configuration) + where TPixel : struct, IPixel + { + var processorImpl = new EdgeDetector2DProcessor(SobelKernels.SobelX, SobelKernels.SobelY, this.Grayscale); + processorImpl.Apply(frame, sourceRectangle, configuration); + } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs index 6de717afd9..0e744dd963 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs @@ -53,7 +53,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms Configuration configuration = source.GetConfiguration(); // Detect the edges. - new SobelProcessor(false).Apply(temp, sourceRectangle, configuration); + new SobelProcessor(false).ApplyToFrame(temp, sourceRectangle, configuration); // Apply threshold binarization filter. new BinaryThresholdProcessor(this.Threshold).Apply(temp, sourceRectangle, configuration); diff --git a/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs b/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs index cf5feac275..07b9b1b8c6 100644 --- a/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs +++ b/tests/ImageSharp.Tests/Processing/Convolution/DetectEdgesTest.cs @@ -21,8 +21,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution this.operations.DetectEdges(); // TODO: Enable once we have updated the images - // SobelProcessor processor = this.Verify>(); - // Assert.True(processor.Grayscale); + SobelProcessor processor = this.Verify(); + Assert.True(processor.Grayscale); } [Fact] @@ -31,45 +31,45 @@ namespace SixLabors.ImageSharp.Tests.Processing.Convolution this.operations.DetectEdges(this.rect); // TODO: Enable once we have updated the images - // SobelProcessor processor = this.Verify>(this.rect); - // Assert.True(processor.Grayscale); + SobelProcessor processor = this.Verify(this.rect); + Assert.True(processor.Grayscale); } public static IEnumerable EdgeDetectionTheoryData => new[] { - new object[]{ new TestType>(), EdgeDetectionOperators.Kayyali }, - new object[]{ new TestType>(), EdgeDetectionOperators.Kirsch }, - new object[]{ new TestType>(), EdgeDetectionOperators.Laplacian3x3 }, - new object[]{ new TestType>(), EdgeDetectionOperators.Laplacian5x5 }, - new object[]{ new TestType>(), EdgeDetectionOperators.LaplacianOfGaussian }, - new object[]{ new TestType>(), EdgeDetectionOperators.Prewitt }, - new object[]{ new TestType>(), EdgeDetectionOperators.RobertsCross }, - new object[]{ new TestType>(), EdgeDetectionOperators.Robinson }, - new object[]{ new TestType>(), EdgeDetectionOperators.Scharr }, - new object[]{ new TestType>(), EdgeDetectionOperators.Sobel }, + new object[]{ new TestType(), EdgeDetectionOperators.Kayyali }, + new object[]{ new TestType(), EdgeDetectionOperators.Kirsch }, + new object[]{ new TestType(), EdgeDetectionOperators.Laplacian3x3 }, + new object[]{ new TestType(), EdgeDetectionOperators.Laplacian5x5 }, + new object[]{ new TestType(), EdgeDetectionOperators.LaplacianOfGaussian }, + new object[]{ new TestType(), EdgeDetectionOperators.Prewitt }, + new object[]{ new TestType(), EdgeDetectionOperators.RobertsCross }, + new object[]{ new TestType(), EdgeDetectionOperators.Robinson }, + new object[]{ new TestType(), EdgeDetectionOperators.Scharr }, + new object[]{ new TestType(), EdgeDetectionOperators.Sobel }, }; [Theory] [MemberData(nameof(EdgeDetectionTheoryData))] public void DetectEdges_filter_SobelProcessorDefaultsSet(TestType type, EdgeDetectionOperators filter) - where TProcessor : IImageProcessor + where TProcessor : EdgeDetectorProcessor { this.operations.DetectEdges(filter); // TODO: Enable once we have updated the images - // var processor = this.Verify(); - // Assert.True(processor.Grayscale); + var processor = this.Verify(); + Assert.True(processor.Grayscale); } [Theory] [MemberData(nameof(EdgeDetectionTheoryData))] public void DetectEdges_filter_grayscale_SobelProcessorDefaultsSet(TestType type, EdgeDetectionOperators filter) - where TProcessor : IImageProcessor + where TProcessor : EdgeDetectorProcessor { bool grey = (int)filter % 2 == 0; this.operations.DetectEdges(filter, grey); // TODO: Enable once we have updated the images - // var processor = this.Verify() - // Assert.Equal(grey, processor.Grayscale); + var processor = this.Verify(); + Assert.Equal(grey, processor.Grayscale); } } } \ No newline at end of file From e53e0dc9c3da1e15c3c5861fc8ae892112a98b28 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 May 2019 01:36:44 +0200 Subject: [PATCH 19/31] refactor Effects processors --- .../Processing/OilPaintExtensions.cs | 31 ++--- .../Processing/PixelateExtensions.cs | 23 ++-- .../Processors/Dithering/ErrorDiffuserBase.cs | 6 +- .../Effects/OilPaintingProcessor.cs | 119 ++-------------- .../Effects/OilPaintingProcessor{TPixel}.cs | 129 ++++++++++++++++++ .../Processors/Effects/PixelateProcessor.cs | 91 +----------- .../Effects/PixelateProcessor{TPixel}.cs | 111 +++++++++++++++ .../Processing/Effects/OilPaintTest.cs | 8 +- .../Processing/Effects/PixelateTest.cs | 6 +- 9 files changed, 288 insertions(+), 236 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Effects/PixelateProcessor{TPixel}.cs diff --git a/src/ImageSharp/Processing/OilPaintExtensions.cs b/src/ImageSharp/Processing/OilPaintExtensions.cs index b6fa4149a6..ee51b31fab 100644 --- a/src/ImageSharp/Processing/OilPaintExtensions.cs +++ b/src/ImageSharp/Processing/OilPaintExtensions.cs @@ -1,14 +1,13 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Effects; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds oil painting effect extensions to the type. + /// Adds oil painting effect extensions to the type. /// public static class OilPaintExtensions { @@ -16,43 +15,36 @@ namespace SixLabors.ImageSharp.Processing /// Alters the colors of the image recreating an oil painting effect with levels and brushSize /// set to 10 and 15 respectively. /// - /// The pixel format. /// The image this method extends. /// The . - public static IImageProcessingContext OilPaint(this IImageProcessingContext source) - where TPixel : struct, IPixel - => OilPaint(source, 10, 15); + public static IImageProcessingContext OilPaint(this IImageProcessingContext source) => OilPaint(source, 10, 15); /// /// Alters the colors of the image recreating an oil painting effect with levels and brushSize /// set to 10 and 15 respectively. /// - /// The pixel format. /// The image this method extends. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext OilPaint(this IImageProcessingContext source, Rectangle rectangle) - where TPixel : struct, IPixel - => OilPaint(source, 10, 15, rectangle); + public static IImageProcessingContext OilPaint(this IImageProcessingContext source, Rectangle rectangle) => + OilPaint(source, 10, 15, rectangle); /// /// Alters the colors of the image recreating an oil painting effect. /// - /// The pixel format. /// The image this method extends. /// The number of intensity levels. Higher values result in a broader range of color intensities forming part of the result image. /// The number of neighboring pixels used in calculating each individual pixel value. /// The . - public static IImageProcessingContext OilPaint(this IImageProcessingContext source, int levels, int brushSize) - where TPixel : struct, IPixel - => source.ApplyProcessor(new OilPaintingProcessor(levels, brushSize)); + public static IImageProcessingContext + OilPaint(this IImageProcessingContext source, int levels, int brushSize) => + source.ApplyProcessor(new OilPaintingProcessor(levels, brushSize)); /// /// Alters the colors of the image recreating an oil painting effect. /// - /// The pixel format. /// The image this method extends. /// The number of intensity levels. Higher values result in a broader range of color intensities forming part of the result image. /// The number of neighboring pixels used in calculating each individual pixel value. @@ -60,8 +52,11 @@ namespace SixLabors.ImageSharp.Processing /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext OilPaint(this IImageProcessingContext source, int levels, int brushSize, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new OilPaintingProcessor(levels, brushSize), rectangle); + public static IImageProcessingContext OilPaint( + this IImageProcessingContext source, + int levels, + int brushSize, + Rectangle rectangle) => + source.ApplyProcessor(new OilPaintingProcessor(levels, brushSize), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/PixelateExtensions.cs b/src/ImageSharp/Processing/PixelateExtensions.cs index 4507f63923..d9f7f9be50 100644 --- a/src/ImageSharp/Processing/PixelateExtensions.cs +++ b/src/ImageSharp/Processing/PixelateExtensions.cs @@ -1,50 +1,45 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Effects; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds pixelation effect extensions to the type. + /// Adds pixelation effect extensions to the type. /// public static class PixelateExtensions { /// /// Pixelates an image with the given pixel size. /// - /// The pixel format. /// The image this method extends. /// The . - public static IImageProcessingContext Pixelate(this IImageProcessingContext source) - where TPixel : struct, IPixel - => Pixelate(source, 4); + public static IImageProcessingContext Pixelate(this IImageProcessingContext source) => Pixelate(source, 4); /// /// Pixelates an image with the given pixel size. /// - /// The pixel format. /// The image this method extends. /// The size of the pixels. /// The . - public static IImageProcessingContext Pixelate(this IImageProcessingContext source, int size) - where TPixel : struct, IPixel - => source.ApplyProcessor(new PixelateProcessor(size)); + public static IImageProcessingContext Pixelate(this IImageProcessingContext source, int size) => + source.ApplyProcessor(new PixelateProcessor(size)); /// /// Pixelates an image with the given pixel size. /// - /// The pixel format. /// The image this method extends. /// The size of the pixels. /// /// The structure that specifies the portion of the image object to alter. /// /// The . - public static IImageProcessingContext Pixelate(this IImageProcessingContext source, int size, Rectangle rectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new PixelateProcessor(size), rectangle); + public static IImageProcessingContext Pixelate( + this IImageProcessingContext source, + int size, + Rectangle rectangle) => + source.ApplyProcessor(new PixelateProcessor(size), rectangle); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuserBase.cs b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuserBase.cs index abf5dce184..327828c39e 100644 --- a/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuserBase.cs +++ b/src/ImageSharp/Processing/Processors/Dithering/ErrorDiffuserBase.cs @@ -21,12 +21,12 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering private readonly Vector4 divisorVector; /// - /// The matrix width + /// The matrix width. /// private readonly int matrixHeight; /// - /// The matrix height + /// The matrix height. /// private readonly int matrixWidth; @@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Dithering private readonly int startingOffset; /// - /// The diffusion matrix + /// The diffusion matrix. /// private readonly DenseMatrix matrix; diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs index 1b17c470ed..14e164fb77 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs @@ -1,27 +1,17 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Numerics; - -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Effects { /// - /// Applies oil painting effect processing to the image. + /// Defines an oil painting effect. /// - /// Adapted from by Dewald Esterhuizen. - /// The pixel format. - internal class OilPaintingProcessor : ImageProcessor - where TPixel : struct, IPixel + public class OilPaintingProcessor : IImageProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The number of intensity levels. Higher values result in a broader range of color intensities forming part of the result image. @@ -39,111 +29,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects } /// - /// Gets the intensity levels + /// Gets the number of intensity levels. /// public int Levels { get; } /// - /// Gets the brush size + /// Gets the brush size. /// public int BrushSize { get; } - /// - protected override void OnFrameApply( - ImageFrame source, - Rectangle sourceRectangle, - Configuration configuration) + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - if (this.BrushSize <= 0 || this.BrushSize > source.Height || this.BrushSize > source.Width) - { - throw new ArgumentOutOfRangeException(nameof(this.BrushSize)); - } - - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - int maxY = endY - 1; - int maxX = endX - 1; - - int radius = this.BrushSize >> 1; - int levels = this.Levels; - - using (Buffer2D targetPixels = configuration.MemoryAllocator.Allocate2D(source.Size())) - { - source.CopyTo(targetPixels); - - var workingRect = Rectangle.FromLTRB(startX, startY, endX, endY); - ParallelHelper.IterateRows( - workingRect, - configuration, - rows => - { - for (int y = rows.Min; y < rows.Max; y++) - { - Span sourceRow = source.GetPixelRowSpan(y); - Span targetRow = targetPixels.GetRowSpan(y); - - for (int x = startX; x < endX; x++) - { - int maxIntensity = 0; - int maxIndex = 0; - - int[] intensityBin = new int[levels]; - float[] redBin = new float[levels]; - float[] blueBin = new float[levels]; - float[] greenBin = new float[levels]; - - for (int fy = 0; fy <= radius; fy++) - { - int fyr = fy - radius; - int offsetY = y + fyr; - - offsetY = offsetY.Clamp(0, maxY); - - Span sourceOffsetRow = source.GetPixelRowSpan(offsetY); - - for (int fx = 0; fx <= radius; fx++) - { - int fxr = fx - radius; - int offsetX = x + fxr; - offsetX = offsetX.Clamp(0, maxX); - - var vector = sourceOffsetRow[offsetX].ToVector4(); - - float sourceRed = vector.X; - float sourceBlue = vector.Z; - float sourceGreen = vector.Y; - - int currentIntensity = (int)MathF.Round( - (sourceBlue + sourceGreen + sourceRed) / 3F * (levels - 1)); - - intensityBin[currentIntensity]++; - blueBin[currentIntensity] += sourceBlue; - greenBin[currentIntensity] += sourceGreen; - redBin[currentIntensity] += sourceRed; - - if (intensityBin[currentIntensity] > maxIntensity) - { - maxIntensity = intensityBin[currentIntensity]; - maxIndex = currentIntensity; - } - } - - float red = MathF.Abs(redBin[maxIndex] / maxIntensity); - float green = MathF.Abs(greenBin[maxIndex] / maxIntensity); - float blue = MathF.Abs(blueBin[maxIndex] / maxIntensity); - - ref TPixel pixel = ref targetRow[x]; - pixel.FromVector4( - new Vector4(red, green, blue, sourceRow[x].ToVector4().W)); - } - } - } - }); - - Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels); - } + return new OilPaintingProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs new file mode 100644 index 0000000000..9775e5ca93 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs @@ -0,0 +1,129 @@ +// Copyright (c) Six Labors and contributors. +/// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Effects +{ + /// + /// Applies oil painting effect processing to the image. + /// + /// Adapted from by Dewald Esterhuizen. + /// The pixel format. + internal class OilPaintingProcessor : ImageProcessor + where TPixel : struct, IPixel + { + private readonly OilPaintingProcessor definition; + + public OilPaintingProcessor(OilPaintingProcessor definition) + { + this.definition = definition; + } + + /// + protected override void OnFrameApply( + ImageFrame source, + Rectangle sourceRectangle, + Configuration configuration) + { + int brushSize = this.definition.BrushSize; + if (brushSize <= 0 || brushSize > source.Height || brushSize > source.Width) + { + throw new ArgumentOutOfRangeException(nameof(brushSize)); + } + + int startY = sourceRectangle.Y; + int endY = sourceRectangle.Bottom; + int startX = sourceRectangle.X; + int endX = sourceRectangle.Right; + int maxY = endY - 1; + int maxX = endX - 1; + + int radius = brushSize >> 1; + int levels = this.definition.Levels; + + using (Buffer2D targetPixels = configuration.MemoryAllocator.Allocate2D(source.Size())) + { + source.CopyTo(targetPixels); + + var workingRect = Rectangle.FromLTRB(startX, startY, endX, endY); + ParallelHelper.IterateRows( + workingRect, + configuration, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span sourceRow = source.GetPixelRowSpan(y); + Span targetRow = targetPixels.GetRowSpan(y); + + for (int x = startX; x < endX; x++) + { + int maxIntensity = 0; + int maxIndex = 0; + + int[] intensityBin = new int[levels]; + float[] redBin = new float[levels]; + float[] blueBin = new float[levels]; + float[] greenBin = new float[levels]; + + for (int fy = 0; fy <= radius; fy++) + { + int fyr = fy - radius; + int offsetY = y + fyr; + + offsetY = offsetY.Clamp(0, maxY); + + Span sourceOffsetRow = source.GetPixelRowSpan(offsetY); + + for (int fx = 0; fx <= radius; fx++) + { + int fxr = fx - radius; + int offsetX = x + fxr; + offsetX = offsetX.Clamp(0, maxX); + + var vector = sourceOffsetRow[offsetX].ToVector4(); + + float sourceRed = vector.X; + float sourceBlue = vector.Z; + float sourceGreen = vector.Y; + + int currentIntensity = (int)MathF.Round( + (sourceBlue + sourceGreen + sourceRed) / 3F * (levels - 1)); + + intensityBin[currentIntensity]++; + blueBin[currentIntensity] += sourceBlue; + greenBin[currentIntensity] += sourceGreen; + redBin[currentIntensity] += sourceRed; + + if (intensityBin[currentIntensity] > maxIntensity) + { + maxIntensity = intensityBin[currentIntensity]; + maxIndex = currentIntensity; + } + } + + float red = MathF.Abs(redBin[maxIndex] / maxIntensity); + float green = MathF.Abs(greenBin[maxIndex] / maxIntensity); + float blue = MathF.Abs(blueBin[maxIndex] / maxIntensity); + + ref TPixel pixel = ref targetRow[x]; + pixel.FromVector4( + new Vector4(red, green, blue, sourceRow[x].ToVector4().W)); + } + } + } + }); + + Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs index 50f76efed7..eeb2aafcb5 100644 --- a/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs @@ -1,25 +1,17 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Common; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Effects { /// - /// Applies a pixelation effect processing to the image. + /// Defines a pixelation effect of a given size. /// - /// The pixel format. - internal class PixelateProcessor : ImageProcessor - where TPixel : struct, IPixel + public class PixelateProcessor : IImageProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The size of the pixels. Must be greater than 0. /// @@ -36,80 +28,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects /// public int Size { get; } - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - if (this.Size <= 0 || this.Size > source.Height || this.Size > source.Width) - { - throw new ArgumentOutOfRangeException(nameof(this.Size)); - } - - int startY = sourceRectangle.Y; - int endY = sourceRectangle.Bottom; - int startX = sourceRectangle.X; - int endX = sourceRectangle.Right; - int size = this.Size; - int offset = this.Size / 2; - - // Align start/end positions. - int minX = Math.Max(0, startX); - int maxX = Math.Min(source.Width, endX); - int minY = Math.Max(0, startY); - int maxY = Math.Min(source.Height, endY); - - // Reset offset if necessary. - if (minX > 0) - { - startX = 0; - } - - if (minY > 0) - { - startY = 0; - } - - // Get the range on the y-plane to choose from. - IEnumerable range = EnumerableExtensions.SteppedRange(minY, i => i < maxY, size); - - Parallel.ForEach( - range, - configuration.GetParallelOptions(), - y => - { - int offsetY = y - startY; - int offsetPy = offset; - - // Make sure that the offset is within the boundary of the image. - while (offsetY + offsetPy >= maxY) - { - offsetPy--; - } - - Span row = source.GetPixelRowSpan(offsetY + offsetPy); - - for (int x = minX; x < maxX; x += size) - { - int offsetX = x - startX; - int offsetPx = offset; - - while (x + offsetPx >= maxX) - { - offsetPx--; - } - - // Get the pixel color in the centre of the soon to be pixelated area. - TPixel pixel = row[offsetX + offsetPx]; - - // For each pixel in the pixelate size, set it to the centre color. - for (int l = offsetY; l < offsetY + size && l < maxY; l++) - { - for (int k = offsetX; k < offsetX + size && k < maxX; k++) - { - source[k, l] = pixel; - } - } - } - }); + return new PixelateProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor{TPixel}.cs new file mode 100644 index 0000000000..21f3bb774d --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor{TPixel}.cs @@ -0,0 +1,111 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Common; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Effects +{ + /// + /// Applies a pixelation effect processing to the image. + /// + /// The pixel format. + internal class PixelateProcessor : ImageProcessor + where TPixel : struct, IPixel + { + private readonly PixelateProcessor definition; + + /// + /// Initializes a new instance of the class. + /// + /// The . + public PixelateProcessor(PixelateProcessor definition) + { + this.definition = definition; + } + + private int Size => this.definition.Size; + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + if (this.Size <= 0 || this.Size > source.Height || this.Size > source.Width) + { + throw new ArgumentOutOfRangeException(nameof(this.Size)); + } + + int startY = sourceRectangle.Y; + int endY = sourceRectangle.Bottom; + int startX = sourceRectangle.X; + int endX = sourceRectangle.Right; + int size = this.Size; + int offset = this.Size / 2; + + // Align start/end positions. + int minX = Math.Max(0, startX); + int maxX = Math.Min(source.Width, endX); + int minY = Math.Max(0, startY); + int maxY = Math.Min(source.Height, endY); + + // Reset offset if necessary. + if (minX > 0) + { + startX = 0; + } + + if (minY > 0) + { + startY = 0; + } + + // Get the range on the y-plane to choose from. + IEnumerable range = EnumerableExtensions.SteppedRange(minY, i => i < maxY, size); + + Parallel.ForEach( + range, + configuration.GetParallelOptions(), + y => + { + int offsetY = y - startY; + int offsetPy = offset; + + // Make sure that the offset is within the boundary of the image. + while (offsetY + offsetPy >= maxY) + { + offsetPy--; + } + + Span row = source.GetPixelRowSpan(offsetY + offsetPy); + + for (int x = minX; x < maxX; x += size) + { + int offsetX = x - startX; + int offsetPx = offset; + + while (x + offsetPx >= maxX) + { + offsetPx--; + } + + // Get the pixel color in the centre of the soon to be pixelated area. + TPixel pixel = row[offsetX + offsetPx]; + + // For each pixel in the pixelate size, set it to the centre color. + for (int l = offsetY; l < offsetY + size && l < maxY; l++) + { + for (int k = offsetX; k < offsetX + size && k < maxX; k++) + { + source[k, l] = pixel; + } + } + } + }); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Effects/OilPaintTest.cs b/tests/ImageSharp.Tests/Processing/Effects/OilPaintTest.cs index 9cd24fc6d1..6e1ee40f56 100644 --- a/tests/ImageSharp.Tests/Processing/Effects/OilPaintTest.cs +++ b/tests/ImageSharp.Tests/Processing/Effects/OilPaintTest.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects public void OilPaint_OilPaintingProcessorDefaultsSet() { this.operations.OilPaint(); - var processor = this.Verify>(); + var processor = this.Verify(); Assert.Equal(10, processor.Levels); Assert.Equal(15, processor.BrushSize); @@ -24,7 +24,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects public void OilPaint_rect_OilPaintingProcessorDefaultsSet() { this.operations.OilPaint(this.rect); - var processor = this.Verify>(this.rect); + var processor = this.Verify(this.rect); Assert.Equal(10, processor.Levels); Assert.Equal(15, processor.BrushSize); @@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects public void OilPaint_Levels_Brsuh_OilPaintingProcessorDefaultsSet() { this.operations.OilPaint(34, 65); - var processor = this.Verify>(); + var processor = this.Verify(); Assert.Equal(34, processor.Levels); Assert.Equal(65, processor.BrushSize); @@ -43,7 +43,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects public void OilPaint_Levels_Brsuh_rect_OilPaintingProcessorDefaultsSet() { this.operations.OilPaint(54, 43, this.rect); - var processor = this.Verify>(this.rect); + var processor = this.Verify(this.rect); Assert.Equal(54, processor.Levels); Assert.Equal(43, processor.BrushSize); diff --git a/tests/ImageSharp.Tests/Processing/Effects/PixelateTest.cs b/tests/ImageSharp.Tests/Processing/Effects/PixelateTest.cs index a93eaf0bc6..d8ce291707 100644 --- a/tests/ImageSharp.Tests/Processing/Effects/PixelateTest.cs +++ b/tests/ImageSharp.Tests/Processing/Effects/PixelateTest.cs @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects public void Pixelate_PixelateProcessorDefaultsSet() { this.operations.Pixelate(); - var processor = this.Verify>(); + var processor = this.Verify(); Assert.Equal(4, processor.Size); } @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects public void Pixelate_Size_PixelateProcessorDefaultsSet() { this.operations.Pixelate(12); - var processor = this.Verify>(); + var processor = this.Verify(); Assert.Equal(12, processor.Size); } @@ -32,7 +32,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Effects public void Pixelate_Size_rect_PixelateProcessorDefaultsSet() { this.operations.Pixelate(23, this.rect); - var processor = this.Verify>(this.rect); + var processor = this.Verify(this.rect); Assert.Equal(23, processor.Size); } From 3c7988cef6e14711661707ae3cccb81016b28161 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 May 2019 02:27:36 +0200 Subject: [PATCH 20/31] Finished refactoring transforms --- src/ImageSharp/Processing/CropExtensions.cs | 15 +- .../Processing/EntropyCropExtensions.cs | 19 +- src/ImageSharp/Processing/FlipExtensions.cs | 9 +- .../Effects/OilPaintingProcessor{TPixel}.cs | 2 +- .../Transforms/AffineTransformProcessor.cs | 106 +-------- .../AffineTransformProcessor{TPixel}.cs | 127 +++++++++++ .../Transforms/AutoOrientProcessor.cs | 92 +------- .../Transforms/AutoOrientProcessor{TPixel}.cs | 103 +++++++++ .../Processors/Transforms/CropProcessor.cs | 59 +---- .../Transforms/CropProcessor{TPixel}.cs | 75 ++++++ .../Transforms/EntropyCropProcessor.cs | 46 +--- .../EntropyCropProcessor{TPixel}.cs | 59 +++++ .../Processors/Transforms/FlipProcessor.cs | 76 +------ .../Transforms/FlipProcessor{TPixel}.cs | 87 +++++++ .../ProjectiveTransformProcessor.cs | 109 +-------- .../ProjectiveTransformProcessor{TPixel}.cs | 126 ++++++++++ .../Processors/Transforms/RotateProcessor.cs | 207 +---------------- .../Transforms/RotateProcessor{TPixel}.cs | 215 ++++++++++++++++++ .../Processors/Transforms/SkewProcessor.cs | 10 +- .../Transforms/TransformProcessorBase.cs | 2 +- .../Transforms/TransformProcessorHelpers.cs | 2 +- src/ImageSharp/Processing/RotateExtensions.cs | 24 +- .../Processing/RotateFlipExtensions.cs | 6 +- src/ImageSharp/Processing/SkewExtensions.cs | 24 +- .../Processing/TransformExtensions.cs | 97 ++++---- .../ImageSharp.Tests/Drawing/DrawImageTest.cs | 3 +- .../Processing/Transforms/CropTest.cs | 4 +- .../Processing/Transforms/EntropyCropTest.cs | 2 +- .../Processing/Transforms/FlipTests.cs | 2 +- .../Processing/Transforms/PadTest.cs | 8 +- .../Processing/Transforms/RotateFlipTests.cs | 4 +- .../Processing/Transforms/RotateTests.cs | 4 +- .../Processing/Transforms/SkewTest.cs | 2 +- .../Transforms/TransformsHelpersTest.cs | 2 +- 34 files changed, 953 insertions(+), 775 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs diff --git a/src/ImageSharp/Processing/CropExtensions.cs b/src/ImageSharp/Processing/CropExtensions.cs index 1c0d80afc9..6aaff5656f 100644 --- a/src/ImageSharp/Processing/CropExtensions.cs +++ b/src/ImageSharp/Processing/CropExtensions.cs @@ -1,40 +1,35 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Transforms; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of cropping operations to the type. + /// Adds extensions that allow the application of cropping operations to the type. /// public static class CropExtensions { /// /// Crops an image to the given width and height. /// - /// The pixel format. /// The image to resize. /// The target image width. /// The target image height. /// The - public static IImageProcessingContext Crop(this IImageProcessingContext source, int width, int height) - where TPixel : struct, IPixel - => Crop(source, new Rectangle(0, 0, width, height)); + public static IImageProcessingContext Crop(this IImageProcessingContext source, int width, int height) => + Crop(source, new Rectangle(0, 0, width, height)); /// /// Crops an image to the given rectangle. /// - /// The pixel format. /// The image to crop. /// /// The structure that specifies the portion of the image object to retain. /// /// The - public static IImageProcessingContext Crop(this IImageProcessingContext source, Rectangle cropRectangle) - where TPixel : struct, IPixel - => source.ApplyProcessor(new CropProcessor(cropRectangle, source.GetCurrentSize())); + public static IImageProcessingContext Crop(this IImageProcessingContext source, Rectangle cropRectangle) => + source.ApplyProcessor(new CropProcessor(cropRectangle, source.GetCurrentSize())); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/EntropyCropExtensions.cs b/src/ImageSharp/Processing/EntropyCropExtensions.cs index 157e69ef2a..34bc5daebf 100644 --- a/src/ImageSharp/Processing/EntropyCropExtensions.cs +++ b/src/ImageSharp/Processing/EntropyCropExtensions.cs @@ -1,35 +1,30 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Transforms; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of entropy cropping operations to the type. + /// Adds extensions that allow the application of entropy cropping operations to the type. /// public static class EntropyCropExtensions { /// /// Crops an image to the area of greatest entropy using a threshold for entropic density of .5F. /// - /// The pixel format. /// The image to crop. - /// The - public static IImageProcessingContext EntropyCrop(this IImageProcessingContext source) - where TPixel : struct, IPixel - => source.ApplyProcessor(new EntropyCropProcessor()); + /// The . + public static IImageProcessingContext EntropyCrop(this IImageProcessingContext source) => + source.ApplyProcessor(new EntropyCropProcessor()); /// /// Crops an image to the area of greatest entropy. /// - /// The pixel format. /// The image to crop. /// The threshold for entropic density. - /// The - public static IImageProcessingContext EntropyCrop(this IImageProcessingContext source, float threshold) - where TPixel : struct, IPixel - => source.ApplyProcessor(new EntropyCropProcessor(threshold)); + /// The . + public static IImageProcessingContext EntropyCrop(this IImageProcessingContext source, float threshold) => + source.ApplyProcessor(new EntropyCropProcessor(threshold)); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/FlipExtensions.cs b/src/ImageSharp/Processing/FlipExtensions.cs index dfbff7e4da..bc862972e5 100644 --- a/src/ImageSharp/Processing/FlipExtensions.cs +++ b/src/ImageSharp/Processing/FlipExtensions.cs @@ -1,25 +1,22 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Transforms; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of flipping operations to the type. + /// Adds extensions that allow the application of flipping operations to the type. /// public static class FlipExtensions { /// /// Flips an image by the given instructions. /// - /// The pixel format. /// The image to rotate, flip, or both. /// The to perform the flip. /// The - public static IImageProcessingContext Flip(this IImageProcessingContext source, FlipMode flipMode) - where TPixel : struct, IPixel - => source.ApplyProcessor(new FlipProcessor(flipMode)); + public static IImageProcessingContext Flip(this IImageProcessingContext source, FlipMode flipMode) + => source.ApplyProcessor(new FlipProcessor(flipMode)); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs index 9775e5ca93..7efb71fa1c 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor{TPixel}.cs @@ -1,5 +1,5 @@ // Copyright (c) Six Labors and contributors. -/// Licensed under the Apache License, Version 2.0. +// Licensed under the Apache License, Version 2.0. using System; using System.Numerics; diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs index 7633ed4418..713f042653 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor.cs @@ -1,26 +1,20 @@ // Copyright (c) Six Labors and contributors. // 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; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { /// - /// Provides the base methods to perform affine transforms on an image. + /// Defines an affine transformation applicable on an . /// - /// The pixel format. - internal class AffineTransformProcessor : TransformProcessorBase - where TPixel : struct, IPixel + public class AffineTransformProcessor : IImageProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The transform matrix. /// The sampler to perform the transform operation. @@ -48,95 +42,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// public Size TargetDimensions { get; } - /// - protected override Image CreateDestination(Image source, Rectangle sourceRectangle) + /// + public virtual IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = - source.Frames.Select(x => new ImageFrame(source.GetConfiguration(), this.TargetDimensions, x.Metadata.DeepClone())); - - // Use the overload to prevent an extra frame being added - return new Image(source.GetConfiguration(), source.Metadata.DeepClone(), frames); - } - - /// - protected override void OnFrameApply( - ImageFrame source, - ImageFrame destination, - Rectangle sourceRectangle, - Configuration configuration) - { - // Handle tranforms that result in output identical to the original. - 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); - - // Convert from screen to world space. - Matrix3x2.Invert(this.TransformMatrix, out Matrix3x2 matrix); - - if (this.Sampler is NearestNeighborResampler) - { - ParallelHelper.IterateRows( - targetBounds, - configuration, - rows => - { - for (int y = rows.Min; y < rows.Max; y++) - { - Span destRow = destination.GetPixelRowSpan(y); - - for (int x = 0; x < width; x++) - { - var point = Point.Transform(new Point(x, y), matrix); - if (sourceRectangle.Contains(point.X, point.Y)) - { - destRow[x] = source[point.X, point.Y]; - } - } - } - }); - - return; - } - - var kernel = new TransformKernelMap(configuration, source.Size(), destination.Size(), this.Sampler); - try - { - ParallelHelper.IterateRowsWithTempBuffer( - targetBounds, - configuration, - (rows, vectorBuffer) => - { - Span vectorSpan = vectorBuffer.Span; - for (int y = rows.Min; y < rows.Max; y++) - { - Span targetRowSpan = destination.GetPixelRowSpan(y); - PixelOperations.Instance.ToVector4(configuration, targetRowSpan, vectorSpan); - ref float ySpanRef = ref kernel.GetYStartReference(y); - ref float xSpanRef = ref kernel.GetXStartReference(y); - - for (int x = 0; x < width; x++) - { - // Use the single precision position to calculate correct bounding pixels - // otherwise we get rogue pixels outside of the bounds. - var point = Vector2.Transform(new Vector2(x, y), matrix); - kernel.Convolve(point, x, ref ySpanRef, ref xSpanRef, source.PixelBuffer, vectorSpan); - } - - PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan, targetRowSpan); - } - }); - } - finally - { - kernel.Dispose(); - } + return new AffineTransformProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs new file mode 100644 index 0000000000..e57ce826bd --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs @@ -0,0 +1,127 @@ +// Copyright (c) Six Labors and contributors. +// 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; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Transforms +{ + /// + /// Provides the base methods to perform affine transforms on an image. + /// + /// The pixel format. + internal class AffineTransformProcessor : TransformProcessorBase + where TPixel : struct, IPixel + { + public AffineTransformProcessor(AffineTransformProcessor definition) + { + this.Definition = definition; + } + + private Size TargetDimensions => this.Definition.TargetDimensions; + + private Matrix3x2 TransformMatrix => this.Definition.TransformMatrix; + + protected AffineTransformProcessor Definition { get; } + + /// + protected override Image CreateDestination(Image source, Rectangle sourceRectangle) + { + // We will always be creating the clone even for mutate because we may need to resize the canvas + IEnumerable> frames = + source.Frames.Select(x => new ImageFrame(source.GetConfiguration(), this.TargetDimensions, x.Metadata.DeepClone())); + + // Use the overload to prevent an extra frame being added + return new Image(source.GetConfiguration(), source.Metadata.DeepClone(), frames); + } + + /// + protected override void OnFrameApply( + ImageFrame source, + ImageFrame destination, + Rectangle sourceRectangle, + Configuration configuration) + { + // Handle tranforms that result in output identical to the original. + 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); + + // Convert from screen to world space. + Matrix3x2.Invert(this.TransformMatrix, out Matrix3x2 matrix); + + var sampler = this.Definition.Sampler; + + if (sampler is NearestNeighborResampler) + { + ParallelHelper.IterateRows( + targetBounds, + configuration, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span destRow = destination.GetPixelRowSpan(y); + + for (int x = 0; x < width; x++) + { + var point = Point.Transform(new Point(x, y), matrix); + if (sourceRectangle.Contains(point.X, point.Y)) + { + destRow[x] = source[point.X, point.Y]; + } + } + } + }); + + return; + } + + var kernel = new TransformKernelMap(configuration, source.Size(), destination.Size(), sampler); + try + { + ParallelHelper.IterateRowsWithTempBuffer( + targetBounds, + configuration, + (rows, vectorBuffer) => + { + Span vectorSpan = vectorBuffer.Span; + for (int y = rows.Min; y < rows.Max; y++) + { + Span targetRowSpan = destination.GetPixelRowSpan(y); + PixelOperations.Instance.ToVector4(configuration, targetRowSpan, vectorSpan); + ref float ySpanRef = ref kernel.GetYStartReference(y); + ref float xSpanRef = ref kernel.GetXStartReference(y); + + for (int x = 0; x < width; x++) + { + // Use the single precision position to calculate correct bounding pixels + // otherwise we get rogue pixels outside of the bounds. + var point = Vector2.Transform(new Vector2(x, y), matrix); + kernel.Convolve(point, x, ref ySpanRef, ref xSpanRef, source.PixelBuffer, vectorSpan); + } + + PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan, targetRowSpan); + } + }); + } + finally + { + kernel.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs index 5b9e3dde23..9bbbba843c 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor.cs @@ -1,103 +1,21 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { /// /// Adjusts an image so that its orientation is suitable for viewing. Adjustments are based on EXIF metadata embedded in the image. /// - /// The pixel format. - internal class AutoOrientProcessor : ImageProcessor - where TPixel : struct, IPixel + public sealed class AutoOrientProcessor : IImageProcessor { - /// - protected override void BeforeImageApply(Image source, Rectangle sourceRectangle) + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - OrientationMode orientation = GetExifOrientation(source); - Size size = sourceRectangle.Size; - switch (orientation) - { - case OrientationMode.TopRight: - new FlipProcessor(FlipMode.Horizontal).Apply(source, sourceRectangle); - break; - - case OrientationMode.BottomRight: - new RotateProcessor((int)RotateMode.Rotate180, size).Apply(source, sourceRectangle); - break; - - case OrientationMode.BottomLeft: - new FlipProcessor(FlipMode.Vertical).Apply(source, sourceRectangle); - break; - - case OrientationMode.LeftTop: - new RotateProcessor((int)RotateMode.Rotate90, size).Apply(source, sourceRectangle); - new FlipProcessor(FlipMode.Horizontal).Apply(source, sourceRectangle); - break; - - case OrientationMode.RightTop: - new RotateProcessor((int)RotateMode.Rotate90, size).Apply(source, sourceRectangle); - break; - - case OrientationMode.RightBottom: - new FlipProcessor(FlipMode.Vertical).Apply(source, sourceRectangle); - new RotateProcessor((int)RotateMode.Rotate270, size).Apply(source, sourceRectangle); - break; - - case OrientationMode.LeftBottom: - new RotateProcessor((int)RotateMode.Rotate270, size).Apply(source, sourceRectangle); - break; - - case OrientationMode.Unknown: - case OrientationMode.TopLeft: - default: - break; - } - } - - /// - protected override void OnFrameApply(ImageFrame sourceBase, Rectangle sourceRectangle, Configuration config) - { - // All processing happens at the image level within BeforeImageApply(); - } - - /// - /// Returns the current EXIF orientation - /// - /// The image to auto rotate. - /// The - private static OrientationMode GetExifOrientation(Image source) - { - if (source.Metadata.ExifProfile is null) - { - return OrientationMode.Unknown; - } - - ExifValue value = source.Metadata.ExifProfile.GetValue(ExifTag.Orientation); - if (value is null) - { - return OrientationMode.Unknown; - } - - OrientationMode orientation; - if (value.DataType == ExifDataType.Short) - { - orientation = (OrientationMode)value.Value; - } - else - { - orientation = (OrientationMode)Convert.ToUInt16(value.Value); - source.Metadata.ExifProfile.RemoveValue(ExifTag.Orientation); - } - - source.Metadata.ExifProfile.SetValue(ExifTag.Orientation, (ushort)OrientationMode.TopLeft); - - return orientation; + return new AutoOrientProcessor(); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs new file mode 100644 index 0000000000..257c223dc4 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs @@ -0,0 +1,103 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +using SixLabors.ImageSharp.Metadata.Profiles.Exif; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Transforms +{ + /// + /// Adjusts an image so that its orientation is suitable for viewing. Adjustments are based on EXIF metadata embedded in the image. + /// + /// The pixel format. + internal class AutoOrientProcessor : ImageProcessor + where TPixel : struct, IPixel + { + /// + protected override void BeforeImageApply(Image source, Rectangle sourceRectangle) + { + OrientationMode orientation = GetExifOrientation(source); + Size size = sourceRectangle.Size; + switch (orientation) + { + case OrientationMode.TopRight: + new FlipProcessor(FlipMode.Horizontal).Apply(source, sourceRectangle); + break; + + case OrientationMode.BottomRight: + new RotateProcessor((int)RotateMode.Rotate180, size).Apply(source, sourceRectangle); + break; + + case OrientationMode.BottomLeft: + new FlipProcessor(FlipMode.Vertical).Apply(source, sourceRectangle); + break; + + case OrientationMode.LeftTop: + new RotateProcessor((int)RotateMode.Rotate90, size).Apply(source, sourceRectangle); + new FlipProcessor(FlipMode.Horizontal).Apply(source, sourceRectangle); + break; + + case OrientationMode.RightTop: + new RotateProcessor((int)RotateMode.Rotate90, size).Apply(source, sourceRectangle); + break; + + case OrientationMode.RightBottom: + new FlipProcessor(FlipMode.Vertical).Apply(source, sourceRectangle); + new RotateProcessor((int)RotateMode.Rotate270, size).Apply(source, sourceRectangle); + break; + + case OrientationMode.LeftBottom: + new RotateProcessor((int)RotateMode.Rotate270, size).Apply(source, sourceRectangle); + break; + + case OrientationMode.Unknown: + case OrientationMode.TopLeft: + default: + break; + } + } + + /// + protected override void OnFrameApply(ImageFrame sourceBase, Rectangle sourceRectangle, Configuration config) + { + // All processing happens at the image level within BeforeImageApply(); + } + + /// + /// Returns the current EXIF orientation + /// + /// The image to auto rotate. + /// The + private static OrientationMode GetExifOrientation(Image source) + { + if (source.Metadata.ExifProfile is null) + { + return OrientationMode.Unknown; + } + + ExifValue value = source.Metadata.ExifProfile.GetValue(ExifTag.Orientation); + if (value is null) + { + return OrientationMode.Unknown; + } + + OrientationMode orientation; + if (value.DataType == ExifDataType.Short) + { + orientation = (OrientationMode)value.Value; + } + else + { + orientation = (OrientationMode)Convert.ToUInt16(value.Value); + source.Metadata.ExifProfile.RemoveValue(ExifTag.Orientation); + } + + source.Metadata.ExifProfile.SetValue(ExifTag.Orientation, (ushort)OrientationMode.TopLeft); + + return orientation; + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs index 5baa196a09..76f223e038 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor.cs @@ -1,32 +1,28 @@ // Copyright (c) Six Labors and contributors. // 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; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { /// - /// Provides methods to allow the cropping of an image. + /// Defines a crop operation on an image. /// - /// The pixel format. - internal class CropProcessor : TransformProcessorBase - where TPixel : struct, IPixel + public sealed class CropProcessor : IImageProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The target cropped rectangle. /// The source image size. public CropProcessor(Rectangle cropRectangle, Size sourceSize) { // Check bounds here and throw if we are passed a rectangle exceeding our source bounds. - Guard.IsTrue(new Rectangle(Point.Empty, sourceSize).Contains(cropRectangle), nameof(cropRectangle), "Crop rectangle should be smaller than the source bounds."); + Guard.IsTrue( + new Rectangle(Point.Empty, sourceSize).Contains(cropRectangle), + nameof(cropRectangle), + "Crop rectangle should be smaller than the source bounds."); this.CropRectangle = cropRectangle; } @@ -35,44 +31,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// public Rectangle CropRectangle { get; } - /// - protected override Image CreateDestination(Image source, Rectangle sourceRectangle) + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = source.Frames.Select(x => new ImageFrame(source.GetConfiguration(), this.CropRectangle.Width, this.CropRectangle.Height, x.Metadata.DeepClone())); - - // Use the overload to prevent an extra frame being added - return new Image(source.GetConfiguration(), source.Metadata.DeepClone(), frames); - } - - /// - protected override void OnFrameApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) - { - // Handle resize dimensions identical to the original - if (source.Width == destination.Width && source.Height == destination.Height && 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; - - // Copying is cheap, we should process more pixels per task: - ParallelExecutionSettings parallelSettings = configuration.GetParallelSettings().MultiplyMinimumPixelsPerTask(4); - - ParallelHelper.IterateRows( - rect, - 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); - } - }); + return new CropProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs new file mode 100644 index 0000000000..9bddda3825 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/CropProcessor{TPixel}.cs @@ -0,0 +1,75 @@ +// Copyright (c) Six Labors and contributors. +// 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; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Transforms +{ + /// + /// Provides methods to allow the cropping of an image. + /// + /// The pixel format. + internal class CropProcessor : TransformProcessorBase + where TPixel : struct, IPixel + { + private readonly CropProcessor definition; + + /// + /// Initializes a new instance of the class. + /// + /// The . + public CropProcessor(CropProcessor definition) + { + this.definition = definition; + } + + private Rectangle CropRectangle => this.definition.CropRectangle; + + /// + protected override Image CreateDestination(Image source, Rectangle sourceRectangle) + { + // We will always be creating the clone even for mutate because we may need to resize the canvas + IEnumerable> frames = source.Frames.Select(x => new ImageFrame(source.GetConfiguration(), this.CropRectangle.Width, this.CropRectangle.Height, x.Metadata.DeepClone())); + + // Use the overload to prevent an extra frame being added + return new Image(source.GetConfiguration(), source.Metadata.DeepClone(), frames); + } + + /// + protected override void OnFrameApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) + { + // Handle resize dimensions identical to the original + if (source.Width == destination.Width && source.Height == destination.Height && 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; + + // Copying is cheap, we should process more pixels per task: + ParallelExecutionSettings parallelSettings = configuration.GetParallelSettings().MultiplyMinimumPixelsPerTask(4); + + ParallelHelper.IterateRows( + rect, + 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); + } + }); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs index 0e744dd963..dee5e7fb37 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor.cs @@ -1,31 +1,25 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.ImageSharp.Processing.Processors.Binarization; -using SixLabors.ImageSharp.Processing.Processors.Convolution; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { /// - /// Provides methods to allow the cropping of an image to preserve areas of highest entropy. + /// Defines cropping operation that preserves areas of highest entropy. /// - /// The pixel format. - internal class EntropyCropProcessor : ImageProcessor - where TPixel : struct, IPixel + public sealed class EntropyCropProcessor : IImageProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// public EntropyCropProcessor() - : this(.5F) + : this(.5F) { } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The threshold to split the image. Must be between 0 and 1. /// @@ -42,33 +36,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// public float Threshold { get; } - /// - protected override void BeforeImageApply(Image source, Rectangle sourceRectangle) + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - Rectangle rectangle; - - // All frames have be the same size so we only need to calculate the correct dimensions for the first frame - using (ImageFrame temp = source.Frames.RootFrame.Clone()) - { - Configuration configuration = source.GetConfiguration(); - - // Detect the edges. - new SobelProcessor(false).ApplyToFrame(temp, sourceRectangle, configuration); - - // Apply threshold binarization filter. - new BinaryThresholdProcessor(this.Threshold).Apply(temp, sourceRectangle, configuration); - - // Search for the first white pixels - rectangle = ImageMaths.GetFilteredBoundingRectangle(temp, 0); - } - - new CropProcessor(rectangle, source.Size()).Apply(source, sourceRectangle); - } - - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - { - // All processing happens at the image level within BeforeImageApply(); + return new EntropyCropProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs new file mode 100644 index 0000000000..eaeb6939ec --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/EntropyCropProcessor{TPixel}.cs @@ -0,0 +1,59 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Processors.Binarization; +using SixLabors.ImageSharp.Processing.Processors.Convolution; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Transforms +{ + /// + /// Provides methods to allow the cropping of an image to preserve areas of highest entropy. + /// + /// The pixel format. + internal class EntropyCropProcessor : ImageProcessor + where TPixel : struct, IPixel + { + private readonly EntropyCropProcessor definition; + + /// + /// Initializes a new instance of the class. + /// + /// The . + public EntropyCropProcessor(EntropyCropProcessor definition) + { + this.definition = definition; + } + + /// + protected override void BeforeImageApply(Image source, Rectangle sourceRectangle) + { + Rectangle rectangle; + + // All frames have be the same size so we only need to calculate the correct dimensions for the first frame + using (ImageFrame temp = source.Frames.RootFrame.Clone()) + { + Configuration configuration = source.GetConfiguration(); + + // Detect the edges. + new SobelProcessor(false).ApplyToFrame(temp, sourceRectangle, configuration); + + // Apply threshold binarization filter. + new BinaryThresholdProcessor(this.definition.Threshold).Apply(temp, sourceRectangle, configuration); + + // Search for the first white pixels + rectangle = ImageMaths.GetFilteredBoundingRectangle(temp, 0); + } + + new CropProcessor(rectangle, source.Size()).Apply(source, sourceRectangle); + } + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + // All processing happens at the image level within BeforeImageApply(); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs index c6f5e9d7b8..9a3eab6cc8 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs @@ -1,27 +1,17 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Buffers; -using System.Threading.Tasks; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { /// - /// Provides methods that allow the flipping of an image around its center point. + /// Defines a flipping around the center point of the image. /// - /// The pixel format. - internal class FlipProcessor : ImageProcessor - where TPixel : struct, IPixel + public class FlipProcessor : IImageProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The used to perform flipping. public FlipProcessor(FlipMode flipMode) @@ -34,63 +24,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// public FlipMode FlipMode { get; } - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - switch (this.FlipMode) - { - // No default needed as we have already set the pixels. - case FlipMode.Vertical: - this.FlipX(source, configuration); - break; - case FlipMode.Horizontal: - this.FlipY(source, configuration); - break; - } - } - - /// - /// Swaps the image at the X-axis, which goes horizontally through the middle at half the height of the image. - /// - /// The source image to apply the process to. - /// The configuration. - private void FlipX(ImageFrame source, Configuration configuration) - { - int height = source.Height; - - using (IMemoryOwner tempBuffer = configuration.MemoryAllocator.Allocate(source.Width)) - { - Span temp = tempBuffer.Memory.Span; - - for (int yTop = 0; yTop < height / 2; yTop++) - { - int yBottom = height - yTop - 1; - Span topRow = source.GetPixelRowSpan(yBottom); - Span bottomRow = source.GetPixelRowSpan(yTop); - topRow.CopyTo(temp); - bottomRow.CopyTo(topRow); - temp.CopyTo(bottomRow); - } - } - } - - /// - /// Swaps the image at the Y-axis, which goes vertically through the middle at half of the width of the image. - /// - /// The source image to apply the process to. - /// The configuration. - private void FlipY(ImageFrame source, Configuration configuration) - { - ParallelHelper.IterateRows( - source.Bounds(), - configuration, - rows => - { - for (int y = rows.Min; y < rows.Max; y++) - { - source.GetPixelRowSpan(y).Reverse(); - } - }); + return new FlipProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs new file mode 100644 index 0000000000..0247862096 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor{TPixel}.cs @@ -0,0 +1,87 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Transforms +{ + /// + /// Provides methods that allow the flipping of an image around its center point. + /// + /// The pixel format. + internal class FlipProcessor : ImageProcessor + where TPixel : struct, IPixel + { + private readonly FlipProcessor definition; + + public FlipProcessor(FlipProcessor definition) + { + this.definition = definition; + } + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + switch (this.definition.FlipMode) + { + // No default needed as we have already set the pixels. + case FlipMode.Vertical: + this.FlipX(source, configuration); + break; + case FlipMode.Horizontal: + this.FlipY(source, configuration); + break; + } + } + + /// + /// Swaps the image at the X-axis, which goes horizontally through the middle at half the height of the image. + /// + /// The source image to apply the process to. + /// The configuration. + private void FlipX(ImageFrame source, Configuration configuration) + { + int height = source.Height; + + using (IMemoryOwner tempBuffer = configuration.MemoryAllocator.Allocate(source.Width)) + { + Span temp = tempBuffer.Memory.Span; + + for (int yTop = 0; yTop < height / 2; yTop++) + { + int yBottom = height - yTop - 1; + Span topRow = source.GetPixelRowSpan(yBottom); + Span bottomRow = source.GetPixelRowSpan(yTop); + topRow.CopyTo(temp); + bottomRow.CopyTo(topRow); + temp.CopyTo(bottomRow); + } + } + } + + /// + /// Swaps the image at the Y-axis, which goes vertically through the middle at half of the width of the image. + /// + /// The source image to apply the process to. + /// The configuration. + private void FlipY(ImageFrame source, Configuration configuration) + { + ParallelHelper.IterateRows( + source.Bounds(), + configuration, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + source.GetPixelRowSpan(y).Reverse(); + } + }); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs index 6c7271c5ec..3a86b3fe4f 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor.cs @@ -1,26 +1,20 @@ // Copyright (c) Six Labors and contributors. // 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; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { /// - /// Provides the base methods to perform non-affine transforms on an image. + /// Defines a projective transformation applicable to an . /// - /// The pixel format. - internal class ProjectiveTransformProcessor : TransformProcessorBase - where TPixel : struct, IPixel + public sealed class ProjectiveTransformProcessor : IImageProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The transform matrix. /// The sampler to perform the transform operation. @@ -39,103 +33,20 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms public IResampler Sampler { get; } /// - /// Gets the matrix used to supply the projective transform + /// Gets the matrix used to supply the projective transform. /// public Matrix4x4 TransformMatrix { get; } /// - /// Gets the target dimensions to constrain the transformed image to + /// Gets the target dimensions to constrain the transformed image to. /// public Size TargetDimensions { get; } - /// - protected override Image CreateDestination(Image source, Rectangle sourceRectangle) + /// + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = - source.Frames.Select(x => new ImageFrame(source.GetConfiguration(), this.TargetDimensions.Width, this.TargetDimensions.Height, x.Metadata.DeepClone())); - - // Use the overload to prevent an extra frame being added - return new Image(source.GetConfiguration(), source.Metadata.DeepClone(), frames); - } - - /// - protected override void OnFrameApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) - { - // Handle tranforms that result in output identical to the original. - 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); - - // Convert from screen to world space. - Matrix4x4.Invert(this.TransformMatrix, out Matrix4x4 matrix); - - if (this.Sampler is NearestNeighborResampler) - { - ParallelHelper.IterateRows( - targetBounds, - configuration, - rows => - { - for (int y = rows.Min; y < rows.Max; y++) - { - Span destRow = destination.GetPixelRowSpan(y); - - for (int x = 0; x < width; x++) - { - Vector2 point = TransformUtils.ProjectiveTransform2D(x, y, matrix); - int px = (int)MathF.Round(point.X); - int py = (int)MathF.Round(point.Y); - - if (sourceRectangle.Contains(px, py)) - { - destRow[x] = source[px, py]; - } - } - } - }); - - return; - } - - var kernel = new TransformKernelMap(configuration, source.Size(), destination.Size(), this.Sampler); - try - { - ParallelHelper.IterateRowsWithTempBuffer( - targetBounds, - configuration, - (rows, vectorBuffer) => - { - Span vectorSpan = vectorBuffer.Span; - for (int y = rows.Min; y < rows.Max; y++) - { - Span targetRowSpan = destination.GetPixelRowSpan(y); - PixelOperations.Instance.ToVector4(configuration, targetRowSpan, vectorSpan); - ref float ySpanRef = ref kernel.GetYStartReference(y); - ref float xSpanRef = ref kernel.GetXStartReference(y); - - for (int x = 0; x < width; x++) - { - // Use the single precision position to calculate correct bounding pixels - // otherwise we get rogue pixels outside of the bounds. - Vector2 point = TransformUtils.ProjectiveTransform2D(x, y, matrix); - kernel.Convolve(point, x, ref ySpanRef, ref xSpanRef, source.PixelBuffer, vectorSpan); - } - - PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan, targetRowSpan); - } - }); - } - finally - { - kernel.Dispose(); - } + return new ProjectiveTransformProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs new file mode 100644 index 0000000000..e6d885803b --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs @@ -0,0 +1,126 @@ +// Copyright (c) Six Labors and contributors. +// 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; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Transforms +{ + /// + /// Provides the base methods to perform non-affine transforms on an image. + /// + /// The pixel format. + internal class ProjectiveTransformProcessor : TransformProcessorBase + where TPixel : struct, IPixel + { + private readonly ProjectiveTransformProcessor definition; + + public ProjectiveTransformProcessor(ProjectiveTransformProcessor definition) + { + this.definition = definition; + } + + private Size TargetDimensions => this.definition.TargetDimensions; + + /// + protected override Image CreateDestination(Image source, Rectangle sourceRectangle) + { + // We will always be creating the clone even for mutate because we may need to resize the canvas + IEnumerable> frames = + source.Frames.Select(x => new ImageFrame(source.GetConfiguration(), this.TargetDimensions.Width, this.TargetDimensions.Height, x.Metadata.DeepClone())); + + // Use the overload to prevent an extra frame being added + return new Image(source.GetConfiguration(), source.Metadata.DeepClone(), frames); + } + + /// + protected override void OnFrameApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) + { + Matrix4x4 transformMatrix = this.definition.TransformMatrix; + + // Handle tranforms that result in output identical to the original. + if (transformMatrix.Equals(default) || 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); + + // Convert from screen to world space. + Matrix4x4.Invert(transformMatrix, out Matrix4x4 matrix); + + IResampler sampler = this.definition.Sampler; + + if (sampler is NearestNeighborResampler) + { + ParallelHelper.IterateRows( + targetBounds, + configuration, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span destRow = destination.GetPixelRowSpan(y); + + for (int x = 0; x < width; x++) + { + Vector2 point = TransformUtils.ProjectiveTransform2D(x, y, matrix); + int px = (int)MathF.Round(point.X); + int py = (int)MathF.Round(point.Y); + + if (sourceRectangle.Contains(px, py)) + { + destRow[x] = source[px, py]; + } + } + } + }); + + return; + } + + var kernel = new TransformKernelMap(configuration, source.Size(), destination.Size(), sampler); + try + { + ParallelHelper.IterateRowsWithTempBuffer( + targetBounds, + configuration, + (rows, vectorBuffer) => + { + Span vectorSpan = vectorBuffer.Span; + for (int y = rows.Min; y < rows.Max; y++) + { + Span targetRowSpan = destination.GetPixelRowSpan(y); + PixelOperations.Instance.ToVector4(configuration, targetRowSpan, vectorSpan); + ref float ySpanRef = ref kernel.GetYStartReference(y); + ref float xSpanRef = ref kernel.GetXStartReference(y); + + for (int x = 0; x < width; x++) + { + // Use the single precision position to calculate correct bounding pixels + // otherwise we get rogue pixels outside of the bounds. + Vector2 point = TransformUtils.ProjectiveTransform2D(x, y, matrix); + kernel.Convolve(point, x, ref ySpanRef, ref xSpanRef, source.PixelBuffer, vectorSpan); + } + + PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan, targetRowSpan); + } + }); + } + finally + { + kernel.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs index 57902a5e6d..ef0671d20d 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor.cs @@ -1,26 +1,19 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; using System.Numerics; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Metadata.Profiles.Exif; -using SixLabors.ImageSharp.ParallelUtils; -using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { /// - /// Provides methods that allow the rotating of images. + /// Defines a rotation applicable to an . /// - /// The pixel format. - internal class RotateProcessor : AffineTransformProcessor - where TPixel : struct, IPixel + public sealed class RotateProcessor : AffineTransformProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The angle of rotation in degrees. /// The source image size @@ -30,16 +23,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The angle of rotation in degrees. /// The sampler to perform the rotating operation. /// The source image size public RotateProcessor(float degrees, IResampler sampler, Size sourceSize) : this( - TransformUtils.CreateRotationMatrixDegrees(degrees, sourceSize), - sampler, - sourceSize) + TransformUtils.CreateRotationMatrixDegrees(degrees, sourceSize), + sampler, + sourceSize) => this.Degrees = degrees; // Helper constructor @@ -53,190 +46,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// public float Degrees { get; } - /// - protected override void OnFrameApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) + /// + public override IImageProcessor CreatePixelSpecificProcessor() { - if (this.OptimizedApply(source, destination, configuration)) - { - return; - } - - base.OnFrameApply(source, destination, sourceRectangle, configuration); - } - - /// - protected override void AfterImageApply(Image source, Image destination, Rectangle sourceRectangle) - { - ExifProfile profile = destination.Metadata.ExifProfile; - if (profile is null) - { - return; - } - - if (MathF.Abs(WrapDegrees(this.Degrees)) < Constants.Epsilon) - { - // No need to do anything so return. - return; - } - - profile.RemoveValue(ExifTag.Orientation); - - base.AfterImageApply(source, destination, sourceRectangle); - } - - /// - /// Wraps a given angle in degrees so that it falls withing the 0-360 degree range - /// - /// The angle of rotation in degrees. - /// The - private static float WrapDegrees(float degrees) - { - degrees %= 360; - - while (degrees < 0) - { - degrees += 360; - } - - return degrees; - } - - /// - /// Rotates the images with an optimized method when the angle is 90, 180 or 270 degrees. - /// - /// The source image. - /// The destination image. - /// The configuration. - /// - /// The - /// - private bool OptimizedApply(ImageFrame source, ImageFrame destination, Configuration configuration) - { - // Wrap the degrees to keep within 0-360 so we can apply optimizations when possible. - float degrees = WrapDegrees(this.Degrees); - - if (MathF.Abs(degrees) < Constants.Epsilon) - { - // The destination will be blank here so copy all the pixel data over - source.GetPixelSpan().CopyTo(destination.GetPixelSpan()); - return true; - } - - if (MathF.Abs(degrees - 90) < Constants.Epsilon) - { - this.Rotate90(source, destination, configuration); - return true; - } - - if (MathF.Abs(degrees - 180) < Constants.Epsilon) - { - this.Rotate180(source, destination, configuration); - return true; - } - - if (MathF.Abs(degrees - 270) < Constants.Epsilon) - { - this.Rotate270(source, destination, configuration); - return true; - } - - return false; - } - - /// - /// Rotates the image 270 degrees clockwise at the centre point. - /// - /// The source image. - /// The destination image. - /// The configuration. - private void Rotate270(ImageFrame source, ImageFrame destination, Configuration configuration) - { - int width = source.Width; - int height = source.Height; - Rectangle destinationBounds = destination.Bounds(); - - ParallelHelper.IterateRows( - source.Bounds(), - configuration, - rows => - { - for (int y = rows.Min; y < rows.Max; y++) - { - Span sourceRow = source.GetPixelRowSpan(y); - for (int x = 0; x < width; x++) - { - int newX = height - y - 1; - newX = height - newX - 1; - int newY = width - x - 1; - - if (destinationBounds.Contains(newX, newY)) - { - destination[newX, newY] = sourceRow[x]; - } - } - } - }); - } - - /// - /// Rotates the image 180 degrees clockwise at the centre point. - /// - /// The source image. - /// The destination image. - /// The configuration. - private void Rotate180(ImageFrame source, ImageFrame destination, Configuration configuration) - { - int width = source.Width; - int height = source.Height; - - ParallelHelper.IterateRows( - source.Bounds(), - configuration, - rows => - { - for (int y = rows.Min; y < rows.Max; y++) - { - Span sourceRow = source.GetPixelRowSpan(y); - Span targetRow = destination.GetPixelRowSpan(height - y - 1); - - for (int x = 0; x < width; x++) - { - targetRow[width - x - 1] = sourceRow[x]; - } - } - }); - } - - /// - /// Rotates the image 90 degrees clockwise at the centre point. - /// - /// The source image. - /// The destination image. - /// The configuration. - private void Rotate90(ImageFrame source, ImageFrame destination, Configuration configuration) - { - int width = source.Width; - int height = source.Height; - Rectangle destinationBounds = destination.Bounds(); - - ParallelHelper.IterateRows( - source.Bounds(), - configuration, - rows => - { - for (int y = rows.Min; y < rows.Max; y++) - { - Span sourceRow = source.GetPixelRowSpan(y); - int newX = height - y - 1; - for (int x = 0; x < width; x++) - { - if (destinationBounds.Contains(newX, x)) - { - destination[newX, x] = sourceRow[x]; - } - } - } - }); + return new RotateProcessor(this); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs new file mode 100644 index 0000000000..aac6f65148 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs @@ -0,0 +1,215 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; +using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Transforms +{ + /// + /// Provides methods that allow the rotating of images. + /// + /// The pixel format. + internal class RotateProcessor : AffineTransformProcessor + where TPixel : struct, IPixel + { + public RotateProcessor(RotateProcessor definition) + : base(definition) + { + this.Degrees = definition.Degrees; + } + + private float Degrees { get; } + + /// + protected override void OnFrameApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) + { + if (this.OptimizedApply(source, destination, configuration)) + { + return; + } + + base.OnFrameApply(source, destination, sourceRectangle, configuration); + } + + /// + protected override void AfterImageApply(Image source, Image destination, Rectangle sourceRectangle) + { + ExifProfile profile = destination.Metadata.ExifProfile; + if (profile is null) + { + return; + } + + if (MathF.Abs(WrapDegrees(this.Degrees)) < Constants.Epsilon) + { + // No need to do anything so return. + return; + } + + profile.RemoveValue(ExifTag.Orientation); + + base.AfterImageApply(source, destination, sourceRectangle); + } + + /// + /// Wraps a given angle in degrees so that it falls withing the 0-360 degree range + /// + /// The angle of rotation in degrees. + /// The . + private static float WrapDegrees(float degrees) + { + degrees %= 360; + + while (degrees < 0) + { + degrees += 360; + } + + return degrees; + } + + /// + /// Rotates the images with an optimized method when the angle is 90, 180 or 270 degrees. + /// + /// The source image. + /// The destination image. + /// The configuration. + /// + /// The + /// + private bool OptimizedApply(ImageFrame source, ImageFrame destination, Configuration configuration) + { + // Wrap the degrees to keep within 0-360 so we can apply optimizations when possible. + float degrees = WrapDegrees(this.Degrees); + + if (MathF.Abs(degrees) < Constants.Epsilon) + { + // The destination will be blank here so copy all the pixel data over + source.GetPixelSpan().CopyTo(destination.GetPixelSpan()); + return true; + } + + if (MathF.Abs(degrees - 90) < Constants.Epsilon) + { + this.Rotate90(source, destination, configuration); + return true; + } + + if (MathF.Abs(degrees - 180) < Constants.Epsilon) + { + this.Rotate180(source, destination, configuration); + return true; + } + + if (MathF.Abs(degrees - 270) < Constants.Epsilon) + { + this.Rotate270(source, destination, configuration); + return true; + } + + return false; + } + + /// + /// Rotates the image 270 degrees clockwise at the centre point. + /// + /// The source image. + /// The destination image. + /// The configuration. + private void Rotate270(ImageFrame source, ImageFrame destination, Configuration configuration) + { + int width = source.Width; + int height = source.Height; + Rectangle destinationBounds = destination.Bounds(); + + ParallelHelper.IterateRows( + source.Bounds(), + configuration, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span sourceRow = source.GetPixelRowSpan(y); + for (int x = 0; x < width; x++) + { + int newX = height - y - 1; + newX = height - newX - 1; + int newY = width - x - 1; + + if (destinationBounds.Contains(newX, newY)) + { + destination[newX, newY] = sourceRow[x]; + } + } + } + }); + } + + /// + /// Rotates the image 180 degrees clockwise at the centre point. + /// + /// The source image. + /// The destination image. + /// The configuration. + private void Rotate180(ImageFrame source, ImageFrame destination, Configuration configuration) + { + int width = source.Width; + int height = source.Height; + + ParallelHelper.IterateRows( + source.Bounds(), + configuration, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span sourceRow = source.GetPixelRowSpan(y); + Span targetRow = destination.GetPixelRowSpan(height - y - 1); + + for (int x = 0; x < width; x++) + { + targetRow[width - x - 1] = sourceRow[x]; + } + } + }); + } + + /// + /// Rotates the image 90 degrees clockwise at the centre point. + /// + /// The source image. + /// The destination image. + /// The configuration. + private void Rotate90(ImageFrame source, ImageFrame destination, Configuration configuration) + { + int width = source.Width; + int height = source.Height; + Rectangle destinationBounds = destination.Bounds(); + + ParallelHelper.IterateRows( + source.Bounds(), + configuration, + rows => + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span sourceRow = source.GetPixelRowSpan(y); + int newX = height - y - 1; + for (int x = 0; x < width; x++) + { + if (destinationBounds.Contains(newX, x)) + { + destination[newX, x] = sourceRow[x]; + } + } + } + }); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs index c7b1d74104..35c3717eea 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs @@ -9,14 +9,12 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { /// - /// Provides methods that allow the skewing of images. + /// Defines a skew transformation applicable to an . /// - /// The pixel format. - internal class SkewProcessor : AffineTransformProcessor - where TPixel : struct, IPixel + internal class SkewProcessor : AffineTransformProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The angle in degrees to perform the skew along the x-axis. /// The angle in degrees to perform the skew along the y-axis. @@ -27,7 +25,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The angle in degrees to perform the skew along the x-axis. /// The angle in degrees to perform the skew along the y-axis. diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorBase.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorBase.cs index 4973b90f46..286ada2e57 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorBase.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorBase.cs @@ -16,6 +16,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms { /// protected override void AfterImageApply(Image source, Image destination, Rectangle sourceRectangle) - => TransformProcessorHelpers.UpdateDimensionalMetData(destination); + => TransformProcessorHelpers.UpdateDimensionalMetadata(destination); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorHelpers.cs b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorHelpers.cs index 7bb666bce5..00c1227a6c 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorHelpers.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/TransformProcessorHelpers.cs @@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// The pixel format. /// The image to update - public static void UpdateDimensionalMetData(Image image) + public static void UpdateDimensionalMetadata(Image image) where TPixel : struct, IPixel { ExifProfile profile = image.Metadata.ExifProfile; diff --git a/src/ImageSharp/Processing/RotateExtensions.cs b/src/ImageSharp/Processing/RotateExtensions.cs index 398a634d10..cb637a1b84 100644 --- a/src/ImageSharp/Processing/RotateExtensions.cs +++ b/src/ImageSharp/Processing/RotateExtensions.cs @@ -1,48 +1,44 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Transforms; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of rotate operations to the type. + /// Adds extensions that allow the application of rotate operations to the type. /// public static class RotateExtensions { /// /// Rotates and flips an image by the given instructions. /// - /// The pixel format. /// The image to rotate. /// The to perform the rotation. /// The - public static IImageProcessingContext Rotate(this IImageProcessingContext source, RotateMode rotateMode) - where TPixel : struct, IPixel - => Rotate(source, (float)rotateMode); + public static IImageProcessingContext Rotate(this IImageProcessingContext source, RotateMode rotateMode) => + Rotate(source, (float)rotateMode); /// /// Rotates an image by the given angle in degrees. /// - /// The pixel format. /// The image to rotate. /// The angle in degrees to perform the rotation. /// The - public static IImageProcessingContext Rotate(this IImageProcessingContext source, float degrees) - where TPixel : struct, IPixel - => Rotate(source, degrees, KnownResamplers.Bicubic); + public static IImageProcessingContext Rotate(this IImageProcessingContext source, float degrees) => + Rotate(source, degrees, KnownResamplers.Bicubic); /// /// Rotates an image by the given angle in degrees using the specified sampling algorithm. /// - /// The pixel format. /// The image to rotate. /// The angle in degrees to perform the rotation. /// The to perform the resampling. /// The - public static IImageProcessingContext Rotate(this IImageProcessingContext source, float degrees, IResampler sampler) - where TPixel : struct, IPixel - => source.ApplyProcessor(new RotateProcessor(degrees, sampler, source.GetCurrentSize())); + public static IImageProcessingContext Rotate( + this IImageProcessingContext source, + float degrees, + IResampler sampler) => + source.ApplyProcessor(new RotateProcessor(degrees, sampler, source.GetCurrentSize())); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/RotateFlipExtensions.cs b/src/ImageSharp/Processing/RotateFlipExtensions.cs index 27ddc8de96..5030c942ef 100644 --- a/src/ImageSharp/Processing/RotateFlipExtensions.cs +++ b/src/ImageSharp/Processing/RotateFlipExtensions.cs @@ -6,20 +6,18 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of rotate-flip operations to the type. + /// Adds extensions that allow the application of rotate-flip operations to the type. /// public static class RotateFlipExtensions { /// /// Rotates and flips an image by the given instructions. /// - /// The pixel format. /// The image to rotate, flip, or both. /// The to perform the rotation. /// The to perform the flip. /// The - public static IImageProcessingContext RotateFlip(this IImageProcessingContext source, RotateMode rotateMode, FlipMode flipMode) - where TPixel : struct, IPixel + public static IImageProcessingContext RotateFlip(this IImageProcessingContext source, RotateMode rotateMode, FlipMode flipMode) => source.Rotate(rotateMode).Flip(flipMode); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/SkewExtensions.cs b/src/ImageSharp/Processing/SkewExtensions.cs index 07e3c6087d..7f378d2483 100644 --- a/src/ImageSharp/Processing/SkewExtensions.cs +++ b/src/ImageSharp/Processing/SkewExtensions.cs @@ -1,39 +1,39 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Transforms; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of skew operations to the type. + /// Adds extensions that allow the application of skew operations to the type. /// public static class SkewExtensions { /// /// Skews an image by the given angles in degrees. /// - /// The pixel format. /// The image to skew. /// The angle in degrees to perform the skew along the x-axis. /// The angle in degrees to perform the skew along the y-axis. - /// The - public static IImageProcessingContext Skew(this IImageProcessingContext source, float degreesX, float degreesY) - where TPixel : struct, IPixel - => Skew(source, degreesX, degreesY, KnownResamplers.Bicubic); + /// The . + public static IImageProcessingContext + Skew(this IImageProcessingContext source, float degreesX, float degreesY) => + Skew(source, degreesX, degreesY, KnownResamplers.Bicubic); /// /// Skews an image by the given angles in degrees using the specified sampling algorithm. /// - /// The pixel format. /// The image to skew. /// The angle in degrees to perform the skew along the x-axis. /// The angle in degrees to perform the skew along the y-axis. /// The to perform the resampling. - /// The - public static IImageProcessingContext Skew(this IImageProcessingContext source, float degreesX, float degreesY, IResampler sampler) - where TPixel : struct, IPixel - => source.ApplyProcessor(new SkewProcessor(degreesX, degreesY, sampler, source.GetCurrentSize())); + /// The . + public static IImageProcessingContext Skew( + this IImageProcessingContext source, + float degreesX, + float degreesY, + IResampler sampler) => + source.ApplyProcessor(new SkewProcessor(degreesX, degreesY, sampler, source.GetCurrentSize())); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/TransformExtensions.cs b/src/ImageSharp/Processing/TransformExtensions.cs index db14b6baf9..35f374d013 100644 --- a/src/ImageSharp/Processing/TransformExtensions.cs +++ b/src/ImageSharp/Processing/TransformExtensions.cs @@ -3,60 +3,53 @@ using System.Numerics; -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Transforms; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of composable transform operations to the type. + /// Adds extensions that allow the application of composable transform operations to the type. /// public static class TransformExtensions { /// /// Performs an affine transform of an image. /// - /// The pixel format. /// The image to transform. /// The affine transform builder. /// The - public static IImageProcessingContext Transform( - this IImageProcessingContext source, - AffineTransformBuilder builder) - where TPixel : struct, IPixel - => Transform(source, builder, KnownResamplers.Bicubic); + public static IImageProcessingContext Transform( + this IImageProcessingContext source, + AffineTransformBuilder builder) => + Transform(source, builder, KnownResamplers.Bicubic); /// /// Performs an affine transform of an image using the specified sampling algorithm. /// - /// The pixel format. - /// The . + /// The . /// The affine transform builder. /// The to perform the resampling. - /// The - public static IImageProcessingContext Transform( - this IImageProcessingContext ctx, + /// The . + public static IImageProcessingContext Transform( + this IImageProcessingContext ctx, AffineTransformBuilder builder, - IResampler sampler) - where TPixel : struct, IPixel - => ctx.Transform(new Rectangle(Point.Empty, ctx.GetCurrentSize()), builder, sampler); + IResampler sampler) => + ctx.Transform(new Rectangle(Point.Empty, ctx.GetCurrentSize()), builder, sampler); /// /// Performs an affine transform of an image using the specified sampling algorithm. /// - /// The pixel format. - /// The . + /// The . /// The source rectangle /// The affine transform builder. /// The to perform the resampling. - /// The - public static IImageProcessingContext Transform( - this IImageProcessingContext ctx, + /// The . + public static IImageProcessingContext Transform( + this IImageProcessingContext ctx, Rectangle sourceRectangle, AffineTransformBuilder builder, IResampler sampler) - where TPixel : struct, IPixel { Matrix3x2 transform = builder.BuildMatrix(sourceRectangle); Size targetDimensions = TransformUtils.GetTransformedSize(sourceRectangle.Size, transform); @@ -66,69 +59,61 @@ namespace SixLabors.ImageSharp.Processing /// /// Performs an affine transform of an image using the specified sampling algorithm. /// - /// The pixel format. - /// The . + /// The . /// The source rectangle /// The transformation matrix. /// The size of the result image. /// The to perform the resampling. - /// The - public static IImageProcessingContext Transform( - this IImageProcessingContext ctx, + /// The . + public static IImageProcessingContext Transform( + this IImageProcessingContext ctx, Rectangle sourceRectangle, Matrix3x2 transform, Size targetDimensions, IResampler sampler) - where TPixel : struct, IPixel { return ctx.ApplyProcessor( - new AffineTransformProcessor(transform, sampler, targetDimensions), + new AffineTransformProcessor(transform, sampler, targetDimensions), sourceRectangle); } /// /// Performs a projective transform of an image. /// - /// The pixel format. /// The image to transform. /// The affine transform builder. - /// The - public static IImageProcessingContext Transform( - this IImageProcessingContext source, - ProjectiveTransformBuilder builder) - where TPixel : struct, IPixel - => Transform(source, builder, KnownResamplers.Bicubic); + /// The . + public static IImageProcessingContext Transform( + this IImageProcessingContext source, + ProjectiveTransformBuilder builder) => + Transform(source, builder, KnownResamplers.Bicubic); /// /// Performs a projective transform of an image using the specified sampling algorithm. /// - /// The pixel format. - /// The . + /// The . /// The projective transform builder. /// The to perform the resampling. - /// The - public static IImageProcessingContext Transform( - this IImageProcessingContext ctx, + /// The . + public static IImageProcessingContext Transform( + this IImageProcessingContext ctx, ProjectiveTransformBuilder builder, - IResampler sampler) - where TPixel : struct, IPixel - => ctx.Transform(new Rectangle(Point.Empty, ctx.GetCurrentSize()), builder, sampler); + IResampler sampler) => + ctx.Transform(new Rectangle(Point.Empty, ctx.GetCurrentSize()), builder, sampler); /// /// Performs a projective transform of an image using the specified sampling algorithm. /// - /// The pixel format. - /// The . + /// The . /// The source rectangle /// The projective transform builder. /// The to perform the resampling. - /// The - public static IImageProcessingContext Transform( - this IImageProcessingContext ctx, + /// The . + public static IImageProcessingContext Transform( + this IImageProcessingContext ctx, Rectangle sourceRectangle, ProjectiveTransformBuilder builder, IResampler sampler) - where TPixel : struct, IPixel { Matrix4x4 transform = builder.BuildMatrix(sourceRectangle); Size targetDimensions = TransformUtils.GetTransformedSize(sourceRectangle.Size, transform); @@ -138,23 +123,21 @@ namespace SixLabors.ImageSharp.Processing /// /// Performs a projective transform of an image using the specified sampling algorithm. /// - /// The pixel format. - /// The . + /// The . /// The source rectangle /// The transformation matrix. /// The size of the result image. /// The to perform the resampling. - /// The - public static IImageProcessingContext Transform( - this IImageProcessingContext ctx, + /// The . + public static IImageProcessingContext Transform( + this IImageProcessingContext ctx, Rectangle sourceRectangle, Matrix4x4 transform, Size targetDimensions, IResampler sampler) - where TPixel : struct, IPixel { return ctx.ApplyProcessor( - new ProjectiveTransformProcessor(transform, sampler, targetDimensions), + new ProjectiveTransformProcessor(transform, sampler, targetDimensions), sourceRectangle); } } diff --git a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs index b07f508834..ee04d43888 100644 --- a/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs +++ b/tests/ImageSharp.Tests/Drawing/DrawImageTest.cs @@ -79,7 +79,8 @@ namespace SixLabors.ImageSharp.Tests .AppendTranslation(new PointF(10, 10)); // Apply a background color so we can see the translation. - blend.Mutate(x => x.Transform(builder).BackgroundColor(NamedColors.HotPink)); + blend.Mutate(x => x.Transform(builder)); + blend.Mutate(x => x.BackgroundColor(NamedColors.HotPink)); // Lets center the matrix so we can tell whether any cut-off issues we may have belong to the drawing processor var position = new Point((image.Width - blend.Width) / 2, (image.Height - blend.Height) / 2); diff --git a/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs index 6731debd36..1c3bf2e90d 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/CropTest.cs @@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms public void CropWidthHeightCropProcessorWithRectangleSet(int width, int height) { this.operations.Crop(width, height); - CropProcessor processor = this.Verify>(); + CropProcessor processor = this.Verify(); Assert.Equal(new Rectangle(0, 0, width, height), processor.CropRectangle); } @@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms { var cropRectangle = new Rectangle(x, y, width, height); this.operations.Crop(cropRectangle); - CropProcessor processor = this.Verify>(); + CropProcessor processor = this.Verify(); Assert.Equal(cropRectangle, processor.CropRectangle); } diff --git a/tests/ImageSharp.Tests/Processing/Transforms/EntropyCropTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/EntropyCropTest.cs index 03a8628a56..aa684acd55 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/EntropyCropTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/EntropyCropTest.cs @@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms public void EntropyCropThresholdFloatEntropyCropProcessorWithThreshold(float threshold) { this.operations.EntropyCrop(threshold); - EntropyCropProcessor processor = this.Verify>(); + EntropyCropProcessor processor = this.Verify(); Assert.Equal(threshold, processor.Threshold); } diff --git a/tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs index 39adcaa3fa..9fe2977adb 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/FlipTests.cs @@ -22,7 +22,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms public void Flip_degreesFloat_RotateProcessorWithAnglesSetAndExpandTrue(FlipMode flip) { this.operations.Flip(flip); - FlipProcessor flipProcessor = this.Verify>(); + FlipProcessor flipProcessor = this.Verify(); Assert.Equal(flip, flipProcessor.FlipMode); } diff --git a/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs index 82d7682558..b870ddd08a 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/PadTest.cs @@ -1,15 +1,13 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Processors.Transforms; + using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Transforms { - using SixLabors.ImageSharp.Processing; - using SixLabors.ImageSharp.Processing.Processors.Transforms; - public class PadTest : BaseImageOperationsExtensionTest { [Fact] @@ -20,7 +18,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms IResampler sampler = KnownResamplers.NearestNeighbor; this.operations.Pad(width, height); - ResizeProcessor resizeProcessor = this.Verify>(); + ResizeProcessor resizeProcessor = this.Verify(); Assert.Equal(width, resizeProcessor.Width); Assert.Equal(height, resizeProcessor.Height); diff --git a/tests/ImageSharp.Tests/Processing/Transforms/RotateFlipTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/RotateFlipTests.cs index dccf7afa6a..079ff67b7a 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/RotateFlipTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/RotateFlipTests.cs @@ -26,8 +26,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms public void RotateDegreesFloatRotateProcessorWithAnglesSet(RotateMode angle, FlipMode flip, float expectedAngle) { this.operations.RotateFlip(angle, flip); - RotateProcessor rotateProcessor = this.Verify>(0); - FlipProcessor flipProcessor = this.Verify>(1); + RotateProcessor rotateProcessor = this.Verify(0); + FlipProcessor flipProcessor = this.Verify(1); Assert.Equal(expectedAngle, rotateProcessor.Degrees); Assert.Equal(flip, flipProcessor.FlipMode); diff --git a/tests/ImageSharp.Tests/Processing/Transforms/RotateTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/RotateTests.cs index ae312d7235..3e2b859714 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/RotateTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/RotateTests.cs @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms public void RotateDegreesFloatRotateProcessorWithAnglesSet(float angle) { this.operations.Rotate(angle); - RotateProcessor processor = this.Verify>(); + RotateProcessor processor = this.Verify(); Assert.Equal(angle, processor.Degrees); } @@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms public void RotateRotateTypeRotateProcessorWithAnglesConvertedFromEnum(RotateMode angle, float expectedAngle) { this.operations.Rotate(angle); // is this api needed ??? - RotateProcessor processor = this.Verify>(); + RotateProcessor processor = this.Verify(); Assert.Equal(expectedAngle, processor.Degrees); } diff --git a/tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs index 73754b9716..38033e80d0 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/SkewTest.cs @@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms public void SkewXYCreateSkewProcessorWithAnglesSet() { this.operations.Skew(10, 20); - SkewProcessor processor = this.Verify>(); + SkewProcessor processor = this.Verify(); Assert.Equal(10, processor.DegreesX); Assert.Equal(20, processor.DegreesY); diff --git a/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs b/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs index a62f4fc7c6..3ac9af960b 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/TransformsHelpersTest.cs @@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms Assert.Equal(ExifDataType.Long, profile.GetValue(ExifTag.PixelXDimension).DataType); Assert.Equal(ExifDataType.Long, profile.GetValue(ExifTag.PixelYDimension).DataType); - TransformProcessorHelpers.UpdateDimensionalMetData(img); + TransformProcessorHelpers.UpdateDimensionalMetadata(img); Assert.Equal(ExifDataType.Short, profile.GetValue(ExifTag.PixelXDimension).DataType); Assert.Equal(ExifDataType.Short, profile.GetValue(ExifTag.PixelYDimension).DataType); From 22892a5cca31befa5796ed2297d22e2c34dc6aac Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 May 2019 02:36:41 +0200 Subject: [PATCH 21/31] sealed everything --- .../Processing/Processors/Convolution/BoxBlurProcessor.cs | 2 +- ...lution2DProcessor.cs => Convolution2DProcessor{TPixel}.cs} | 3 ++- ...2PassProcessor.cs => Convolution2PassProcessor{TPixel}.cs} | 0 ...onvolutionProcessor.cs => ConvolutionProcessor{TPixel}.cs} | 0 ...ector2DProcessor.cs => EdgeDetector2DProcessor{TPixel}.cs} | 0 ...ssProcessor.cs => EdgeDetectorCompassProcessor{TPixel}.cs} | 0 .../Processors/Convolution/GaussianBlurProcessor.cs | 2 +- .../Processors/Convolution/GaussianSharpenProcessor.cs | 2 +- .../Processing/Processors/Convolution/KayyaliProcessor.cs | 2 +- .../Processing/Processors/Convolution/KirschProcessor.cs | 2 +- .../Processors/Convolution/Laplacian3x3Processor.cs | 2 +- .../Processors/Convolution/Laplacian5x5Processor.cs | 2 +- .../Processors/Convolution/LaplacianOfGaussianProcessor.cs | 2 +- .../Processing/Processors/Convolution/PrewittProcessor.cs | 2 +- .../Processors/Convolution/RobertsCrossProcessor.cs | 2 +- .../Processing/Processors/Convolution/RobinsonProcessor.cs | 2 +- .../Processing/Processors/Convolution/ScharrProcessor.cs | 2 +- .../Processing/Processors/Convolution/SobelProcessor.cs | 2 +- .../Processing/Processors/Effects/OilPaintingProcessor.cs | 2 +- .../Processing/Processors/Effects/PixelateProcessor.cs | 2 +- .../Processing/Processors/Filters/AchromatomalyProcessor.cs | 4 +--- .../Processing/Processors/Filters/AchromatopsiaProcessor.cs | 4 +--- .../Processing/Processors/Filters/BlackWhiteProcessor.cs | 4 +--- .../Processing/Processors/Filters/BrightnessProcessor.cs | 4 +--- .../Processing/Processors/Filters/ContrastProcessor.cs | 4 +--- .../Processing/Processors/Filters/DeuteranomalyProcessor.cs | 4 +--- .../Processing/Processors/Filters/DeuteranopiaProcessor.cs | 4 +--- .../Processing/Processors/Filters/GrayscaleBt601Processor.cs | 4 +--- .../Processing/Processors/Filters/GrayscaleBt709Processor.cs | 2 +- src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs | 4 +--- .../Processing/Processors/Filters/InvertProcessor.cs | 4 +--- .../Processing/Processors/Filters/KodachromeProcessor.cs | 2 +- .../Processing/Processors/Filters/LomographProcessor.cs | 2 +- .../Processing/Processors/Filters/OpacityProcessor.cs | 2 +- .../Processing/Processors/Filters/PolaroidProcessor.cs | 2 +- .../Processing/Processors/Filters/ProtanomalyProcessor.cs | 4 +--- .../Processing/Processors/Filters/ProtanopiaProcessor.cs | 4 +--- .../Processing/Processors/Filters/SaturateProcessor.cs | 4 +--- .../Processing/Processors/Filters/SepiaProcessor.cs | 4 +--- .../Processing/Processors/Filters/TritanomalyProcessor.cs | 4 +--- .../Processing/Processors/Filters/TritanopiaProcessor.cs | 4 +--- .../Processing/Processors/Transforms/FlipProcessor.cs | 2 +- .../Processing/Processors/Transforms/SkewProcessor.cs | 2 +- 43 files changed, 40 insertions(+), 71 deletions(-) rename src/ImageSharp/Processing/Processors/Convolution/{Convolution2DProcessor.cs => Convolution2DProcessor{TPixel}.cs} (99%) rename src/ImageSharp/Processing/Processors/Convolution/{Convolution2PassProcessor.cs => Convolution2PassProcessor{TPixel}.cs} (100%) rename src/ImageSharp/Processing/Processors/Convolution/{ConvolutionProcessor.cs => ConvolutionProcessor{TPixel}.cs} (100%) rename src/ImageSharp/Processing/Processors/Convolution/{EdgeDetector2DProcessor.cs => EdgeDetector2DProcessor{TPixel}.cs} (100%) rename src/ImageSharp/Processing/Processors/Convolution/{EdgeDetectorCompassProcessor.cs => EdgeDetectorCompassProcessor{TPixel}.cs} (100%) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs index bb37489e13..4e56e75d39 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BoxBlurProcessor.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Defines a box blur processor of a given Radius. /// - public class BoxBlurProcessor : IImageProcessor + public sealed class BoxBlurProcessor : IImageProcessor { /// /// The default radius used by the parameterless constructor. diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs similarity index 99% rename from src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs rename to src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs index 633b50a9b7..299b1d41c1 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Convolution2DProcessor{TPixel}.cs @@ -1,9 +1,10 @@ -// Copyright (c) Six Labors and contributors. +// Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. using System; using System.Numerics; using System.Runtime.InteropServices; + using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.ParallelUtils; using SixLabors.ImageSharp.PixelFormats; diff --git a/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor.cs rename to src/ImageSharp/Processing/Processors/Convolution/Convolution2PassProcessor{TPixel}.cs diff --git a/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor.cs rename to src/ImageSharp/Processing/Processors/Convolution/ConvolutionProcessor{TPixel}.cs diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor.cs rename to src/ImageSharp/Processing/Processors/Convolution/EdgeDetector2DProcessor{TPixel}.cs diff --git a/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs similarity index 100% rename from src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor.cs rename to src/ImageSharp/Processing/Processors/Convolution/EdgeDetectorCompassProcessor{TPixel}.cs diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs index c64a024f9f..764f4ca517 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianBlurProcessor.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Defines Gaussian blur by a (Sigma, Radius) pair. /// - public class GaussianBlurProcessor : IImageProcessor + public sealed class GaussianBlurProcessor : IImageProcessor { /// /// The default value for . diff --git a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs index 84e56869d3..23282af36d 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/GaussianSharpenProcessor.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// /// Defines Gaussian sharpening by a (Sigma, Radius) pair. /// - public class GaussianSharpenProcessor : IImageProcessor + public sealed class GaussianSharpenProcessor : IImageProcessor { /// /// The default value for . diff --git a/src/ImageSharp/Processing/Processors/Convolution/KayyaliProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/KayyaliProcessor.cs index 99dfb17ffb..2d0f056b61 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/KayyaliProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/KayyaliProcessor.cs @@ -7,7 +7,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Defines edge detection processing using the Kayyali operator filter. /// See . /// - public class KayyaliProcessor : EdgeDetectorProcessor + public sealed class KayyaliProcessor : EdgeDetectorProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs index fc73903c7d..9e95344222 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/KirschProcessor.cs @@ -7,7 +7,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Defines edge detection using the Kirsch operator filter. /// See . /// - public class KirschProcessor : EdgeDetectorProcessor + public sealed class KirschProcessor : EdgeDetectorProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs b/src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs index 18a53cb131..9c9488fec0 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Laplacian3x3Processor.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Applies edge detection processing to the image using the Laplacian 3x3 operator filter. /// /// - public class Laplacian3x3Processor : EdgeDetectorProcessor + public sealed class Laplacian3x3Processor : EdgeDetectorProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Convolution/Laplacian5x5Processor.cs b/src/ImageSharp/Processing/Processors/Convolution/Laplacian5x5Processor.cs index 1c95ca4903..fa0c8c5aa3 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/Laplacian5x5Processor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/Laplacian5x5Processor.cs @@ -7,7 +7,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Defines edge detection processing using the Laplacian 5x5 operator filter. /// . /// - public class Laplacian5x5Processor : EdgeDetectorProcessor + public sealed class Laplacian5x5Processor : EdgeDetectorProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Convolution/LaplacianOfGaussianProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/LaplacianOfGaussianProcessor.cs index d904d69b1b..2caff8201c 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/LaplacianOfGaussianProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/LaplacianOfGaussianProcessor.cs @@ -7,7 +7,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Applies edge detection processing to the image using the Laplacian of Gaussian operator filter. /// See . /// - public class LaplacianOfGaussianProcessor : EdgeDetectorProcessor + public sealed class LaplacianOfGaussianProcessor : EdgeDetectorProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Convolution/PrewittProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/PrewittProcessor.cs index 939b1e0c52..29f6fc279c 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/PrewittProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/PrewittProcessor.cs @@ -7,7 +7,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Defines edge detection using the Prewitt operator filter. /// See . /// - public class PrewittProcessor : EdgeDetectorProcessor + public sealed class PrewittProcessor : EdgeDetectorProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs index ff41b6c694..a9db37a076 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/RobertsCrossProcessor.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Defines edge detection processing using the Roberts Cross operator filter. /// See . /// - public class RobertsCrossProcessor : EdgeDetectorProcessor + public sealed class RobertsCrossProcessor : EdgeDetectorProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs index 603a81f676..fd73789027 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/RobinsonProcessor.cs @@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Defines edge detection using the Robinson operator filter. /// See . /// - internal class RobinsonProcessor : EdgeDetectorProcessor + public sealed class RobinsonProcessor : EdgeDetectorProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs index d23ba53384..ec0183dc63 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/ScharrProcessor.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Defines edge detection processing using the Scharr operator filter. /// /// - public class ScharrProcessor : EdgeDetectorProcessor + public sealed class ScharrProcessor : EdgeDetectorProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs index 157bb62e50..bc4339e052 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/SobelProcessor.cs @@ -10,7 +10,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution /// Defines edge detection using the Sobel operator filter. /// See . /// - public class SobelProcessor : EdgeDetectorProcessor + public sealed class SobelProcessor : EdgeDetectorProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs index 14e164fb77..741ba9eced 100644 --- a/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/OilPaintingProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects /// /// Defines an oil painting effect. /// - public class OilPaintingProcessor : IImageProcessor + public sealed class OilPaintingProcessor : IImageProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs index eeb2aafcb5..1599c9dab8 100644 --- a/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Effects/PixelateProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Effects /// /// Defines a pixelation effect of a given size. /// - public class PixelateProcessor : IImageProcessor + public sealed class PixelateProcessor : IImageProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/AchromatomalyProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/AchromatomalyProcessor.cs index 7c3789c388..0e6653d99b 100644 --- a/src/ImageSharp/Processing/Processors/Filters/AchromatomalyProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/AchromatomalyProcessor.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Converts the colors of the image recreating Achromatomaly (Color desensitivity) color blindness. /// - public class AchromatomalyProcessor : FilterProcessor + public sealed class AchromatomalyProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/AchromatopsiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/AchromatopsiaProcessor.cs index ab04a30ba1..10a201b1d7 100644 --- a/src/ImageSharp/Processing/Processors/Filters/AchromatopsiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/AchromatopsiaProcessor.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Converts the colors of the image recreating Achromatopsia (Monochrome) color blindness. /// - public class AchromatopsiaProcessor : FilterProcessor + public sealed class AchromatopsiaProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs index 7e2e7f2f47..425ae511f6 100644 --- a/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/BlackWhiteProcessor.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Applies a black and white filter matrix to the image. /// - public class BlackWhiteProcessor : FilterProcessor + public sealed class BlackWhiteProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs index 94a7a394a6..77862b3e3f 100644 --- a/src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/BrightnessProcessor.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Applies a brightness filter matrix using the given amount. /// - public class BrightnessProcessor : FilterProcessor + public sealed class BrightnessProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs index 1f74a1d642..3fdeafde3e 100644 --- a/src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/ContrastProcessor.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Applies a contrast filter matrix using the given amount. /// - public class ContrastProcessor : FilterProcessor + public sealed class ContrastProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/DeuteranomalyProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/DeuteranomalyProcessor.cs index 7ad0630aac..7e1c0ca503 100644 --- a/src/ImageSharp/Processing/Processors/Filters/DeuteranomalyProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/DeuteranomalyProcessor.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Converts the colors of the image recreating Deuteranomaly (Green-Weak) color blindness. /// - public class DeuteranomalyProcessor : FilterProcessor + public sealed class DeuteranomalyProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/DeuteranopiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/DeuteranopiaProcessor.cs index 188d02dc8b..e4ed44e74e 100644 --- a/src/ImageSharp/Processing/Processors/Filters/DeuteranopiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/DeuteranopiaProcessor.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Converts the colors of the image recreating Deuteranopia (Green-Blind) color blindness. /// - public class DeuteranopiaProcessor : FilterProcessor + public sealed class DeuteranopiaProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs index c24992b441..153a1a17c5 100644 --- a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt601Processor.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Applies a grayscale filter matrix using the given amount and the formula as specified by ITU-R Recommendation BT.601 /// - public class GrayscaleBt601Processor : FilterProcessor + public sealed class GrayscaleBt601Processor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs index dc5294ccf0..2d7d2a1eaa 100644 --- a/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/GrayscaleBt709Processor.cs @@ -9,7 +9,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a grayscale filter matrix using the given amount and the formula as specified by ITU-R Recommendation BT.709 /// - public class GrayscaleBt709Processor : FilterProcessor + public sealed class GrayscaleBt709Processor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs index 65f7eef330..05d3cbdc7c 100644 --- a/src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/HueProcessor.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Applies a hue filter matrix using the given angle of rotation in degrees /// - public class HueProcessor : FilterProcessor + public sealed class HueProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs index cc41e65721..99b138033d 100644 --- a/src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/InvertProcessor.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Applies a filter matrix that inverts the colors of an image /// - public class InvertProcessor : FilterProcessor + public sealed class InvertProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs index a5215a434c..012b10ee03 100644 --- a/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/KodachromeProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies a filter matrix recreating an old Kodachrome camera effect matrix to the image /// - public class KodachromeProcessor : FilterProcessor + public sealed class KodachromeProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs index dae168e2cb..f5a1befffb 100644 --- a/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/LomographProcessor.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating an old Lomograph effect. /// - public class LomographProcessor : FilterProcessor + public sealed class LomographProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs index 0ee5dd59e5..922ca32330 100644 --- a/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/OpacityProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Applies an opacity filter matrix using the given amount. /// - public class OpacityProcessor : FilterProcessor + public sealed class OpacityProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs index 341c02acfc..676bbc06bb 100644 --- a/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/PolaroidProcessor.cs @@ -6,7 +6,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Filters /// /// Converts the colors of the image recreating an old Polaroid effect. /// - public class PolaroidProcessor : FilterProcessor + public sealed class PolaroidProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/ProtanomalyProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ProtanomalyProcessor.cs index 755bc0f81a..1c01b608e5 100644 --- a/src/ImageSharp/Processing/Processors/Filters/ProtanomalyProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/ProtanomalyProcessor.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Converts the colors of the image recreating Protanomaly (Red-Weak) color blindness. /// - public class ProtanomalyProcessor : FilterProcessor + public sealed class ProtanomalyProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/ProtanopiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/ProtanopiaProcessor.cs index c4f4a76611..ec423bd9a5 100644 --- a/src/ImageSharp/Processing/Processors/Filters/ProtanopiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/ProtanopiaProcessor.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Converts the colors of the image recreating Protanopia (Red-Blind) color blindness. /// - public class ProtanopiaProcessor : FilterProcessor + public sealed class ProtanopiaProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs index 770fb98277..2cc40664b4 100644 --- a/src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/SaturateProcessor.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Applies a saturation filter matrix using the given amount. /// - public class SaturateProcessor : FilterProcessor + public sealed class SaturateProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs index 9da48993a2..34af410671 100644 --- a/src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/SepiaProcessor.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Applies a sepia filter matrix using the given amount. /// - public class SepiaProcessor : FilterProcessor + public sealed class SepiaProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/TritanomalyProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/TritanomalyProcessor.cs index c64475b994..a31e52c7e7 100644 --- a/src/ImageSharp/Processing/Processors/Filters/TritanomalyProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/TritanomalyProcessor.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Converts the colors of the image recreating Tritanomaly (Blue-Weak) color blindness. /// - public class TritanomalyProcessor : FilterProcessor + public sealed class TritanomalyProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Filters/TritanopiaProcessor.cs b/src/ImageSharp/Processing/Processors/Filters/TritanopiaProcessor.cs index 722840e743..b622126f32 100644 --- a/src/ImageSharp/Processing/Processors/Filters/TritanopiaProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Filters/TritanopiaProcessor.cs @@ -1,14 +1,12 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; - namespace SixLabors.ImageSharp.Processing.Processors.Filters { /// /// Converts the colors of the image recreating Tritanopia (Blue-Blind) color blindness. /// - public class TritanopiaProcessor : FilterProcessor + public sealed class TritanopiaProcessor : FilterProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs index 9a3eab6cc8..f604d8399f 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/FlipProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// Defines a flipping around the center point of the image. /// - public class FlipProcessor : IImageProcessor + public sealed class FlipProcessor : IImageProcessor { /// /// Initializes a new instance of the class. diff --git a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs index 35c3717eea..cb73bb66c2 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/SkewProcessor.cs @@ -11,7 +11,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// Defines a skew transformation applicable to an . /// - internal class SkewProcessor : AffineTransformProcessor + public sealed class SkewProcessor : AffineTransformProcessor { /// /// Initializes a new instance of the class. From 394174bb532ad4f61b35b81cbfa38b9131535158 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 May 2019 15:56:34 +0200 Subject: [PATCH 22/31] refactor HistogramEqualization --- .../HistogramEqualizationExtension.cs | 44 +- .../AdaptiveHistEqualizationProcessor.cs | 526 +---------------- ...aptiveHistEqualizationProcessor{TPixel}.cs | 546 ++++++++++++++++++ .../AdaptiveHistEqualizationSWProcessor.cs | 371 +----------- ...tiveHistEqualizationSWProcessor{TPixel}.cs | 390 +++++++++++++ .../GlobalHistogramEqualizationProcessor.cs | 99 +--- ...lHistogramEqualizationProcessor{TPixel}.cs | 107 ++++ .../HistogramEqualizationOptions.cs | 2 +- .../HistogramEqualizationProcessor.cs | 134 ++--- .../HistogramEqualizationProcessor{TPixel}.cs | 139 +++++ .../HistogramEqualizationTests.cs | 4 +- .../Processors/Dithering/DitherTests.cs | 3 +- 12 files changed, 1264 insertions(+), 1101 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs diff --git a/src/ImageSharp/Processing/HistogramEqualizationExtension.cs b/src/ImageSharp/Processing/HistogramEqualizationExtension.cs index d967ef3622..aae3e997fe 100644 --- a/src/ImageSharp/Processing/HistogramEqualizationExtension.cs +++ b/src/ImageSharp/Processing/HistogramEqualizationExtension.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.ImageSharp.Processing.Processors.Normalization; namespace SixLabors.ImageSharp.Processing @@ -14,49 +13,20 @@ namespace SixLabors.ImageSharp.Processing /// /// Equalizes the histogram of an image to increases the contrast. /// - /// The pixel format. /// The image this method extends. /// The . - public static IImageProcessingContext HistogramEqualization(this IImageProcessingContext source) - where TPixel : struct, IPixel - => HistogramEqualization(source, HistogramEqualizationOptions.Default); + public static IImageProcessingContext HistogramEqualization(this IImageProcessingContext source) => + HistogramEqualization(source, HistogramEqualizationOptions.Default); /// /// Equalizes the histogram of an image to increases the contrast. /// - /// The pixel format. /// The image this method extends. /// The histogram equalization options to use. /// The . - public static IImageProcessingContext HistogramEqualization(this IImageProcessingContext source, HistogramEqualizationOptions options) - where TPixel : struct, IPixel - => source.ApplyProcessor(GetProcessor(options)); - - private static HistogramEqualizationProcessor GetProcessor(HistogramEqualizationOptions options) - where TPixel : struct, IPixel - { - HistogramEqualizationProcessor processor; - - switch (options.Method) - { - case HistogramEqualizationMethod.Global: - processor = new GlobalHistogramEqualizationProcessor(options.LuminanceLevels, options.ClipHistogram, options.ClipLimitPercentage); - break; - - case HistogramEqualizationMethod.AdaptiveTileInterpolation: - processor = new AdaptiveHistEqualizationProcessor(options.LuminanceLevels, options.ClipHistogram, options.ClipLimitPercentage, options.Tiles); - break; - - case HistogramEqualizationMethod.AdaptiveSlidingWindow: - processor = new AdaptiveHistEqualizationSWProcessor(options.LuminanceLevels, options.ClipHistogram, options.ClipLimitPercentage, options.Tiles); - break; - - default: - processor = new GlobalHistogramEqualizationProcessor(options.LuminanceLevels, options.ClipHistogram, options.ClipLimitPercentage); - break; - } - - return processor; - } + public static IImageProcessingContext HistogramEqualization( + this IImageProcessingContext source, + HistogramEqualizationOptions options) => + source.ApplyProcessor(HistogramEqualizationProcessor.FromOptions(options)); } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs index cb52a88b7b..4fd0f853d0 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs @@ -1,545 +1,37 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Buffers; -using System.Collections.Generic; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Threading.Tasks; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Normalization { /// /// Applies an adaptive histogram equalization to the image. The image is split up in tiles. For each tile a cumulative distribution function (cdf) is calculated. /// To calculate the final equalized pixel value, the cdf value of four adjacent tiles will be interpolated. /// - /// The pixel format. - internal class AdaptiveHistEqualizationProcessor : HistogramEqualizationProcessor - where TPixel : struct, IPixel + internal class AdaptiveHistEqualizationProcessor : HistogramEqualizationProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// or 65536 for 16-bit grayscale images. /// Indicating whether to clip the histogram bins at a specific value. /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. - /// The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100. - public AdaptiveHistEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int tiles) + /// The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100. + public AdaptiveHistEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int numberOfTiles) : base(luminanceLevels, clipHistogram, clipLimitPercentage) { - Guard.MustBeGreaterThanOrEqualTo(tiles, 2, nameof(tiles)); - Guard.MustBeLessThanOrEqualTo(tiles, 100, nameof(tiles)); - - this.Tiles = tiles; + this.NumberOfTiles = numberOfTiles; } /// /// Gets the number of tiles the image is split into (horizontal and vertically) for the adaptive histogram equalization. /// - private int Tiles { get; } - - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - { - int sourceWidth = source.Width; - int sourceHeight = source.Height; - int numberOfPixels = sourceWidth * sourceHeight; - int tileWidth = (int)MathF.Ceiling(sourceWidth / (float)this.Tiles); - int tileHeight = (int)MathF.Ceiling(sourceHeight / (float)this.Tiles); - int pixelsInTile = tileWidth * tileHeight; - int halfTileWidth = tileWidth / 2; - int halfTileHeight = tileHeight / 2; - int luminanceLevels = this.LuminanceLevels; - - // The image is split up into tiles. For each tile the cumulative distribution function will be calculated. - using (var cdfData = new CdfTileData(configuration, sourceWidth, sourceHeight, this.Tiles, this.Tiles, tileWidth, tileHeight, luminanceLevels)) - { - cdfData.CalculateLookupTables(source, this); - - var tileYStartPositions = new List<(int y, int cdfY)>(); - int cdfY = 0; - for (int y = halfTileHeight; y < sourceHeight - halfTileHeight; y += tileHeight) - { - tileYStartPositions.Add((y, cdfY)); - cdfY++; - } - - Parallel.For( - 0, - tileYStartPositions.Count, - new ParallelOptions() { MaxDegreeOfParallelism = configuration.MaxDegreeOfParallelism }, - index => - { - int cdfX = 0; - int tileX = 0; - int tileY = 0; - int y = tileYStartPositions[index].y; - int cdfYY = tileYStartPositions[index].cdfY; - - // It's unfortunate that we have to do this per iteration. - ref TPixel sourceBase = ref source.GetPixelReference(0, 0); - - cdfX = 0; - for (int x = halfTileWidth; x < sourceWidth - halfTileWidth; x += tileWidth) - { - tileY = 0; - int yEnd = Math.Min(y + tileHeight, sourceHeight); - int xEnd = Math.Min(x + tileWidth, sourceWidth); - for (int dy = y; dy < yEnd; dy++) - { - int dyOffSet = dy * sourceWidth; - tileX = 0; - for (int dx = x; dx < xEnd; dx++) - { - ref TPixel pixel = ref Unsafe.Add(ref sourceBase, dyOffSet + dx); - float luminanceEqualized = InterpolateBetweenFourTiles( - pixel, - cdfData, - this.Tiles, - this.Tiles, - tileX, - tileY, - cdfX, - cdfYY, - tileWidth, - tileHeight, - luminanceLevels); - - pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); - tileX++; - } - - tileY++; - } - - cdfX++; - } - }); - - ref TPixel pixelsBase = ref source.GetPixelReference(0, 0); - - // Fix left column - ProcessBorderColumn(ref pixelsBase, cdfData, 0, sourceWidth, sourceHeight, tileWidth, tileHeight, xStart: 0, xEnd: halfTileWidth, luminanceLevels); - - // Fix right column - int rightBorderStartX = ((this.Tiles - 1) * tileWidth) + halfTileWidth; - ProcessBorderColumn(ref pixelsBase, cdfData, this.Tiles - 1, sourceWidth, sourceHeight, tileWidth, tileHeight, xStart: rightBorderStartX, xEnd: sourceWidth, luminanceLevels); - - // Fix top row - ProcessBorderRow(ref pixelsBase, cdfData, 0, sourceWidth, tileWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); - - // Fix bottom row - int bottomBorderStartY = ((this.Tiles - 1) * tileHeight) + halfTileHeight; - ProcessBorderRow(ref pixelsBase, cdfData, this.Tiles - 1, sourceWidth, tileWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels); - - // Left top corner - ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, 0, 0, xStart: 0, xEnd: halfTileWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); - - // Left bottom corner - ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, 0, this.Tiles - 1, xStart: 0, xEnd: halfTileWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels); - - // Right top corner - ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, this.Tiles - 1, 0, xStart: rightBorderStartX, xEnd: sourceWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); - - // Right bottom corner - ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, this.Tiles - 1, this.Tiles - 1, xStart: rightBorderStartX, xEnd: sourceWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels); - } - } - - /// - /// Processes the part of a corner tile which was previously left out. It consists of 1 / 4 of a tile and does not need interpolation. - /// - /// The output pixels base reference. - /// The lookup table to remap the grey values. - /// The source image width. - /// The x-position in the CDF lookup map. - /// The y-position in the CDF lookup map. - /// X start position. - /// X end position. - /// Y start position. - /// Y end position. - /// - /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images - /// or 65536 for 16-bit grayscale images. - /// - private static void ProcessCornerTile( - ref TPixel pixelsBase, - CdfTileData cdfData, - int sourceWidth, - int cdfX, - int cdfY, - int xStart, - int xEnd, - int yStart, - int yEnd, - int luminanceLevels) - { - for (int dy = yStart; dy < yEnd; dy++) - { - int dyOffSet = dy * sourceWidth; - for (int dx = xStart; dx < xEnd; dx++) - { - ref TPixel pixel = ref Unsafe.Add(ref pixelsBase, dyOffSet + dx); - float luminanceEqualized = cdfData.RemapGreyValue(cdfX, cdfY, GetLuminance(pixel, luminanceLevels)); - pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); - } - } - } - - /// - /// Processes a border column of the image which is half the size of the tile width. - /// - /// The output pixels reference. - /// The pre-computed lookup tables to remap the grey values for each tiles. - /// The X index of the lookup table to use. - /// The source image width. - /// The source image height. - /// The width of a tile. - /// The height of a tile. - /// X start position in the image. - /// X end position of the image. - /// - /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images - /// or 65536 for 16-bit grayscale images. - /// - private static void ProcessBorderColumn( - ref TPixel pixelBase, - CdfTileData cdfData, - int cdfX, - int sourceWidth, - int sourceHeight, - int tileWidth, - int tileHeight, - int xStart, - int xEnd, - int luminanceLevels) - { - int halfTileWidth = tileWidth / 2; - int halfTileHeight = tileHeight / 2; - - int cdfY = 0; - for (int y = halfTileHeight; y < sourceHeight - halfTileHeight; y += tileHeight) - { - int yLimit = Math.Min(y + tileHeight, sourceHeight - 1); - int tileY = 0; - for (int dy = y; dy < yLimit; dy++) - { - int dyOffSet = dy * sourceWidth; - int tileX = halfTileWidth; - for (int dx = xStart; dx < xEnd; dx++) - { - ref TPixel pixel = ref Unsafe.Add(ref pixelBase, dyOffSet + dx); - float luminanceEqualized = InterpolateBetweenTwoTiles(pixel, cdfData, cdfX, cdfY, cdfX, cdfY + 1, tileY, tileHeight, luminanceLevels); - pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); - tileX++; - } - - tileY++; - } - - cdfY++; - } - } + public int NumberOfTiles { get; } - /// - /// Processes a border row of the image which is half of the size of the tile height. - /// - /// The output pixels base reference. - /// The pre-computed lookup tables to remap the grey values for each tiles. - /// The Y index of the lookup table to use. - /// The source image width. - /// The width of a tile. - /// Y start position in the image. - /// Y end position of the image. - /// - /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images - /// or 65536 for 16-bit grayscale images. - /// - private static void ProcessBorderRow( - ref TPixel pixelBase, - CdfTileData cdfData, - int cdfY, - int sourceWidth, - int tileWidth, - int yStart, - int yEnd, - int luminanceLevels) + /// + public override IImageProcessor CreatePixelSpecificProcessor() { - int halfTileWidth = tileWidth / 2; - - int cdfX = 0; - for (int x = halfTileWidth; x < sourceWidth - halfTileWidth; x += tileWidth) - { - int tileY = 0; - for (int dy = yStart; dy < yEnd; dy++) - { - int dyOffSet = dy * sourceWidth; - int tileX = 0; - int xLimit = Math.Min(x + tileWidth, sourceWidth - 1); - for (int dx = x; dx < xLimit; dx++) - { - ref TPixel pixel = ref Unsafe.Add(ref pixelBase, dyOffSet + dx); - float luminanceEqualized = InterpolateBetweenTwoTiles(pixel, cdfData, cdfX, cdfY, cdfX + 1, cdfY, tileX, tileWidth, luminanceLevels); - pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); - tileX++; - } - - tileY++; - } - - cdfX++; - } - } - - /// - /// Bilinear interpolation between four adjacent tiles. - /// - /// The pixel to remap the grey value from. - /// The pre-computed lookup tables to remap the grey values for each tiles. - /// The number of tiles in the x-direction. - /// The number of tiles in the y-direction. - /// X position inside the tile. - /// Y position inside the tile. - /// X index of the top left lookup table to use. - /// Y index of the top left lookup table to use. - /// Width of one tile in pixels. - /// Height of one tile in pixels. - /// - /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images - /// or 65536 for 16-bit grayscale images. - /// - /// A re-mapped grey value. - [MethodImpl(InliningOptions.ShortMethod)] - private static float InterpolateBetweenFourTiles( - TPixel sourcePixel, - CdfTileData cdfData, - int tileCountX, - int tileCountY, - int tileX, - int tileY, - int cdfX, - int cdfY, - int tileWidth, - int tileHeight, - int luminanceLevels) - { - int luminance = GetLuminance(sourcePixel, luminanceLevels); - float tx = tileX / (float)(tileWidth - 1); - float ty = tileY / (float)(tileHeight - 1); - - int yTop = cdfY; - int yBottom = Math.Min(tileCountY - 1, yTop + 1); - int xLeft = cdfX; - int xRight = Math.Min(tileCountX - 1, xLeft + 1); - - float cdfLeftTopLuminance = cdfData.RemapGreyValue(xLeft, yTop, luminance); - float cdfRightTopLuminance = cdfData.RemapGreyValue(xRight, yTop, luminance); - float cdfLeftBottomLuminance = cdfData.RemapGreyValue(xLeft, yBottom, luminance); - float cdfRightBottomLuminance = cdfData.RemapGreyValue(xRight, yBottom, luminance); - return BilinearInterpolation(tx, ty, cdfLeftTopLuminance, cdfRightTopLuminance, cdfLeftBottomLuminance, cdfRightBottomLuminance); - } - - /// - /// Linear interpolation between two tiles. - /// - /// The pixel to remap the grey value from. - /// The CDF lookup map. - /// X position inside the first tile. - /// Y position inside the first tile. - /// X position inside the second tile. - /// Y position inside the second tile. - /// Position inside the tile. - /// Width of the tile. - /// - /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images - /// or 65536 for 16-bit grayscale images. - /// - /// A re-mapped grey value. - [MethodImpl(InliningOptions.ShortMethod)] - private static float InterpolateBetweenTwoTiles( - TPixel sourcePixel, - CdfTileData cdfData, - int tileX1, - int tileY1, - int tileX2, - int tileY2, - int tilePos, - int tileWidth, - int luminanceLevels) - { - int luminance = GetLuminance(sourcePixel, luminanceLevels); - float tx = tilePos / (float)(tileWidth - 1); - - float cdfLuminance1 = cdfData.RemapGreyValue(tileX1, tileY1, luminance); - float cdfLuminance2 = cdfData.RemapGreyValue(tileX2, tileY2, luminance); - return LinearInterpolation(cdfLuminance1, cdfLuminance2, tx); - } - - /// - /// Bilinear interpolation between four tiles. - /// - /// The interpolation value in x direction in the range of [0, 1]. - /// The interpolation value in y direction in the range of [0, 1]. - /// Luminance from top left tile. - /// Luminance from right top tile. - /// Luminance from left bottom tile. - /// Luminance from right bottom tile. - /// Interpolated Luminance. - [MethodImpl(InliningOptions.ShortMethod)] - private static float BilinearInterpolation(float tx, float ty, float lt, float rt, float lb, float rb) - => LinearInterpolation(LinearInterpolation(lt, rt, tx), LinearInterpolation(lb, rb, tx), ty); - - /// - /// Linear interpolation between two grey values. - /// - /// The left value. - /// The right value. - /// The interpolation value between the two values in the range of [0, 1]. - /// The interpolated value. - [MethodImpl(InliningOptions.ShortMethod)] - private static float LinearInterpolation(float left, float right, float t) - => left + ((right - left) * t); - - /// - /// Contains the results of the cumulative distribution function for all tiles. - /// - private sealed class CdfTileData : IDisposable - { - private readonly Configuration configuration; - private readonly MemoryAllocator memoryAllocator; - - // Used for storing the minimum value for each CDF entry. - private readonly Buffer2D cdfMinBuffer2D; - - // Used for storing the LUT for each CDF entry. - private readonly Buffer2D cdfLutBuffer2D; - private readonly int pixelsInTile; - private readonly int sourceWidth; - private readonly int sourceHeight; - private readonly int tileWidth; - private readonly int tileHeight; - private readonly int luminanceLevels; - private readonly List<(int y, int cdfY)> tileYStartPositions; - - public CdfTileData( - Configuration configuration, - int sourceWidth, - int sourceHeight, - int tileCountX, - int tileCountY, - int tileWidth, - int tileHeight, - int luminanceLevels) - { - this.configuration = configuration; - this.memoryAllocator = configuration.MemoryAllocator; - this.luminanceLevels = luminanceLevels; - this.cdfMinBuffer2D = this.memoryAllocator.Allocate2D(tileCountX, tileCountY); - this.cdfLutBuffer2D = this.memoryAllocator.Allocate2D(tileCountX * luminanceLevels, tileCountY); - this.sourceWidth = sourceWidth; - this.sourceHeight = sourceHeight; - this.tileWidth = tileWidth; - this.tileHeight = tileHeight; - this.pixelsInTile = tileWidth * tileHeight; - - // Calculate the start positions and rent buffers. - this.tileYStartPositions = new List<(int y, int cdfY)>(); - int cdfY = 0; - for (int y = 0; y < sourceHeight; y += tileHeight) - { - this.tileYStartPositions.Add((y, cdfY)); - cdfY++; - } - } - - public void CalculateLookupTables(ImageFrame source, HistogramEqualizationProcessor processor) - { - int sourceWidth = this.sourceWidth; - int sourceHeight = this.sourceHeight; - int tileWidth = this.tileWidth; - int tileHeight = this.tileHeight; - int luminanceLevels = this.luminanceLevels; - MemoryAllocator memoryAllocator = this.memoryAllocator; - - Parallel.For( - 0, - this.tileYStartPositions.Count, - new ParallelOptions() { MaxDegreeOfParallelism = this.configuration.MaxDegreeOfParallelism }, - index => - { - int cdfX = 0; - int cdfY = this.tileYStartPositions[index].cdfY; - int y = this.tileYStartPositions[index].y; - int endY = Math.Min(y + tileHeight, sourceHeight); - ref TPixel sourceBase = ref source.GetPixelReference(0, 0); - ref int cdfMinBase = ref MemoryMarshal.GetReference(this.cdfMinBuffer2D.GetRowSpan(cdfY)); - - using (IMemoryOwner histogramBuffer = this.memoryAllocator.Allocate(luminanceLevels)) - { - Span histogram = histogramBuffer.GetSpan(); - ref int histogramBase = ref MemoryMarshal.GetReference(histogram); - - for (int x = 0; x < sourceWidth; x += tileWidth) - { - histogram.Clear(); - ref int cdfBase = ref MemoryMarshal.GetReference(this.GetCdfLutSpan(cdfX, index)); - - int xlimit = Math.Min(x + tileWidth, sourceWidth); - for (int dy = y; dy < endY; dy++) - { - int dyOffset = dy * sourceWidth; - for (int dx = x; dx < xlimit; dx++) - { - int luminace = GetLuminance(Unsafe.Add(ref sourceBase, dyOffset + dx), luminanceLevels); - histogram[luminace]++; - } - } - - if (processor.ClipHistogramEnabled) - { - processor.ClipHistogram(histogram, processor.ClipLimitPercentage, this.pixelsInTile); - } - - Unsafe.Add(ref cdfMinBase, cdfX) = processor.CalculateCdf(ref cdfBase, ref histogramBase, histogram.Length - 1); - - cdfX++; - } - } - }); - } - - [MethodImpl(InliningOptions.ShortMethod)] - public Span GetCdfLutSpan(int tileX, int tileY) => this.cdfLutBuffer2D.GetRowSpan(tileY).Slice(tileX * this.luminanceLevels, this.luminanceLevels); - - /// - /// Remaps the grey value with the cdf. - /// - /// The tiles x-position. - /// The tiles y-position. - /// The original luminance. - /// The remapped luminance. - [MethodImpl(InliningOptions.ShortMethod)] - public float RemapGreyValue(int tilesX, int tilesY, int luminance) - { - int cdfMin = this.cdfMinBuffer2D[tilesX, tilesY]; - Span cdfSpan = this.GetCdfLutSpan(tilesX, tilesY); - return (this.pixelsInTile - cdfMin) == 0 - ? cdfSpan[luminance] / this.pixelsInTile - : cdfSpan[luminance] / (float)(this.pixelsInTile - cdfMin); - } - - public void Dispose() - { - this.cdfMinBuffer2D.Dispose(); - this.cdfLutBuffer2D.Dispose(); - } + return new AdaptiveHistEqualizationProcessor(this.LuminanceLevels, this.ClipHistogram, this.ClipLimitPercentage, this.NumberOfTiles); } } } diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor{TPixel}.cs new file mode 100644 index 0000000000..930c4010fa --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor{TPixel}.cs @@ -0,0 +1,546 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.Collections.Generic; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Memory; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Normalization +{ + /// + /// Applies an adaptive histogram equalization to the image. The image is split up in tiles. For each tile a cumulative distribution function (cdf) is calculated. + /// To calculate the final equalized pixel value, the cdf value of four adjacent tiles will be interpolated. + /// + /// The pixel format. + internal class AdaptiveHistEqualizationProcessor : HistogramEqualizationProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images + /// or 65536 for 16-bit grayscale images. + /// Indicating whether to clip the histogram bins at a specific value. + /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. + /// The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100. + public AdaptiveHistEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int tiles) + : base(luminanceLevels, clipHistogram, clipLimitPercentage) + { + Guard.MustBeGreaterThanOrEqualTo(tiles, 2, nameof(tiles)); + Guard.MustBeLessThanOrEqualTo(tiles, 100, nameof(tiles)); + + this.Tiles = tiles; + } + + /// + /// Gets the number of tiles the image is split into (horizontal and vertically) for the adaptive histogram equalization. + /// + private int Tiles { get; } + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + int sourceWidth = source.Width; + int sourceHeight = source.Height; + int numberOfPixels = sourceWidth * sourceHeight; + int tileWidth = (int)MathF.Ceiling(sourceWidth / (float)this.Tiles); + int tileHeight = (int)MathF.Ceiling(sourceHeight / (float)this.Tiles); + int pixelsInTile = tileWidth * tileHeight; + int halfTileWidth = tileWidth / 2; + int halfTileHeight = tileHeight / 2; + int luminanceLevels = this.LuminanceLevels; + + // The image is split up into tiles. For each tile the cumulative distribution function will be calculated. + using (var cdfData = new CdfTileData(configuration, sourceWidth, sourceHeight, this.Tiles, this.Tiles, tileWidth, tileHeight, luminanceLevels)) + { + cdfData.CalculateLookupTables(source, this); + + var tileYStartPositions = new List<(int y, int cdfY)>(); + int cdfY = 0; + for (int y = halfTileHeight; y < sourceHeight - halfTileHeight; y += tileHeight) + { + tileYStartPositions.Add((y, cdfY)); + cdfY++; + } + + Parallel.For( + 0, + tileYStartPositions.Count, + new ParallelOptions() { MaxDegreeOfParallelism = configuration.MaxDegreeOfParallelism }, + index => + { + int cdfX = 0; + int tileX = 0; + int tileY = 0; + int y = tileYStartPositions[index].y; + int cdfYY = tileYStartPositions[index].cdfY; + + // It's unfortunate that we have to do this per iteration. + ref TPixel sourceBase = ref source.GetPixelReference(0, 0); + + cdfX = 0; + for (int x = halfTileWidth; x < sourceWidth - halfTileWidth; x += tileWidth) + { + tileY = 0; + int yEnd = Math.Min(y + tileHeight, sourceHeight); + int xEnd = Math.Min(x + tileWidth, sourceWidth); + for (int dy = y; dy < yEnd; dy++) + { + int dyOffSet = dy * sourceWidth; + tileX = 0; + for (int dx = x; dx < xEnd; dx++) + { + ref TPixel pixel = ref Unsafe.Add(ref sourceBase, dyOffSet + dx); + float luminanceEqualized = InterpolateBetweenFourTiles( + pixel, + cdfData, + this.Tiles, + this.Tiles, + tileX, + tileY, + cdfX, + cdfYY, + tileWidth, + tileHeight, + luminanceLevels); + + pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); + tileX++; + } + + tileY++; + } + + cdfX++; + } + }); + + ref TPixel pixelsBase = ref source.GetPixelReference(0, 0); + + // Fix left column + ProcessBorderColumn(ref pixelsBase, cdfData, 0, sourceWidth, sourceHeight, tileWidth, tileHeight, xStart: 0, xEnd: halfTileWidth, luminanceLevels); + + // Fix right column + int rightBorderStartX = ((this.Tiles - 1) * tileWidth) + halfTileWidth; + ProcessBorderColumn(ref pixelsBase, cdfData, this.Tiles - 1, sourceWidth, sourceHeight, tileWidth, tileHeight, xStart: rightBorderStartX, xEnd: sourceWidth, luminanceLevels); + + // Fix top row + ProcessBorderRow(ref pixelsBase, cdfData, 0, sourceWidth, tileWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); + + // Fix bottom row + int bottomBorderStartY = ((this.Tiles - 1) * tileHeight) + halfTileHeight; + ProcessBorderRow(ref pixelsBase, cdfData, this.Tiles - 1, sourceWidth, tileWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels); + + // Left top corner + ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, 0, 0, xStart: 0, xEnd: halfTileWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); + + // Left bottom corner + ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, 0, this.Tiles - 1, xStart: 0, xEnd: halfTileWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels); + + // Right top corner + ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, this.Tiles - 1, 0, xStart: rightBorderStartX, xEnd: sourceWidth, yStart: 0, yEnd: halfTileHeight, luminanceLevels); + + // Right bottom corner + ProcessCornerTile(ref pixelsBase, cdfData, sourceWidth, this.Tiles - 1, this.Tiles - 1, xStart: rightBorderStartX, xEnd: sourceWidth, yStart: bottomBorderStartY, yEnd: sourceHeight, luminanceLevels); + } + } + + /// + /// Processes the part of a corner tile which was previously left out. It consists of 1 / 4 of a tile and does not need interpolation. + /// + /// The output pixels base reference. + /// The lookup table to remap the grey values. + /// The source image width. + /// The x-position in the CDF lookup map. + /// The y-position in the CDF lookup map. + /// X start position. + /// X end position. + /// Y start position. + /// Y end position. + /// + /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images + /// or 65536 for 16-bit grayscale images. + /// + private static void ProcessCornerTile( + ref TPixel pixelsBase, + CdfTileData cdfData, + int sourceWidth, + int cdfX, + int cdfY, + int xStart, + int xEnd, + int yStart, + int yEnd, + int luminanceLevels) + { + for (int dy = yStart; dy < yEnd; dy++) + { + int dyOffSet = dy * sourceWidth; + for (int dx = xStart; dx < xEnd; dx++) + { + ref TPixel pixel = ref Unsafe.Add(ref pixelsBase, dyOffSet + dx); + float luminanceEqualized = cdfData.RemapGreyValue(cdfX, cdfY, GetLuminance(pixel, luminanceLevels)); + pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); + } + } + } + + /// + /// Processes a border column of the image which is half the size of the tile width. + /// + /// The output pixels reference. + /// The pre-computed lookup tables to remap the grey values for each tiles. + /// The X index of the lookup table to use. + /// The source image width. + /// The source image height. + /// The width of a tile. + /// The height of a tile. + /// X start position in the image. + /// X end position of the image. + /// + /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images + /// or 65536 for 16-bit grayscale images. + /// + private static void ProcessBorderColumn( + ref TPixel pixelBase, + CdfTileData cdfData, + int cdfX, + int sourceWidth, + int sourceHeight, + int tileWidth, + int tileHeight, + int xStart, + int xEnd, + int luminanceLevels) + { + int halfTileWidth = tileWidth / 2; + int halfTileHeight = tileHeight / 2; + + int cdfY = 0; + for (int y = halfTileHeight; y < sourceHeight - halfTileHeight; y += tileHeight) + { + int yLimit = Math.Min(y + tileHeight, sourceHeight - 1); + int tileY = 0; + for (int dy = y; dy < yLimit; dy++) + { + int dyOffSet = dy * sourceWidth; + int tileX = halfTileWidth; + for (int dx = xStart; dx < xEnd; dx++) + { + ref TPixel pixel = ref Unsafe.Add(ref pixelBase, dyOffSet + dx); + float luminanceEqualized = InterpolateBetweenTwoTiles(pixel, cdfData, cdfX, cdfY, cdfX, cdfY + 1, tileY, tileHeight, luminanceLevels); + pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); + tileX++; + } + + tileY++; + } + + cdfY++; + } + } + + /// + /// Processes a border row of the image which is half of the size of the tile height. + /// + /// The output pixels base reference. + /// The pre-computed lookup tables to remap the grey values for each tiles. + /// The Y index of the lookup table to use. + /// The source image width. + /// The width of a tile. + /// Y start position in the image. + /// Y end position of the image. + /// + /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images + /// or 65536 for 16-bit grayscale images. + /// + private static void ProcessBorderRow( + ref TPixel pixelBase, + CdfTileData cdfData, + int cdfY, + int sourceWidth, + int tileWidth, + int yStart, + int yEnd, + int luminanceLevels) + { + int halfTileWidth = tileWidth / 2; + + int cdfX = 0; + for (int x = halfTileWidth; x < sourceWidth - halfTileWidth; x += tileWidth) + { + int tileY = 0; + for (int dy = yStart; dy < yEnd; dy++) + { + int dyOffSet = dy * sourceWidth; + int tileX = 0; + int xLimit = Math.Min(x + tileWidth, sourceWidth - 1); + for (int dx = x; dx < xLimit; dx++) + { + ref TPixel pixel = ref Unsafe.Add(ref pixelBase, dyOffSet + dx); + float luminanceEqualized = InterpolateBetweenTwoTiles(pixel, cdfData, cdfX, cdfY, cdfX + 1, cdfY, tileX, tileWidth, luminanceLevels); + pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); + tileX++; + } + + tileY++; + } + + cdfX++; + } + } + + /// + /// Bilinear interpolation between four adjacent tiles. + /// + /// The pixel to remap the grey value from. + /// The pre-computed lookup tables to remap the grey values for each tiles. + /// The number of tiles in the x-direction. + /// The number of tiles in the y-direction. + /// X position inside the tile. + /// Y position inside the tile. + /// X index of the top left lookup table to use. + /// Y index of the top left lookup table to use. + /// Width of one tile in pixels. + /// Height of one tile in pixels. + /// + /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images + /// or 65536 for 16-bit grayscale images. + /// + /// A re-mapped grey value. + [MethodImpl(InliningOptions.ShortMethod)] + private static float InterpolateBetweenFourTiles( + TPixel sourcePixel, + CdfTileData cdfData, + int tileCountX, + int tileCountY, + int tileX, + int tileY, + int cdfX, + int cdfY, + int tileWidth, + int tileHeight, + int luminanceLevels) + { + int luminance = GetLuminance(sourcePixel, luminanceLevels); + float tx = tileX / (float)(tileWidth - 1); + float ty = tileY / (float)(tileHeight - 1); + + int yTop = cdfY; + int yBottom = Math.Min(tileCountY - 1, yTop + 1); + int xLeft = cdfX; + int xRight = Math.Min(tileCountX - 1, xLeft + 1); + + float cdfLeftTopLuminance = cdfData.RemapGreyValue(xLeft, yTop, luminance); + float cdfRightTopLuminance = cdfData.RemapGreyValue(xRight, yTop, luminance); + float cdfLeftBottomLuminance = cdfData.RemapGreyValue(xLeft, yBottom, luminance); + float cdfRightBottomLuminance = cdfData.RemapGreyValue(xRight, yBottom, luminance); + return BilinearInterpolation(tx, ty, cdfLeftTopLuminance, cdfRightTopLuminance, cdfLeftBottomLuminance, cdfRightBottomLuminance); + } + + /// + /// Linear interpolation between two tiles. + /// + /// The pixel to remap the grey value from. + /// The CDF lookup map. + /// X position inside the first tile. + /// Y position inside the first tile. + /// X position inside the second tile. + /// Y position inside the second tile. + /// Position inside the tile. + /// Width of the tile. + /// + /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images + /// or 65536 for 16-bit grayscale images. + /// + /// A re-mapped grey value. + [MethodImpl(InliningOptions.ShortMethod)] + private static float InterpolateBetweenTwoTiles( + TPixel sourcePixel, + CdfTileData cdfData, + int tileX1, + int tileY1, + int tileX2, + int tileY2, + int tilePos, + int tileWidth, + int luminanceLevels) + { + int luminance = GetLuminance(sourcePixel, luminanceLevels); + float tx = tilePos / (float)(tileWidth - 1); + + float cdfLuminance1 = cdfData.RemapGreyValue(tileX1, tileY1, luminance); + float cdfLuminance2 = cdfData.RemapGreyValue(tileX2, tileY2, luminance); + return LinearInterpolation(cdfLuminance1, cdfLuminance2, tx); + } + + /// + /// Bilinear interpolation between four tiles. + /// + /// The interpolation value in x direction in the range of [0, 1]. + /// The interpolation value in y direction in the range of [0, 1]. + /// Luminance from top left tile. + /// Luminance from right top tile. + /// Luminance from left bottom tile. + /// Luminance from right bottom tile. + /// Interpolated Luminance. + [MethodImpl(InliningOptions.ShortMethod)] + private static float BilinearInterpolation(float tx, float ty, float lt, float rt, float lb, float rb) + => LinearInterpolation(LinearInterpolation(lt, rt, tx), LinearInterpolation(lb, rb, tx), ty); + + /// + /// Linear interpolation between two grey values. + /// + /// The left value. + /// The right value. + /// The interpolation value between the two values in the range of [0, 1]. + /// The interpolated value. + [MethodImpl(InliningOptions.ShortMethod)] + private static float LinearInterpolation(float left, float right, float t) + => left + ((right - left) * t); + + /// + /// Contains the results of the cumulative distribution function for all tiles. + /// + private sealed class CdfTileData : IDisposable + { + private readonly Configuration configuration; + private readonly MemoryAllocator memoryAllocator; + + // Used for storing the minimum value for each CDF entry. + private readonly Buffer2D cdfMinBuffer2D; + + // Used for storing the LUT for each CDF entry. + private readonly Buffer2D cdfLutBuffer2D; + private readonly int pixelsInTile; + private readonly int sourceWidth; + private readonly int sourceHeight; + private readonly int tileWidth; + private readonly int tileHeight; + private readonly int luminanceLevels; + private readonly List<(int y, int cdfY)> tileYStartPositions; + + public CdfTileData( + Configuration configuration, + int sourceWidth, + int sourceHeight, + int tileCountX, + int tileCountY, + int tileWidth, + int tileHeight, + int luminanceLevels) + { + this.configuration = configuration; + this.memoryAllocator = configuration.MemoryAllocator; + this.luminanceLevels = luminanceLevels; + this.cdfMinBuffer2D = this.memoryAllocator.Allocate2D(tileCountX, tileCountY); + this.cdfLutBuffer2D = this.memoryAllocator.Allocate2D(tileCountX * luminanceLevels, tileCountY); + this.sourceWidth = sourceWidth; + this.sourceHeight = sourceHeight; + this.tileWidth = tileWidth; + this.tileHeight = tileHeight; + this.pixelsInTile = tileWidth * tileHeight; + + // Calculate the start positions and rent buffers. + this.tileYStartPositions = new List<(int y, int cdfY)>(); + int cdfY = 0; + for (int y = 0; y < sourceHeight; y += tileHeight) + { + this.tileYStartPositions.Add((y, cdfY)); + cdfY++; + } + } + + public void CalculateLookupTables(ImageFrame source, HistogramEqualizationProcessor processor) + { + int sourceWidth = this.sourceWidth; + int sourceHeight = this.sourceHeight; + int tileWidth = this.tileWidth; + int tileHeight = this.tileHeight; + int luminanceLevels = this.luminanceLevels; + MemoryAllocator memoryAllocator = this.memoryAllocator; + + Parallel.For( + 0, + this.tileYStartPositions.Count, + new ParallelOptions() { MaxDegreeOfParallelism = this.configuration.MaxDegreeOfParallelism }, + index => + { + int cdfX = 0; + int cdfY = this.tileYStartPositions[index].cdfY; + int y = this.tileYStartPositions[index].y; + int endY = Math.Min(y + tileHeight, sourceHeight); + ref TPixel sourceBase = ref source.GetPixelReference(0, 0); + ref int cdfMinBase = ref MemoryMarshal.GetReference(this.cdfMinBuffer2D.GetRowSpan(cdfY)); + + using (IMemoryOwner histogramBuffer = this.memoryAllocator.Allocate(luminanceLevels)) + { + Span histogram = histogramBuffer.GetSpan(); + ref int histogramBase = ref MemoryMarshal.GetReference(histogram); + + for (int x = 0; x < sourceWidth; x += tileWidth) + { + histogram.Clear(); + ref int cdfBase = ref MemoryMarshal.GetReference(this.GetCdfLutSpan(cdfX, index)); + + int xlimit = Math.Min(x + tileWidth, sourceWidth); + for (int dy = y; dy < endY; dy++) + { + int dyOffset = dy * sourceWidth; + for (int dx = x; dx < xlimit; dx++) + { + int luminace = GetLuminance(Unsafe.Add(ref sourceBase, dyOffset + dx), luminanceLevels); + histogram[luminace]++; + } + } + + if (processor.ClipHistogramEnabled) + { + processor.ClipHistogram(histogram, processor.ClipLimitPercentage, this.pixelsInTile); + } + + Unsafe.Add(ref cdfMinBase, cdfX) = processor.CalculateCdf(ref cdfBase, ref histogramBase, histogram.Length - 1); + + cdfX++; + } + } + }); + } + + [MethodImpl(InliningOptions.ShortMethod)] + public Span GetCdfLutSpan(int tileX, int tileY) => this.cdfLutBuffer2D.GetRowSpan(tileY).Slice(tileX * this.luminanceLevels, this.luminanceLevels); + + /// + /// Remaps the grey value with the cdf. + /// + /// The tiles x-position. + /// The tiles y-position. + /// The original luminance. + /// The remapped luminance. + [MethodImpl(InliningOptions.ShortMethod)] + public float RemapGreyValue(int tilesX, int tilesY, int luminance) + { + int cdfMin = this.cdfMinBuffer2D[tilesX, tilesY]; + Span cdfSpan = this.GetCdfLutSpan(tilesX, tilesY); + return (this.pixelsInTile - cdfMin) == 0 + ? cdfSpan[luminance] / this.pixelsInTile + : cdfSpan[luminance] / (float)(this.pixelsInTile - cdfMin); + } + + public void Dispose() + { + this.cdfMinBuffer2D.Dispose(); + this.cdfLutBuffer2D.Dispose(); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs index aa9b530c6d..cd4a9644ff 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs @@ -1,389 +1,36 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Buffers; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Threading.Tasks; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Normalization { /// /// Applies an adaptive histogram equalization to the image using an sliding window approach. /// - /// The pixel format. - internal class AdaptiveHistEqualizationSWProcessor : HistogramEqualizationProcessor - where TPixel : struct, IPixel + internal class AdaptiveHistEqualizationSWProcessor : HistogramEqualizationProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// or 65536 for 16-bit grayscale images. /// Indicating whether to clip the histogram bins at a specific value. /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. - /// The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100. - public AdaptiveHistEqualizationSWProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int tiles) + /// The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100. + public AdaptiveHistEqualizationSWProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int numberOfTiles) : base(luminanceLevels, clipHistogram, clipLimitPercentage) { - Guard.MustBeGreaterThanOrEqualTo(tiles, 2, nameof(tiles)); - Guard.MustBeLessThanOrEqualTo(tiles, 100, nameof(tiles)); - - this.Tiles = tiles; + this.NumberOfTiles = numberOfTiles; } /// /// Gets the number of tiles the image is split into (horizontal and vertically) for the adaptive histogram equalization. /// - private int Tiles { get; } - - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) - { - MemoryAllocator memoryAllocator = configuration.MemoryAllocator; - int numberOfPixels = source.Width * source.Height; - Span pixels = source.GetPixelSpan(); - - var parallelOptions = new ParallelOptions() { MaxDegreeOfParallelism = configuration.MaxDegreeOfParallelism }; - int tileWidth = source.Width / this.Tiles; - int tileHeight = tileWidth; - int pixeInTile = tileWidth * tileHeight; - int halfTileHeight = tileHeight / 2; - int halfTileWidth = halfTileHeight; - var slidingWindowInfos = new SlidingWindowInfos(tileWidth, tileHeight, halfTileWidth, halfTileHeight, pixeInTile); - - using (Buffer2D targetPixels = configuration.MemoryAllocator.Allocate2D(source.Width, source.Height)) - { - // Process the inner tiles, which do not require to check the borders. - Parallel.For( - halfTileWidth, - source.Width - halfTileWidth, - parallelOptions, - this.ProcessSlidingWindow( - source, - memoryAllocator, - targetPixels, - slidingWindowInfos, - yStart: halfTileHeight, - yEnd: source.Height - halfTileHeight, - useFastPath: true, - configuration)); - - // Process the left border of the image. - Parallel.For( - 0, - halfTileWidth, - parallelOptions, - this.ProcessSlidingWindow( - source, - memoryAllocator, - targetPixels, - slidingWindowInfos, - yStart: 0, - yEnd: source.Height, - useFastPath: false, - configuration)); - - // Process the right border of the image. - Parallel.For( - source.Width - halfTileWidth, - source.Width, - parallelOptions, - this.ProcessSlidingWindow( - source, - memoryAllocator, - targetPixels, - slidingWindowInfos, - yStart: 0, - yEnd: source.Height, - useFastPath: false, - configuration)); - - // Process the top border of the image. - Parallel.For( - halfTileWidth, - source.Width - halfTileWidth, - parallelOptions, - this.ProcessSlidingWindow( - source, - memoryAllocator, - targetPixels, - slidingWindowInfos, - yStart: 0, - yEnd: halfTileHeight, - useFastPath: false, - configuration)); - - // Process the bottom border of the image. - Parallel.For( - halfTileWidth, - source.Width - halfTileWidth, - parallelOptions, - this.ProcessSlidingWindow( - source, - memoryAllocator, - targetPixels, - slidingWindowInfos, - yStart: source.Height - halfTileHeight, - yEnd: source.Height, - useFastPath: false, - configuration)); - - Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels); - } - } - - /// - /// Applies the sliding window equalization to one column of the image. The window is moved from top to bottom. - /// Moving the window one pixel down requires to remove one row from the top of the window from the histogram and - /// adding a new row at the bottom. - /// - /// The source image. - /// The memory allocator. - /// The target pixels. - /// Informations about the sliding window dimensions. - /// The y start position. - /// The y end position. - /// if set to true the borders of the image will not be checked. - /// The configuration. - /// Action Delegate. - private Action ProcessSlidingWindow( - ImageFrame source, - MemoryAllocator memoryAllocator, - Buffer2D targetPixels, - SlidingWindowInfos swInfos, - int yStart, - int yEnd, - bool useFastPath, - Configuration configuration) - { - return x => - { - using (IMemoryOwner histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) - using (IMemoryOwner histogramBufferCopy = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) - using (IMemoryOwner cdfBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) - using (IMemoryOwner pixelRowBuffer = memoryAllocator.Allocate(swInfos.TileWidth, AllocationOptions.Clean)) - { - Span histogram = histogramBuffer.GetSpan(); - ref int histogramBase = ref MemoryMarshal.GetReference(histogram); - - Span histogramCopy = histogramBufferCopy.GetSpan(); - ref int histogramCopyBase = ref MemoryMarshal.GetReference(histogramCopy); - - ref int cdfBase = ref MemoryMarshal.GetReference(cdfBuffer.GetSpan()); - - Span pixelRow = pixelRowBuffer.GetSpan(); - ref Vector4 pixelRowBase = ref MemoryMarshal.GetReference(pixelRow); - - // Build the initial histogram of grayscale values. - for (int dy = yStart - swInfos.HalfTileHeight; dy < yStart + swInfos.HalfTileHeight; dy++) - { - if (useFastPath) - { - this.CopyPixelRowFast(source, pixelRow, x - swInfos.HalfTileWidth, dy, swInfos.TileWidth, configuration); - } - else - { - this.CopyPixelRow(source, pixelRow, x - swInfos.HalfTileWidth, dy, swInfos.TileWidth, configuration); - } - - this.AddPixelsToHistogram(ref pixelRowBase, ref histogramBase, this.LuminanceLevels, pixelRow.Length); - } - - for (int y = yStart; y < yEnd; y++) - { - if (this.ClipHistogramEnabled) - { - // Clipping the histogram, but doing it on a copy to keep the original un-clipped values for the next iteration. - histogram.CopyTo(histogramCopy); - this.ClipHistogram(histogramCopy, this.ClipLimitPercentage, swInfos.PixeInTile); - } - - // Calculate the cumulative distribution function, which will map each input pixel in the current tile to a new value. - int cdfMin = this.ClipHistogramEnabled - ? this.CalculateCdf(ref cdfBase, ref histogramCopyBase, histogram.Length - 1) - : this.CalculateCdf(ref cdfBase, ref histogramBase, histogram.Length - 1); - - float numberOfPixelsMinusCdfMin = swInfos.PixeInTile - cdfMin; - - // Map the current pixel to the new equalized value. - int luminance = GetLuminance(source[x, y], this.LuminanceLevels); - float luminanceEqualized = Unsafe.Add(ref cdfBase, luminance) / numberOfPixelsMinusCdfMin; - targetPixels[x, y].FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, source[x, y].ToVector4().W)); - - // Remove top most row from the histogram, mirroring rows which exceeds the borders. - if (useFastPath) - { - this.CopyPixelRowFast(source, pixelRow, x - swInfos.HalfTileWidth, y - swInfos.HalfTileWidth, swInfos.TileWidth, configuration); - } - else - { - this.CopyPixelRow(source, pixelRow, x - swInfos.HalfTileWidth, y - swInfos.HalfTileWidth, swInfos.TileWidth, configuration); - } - - this.RemovePixelsFromHistogram(ref pixelRowBase, ref histogramBase, this.LuminanceLevels, pixelRow.Length); - - // Add new bottom row to the histogram, mirroring rows which exceeds the borders. - if (useFastPath) - { - this.CopyPixelRowFast(source, pixelRow, x - swInfos.HalfTileWidth, y + swInfos.HalfTileWidth, swInfos.TileWidth, configuration); - } - else - { - this.CopyPixelRow(source, pixelRow, x - swInfos.HalfTileWidth, y + swInfos.HalfTileWidth, swInfos.TileWidth, configuration); - } - - this.AddPixelsToHistogram(ref pixelRowBase, ref histogramBase, this.LuminanceLevels, pixelRow.Length); - } - } - }; - } - - /// - /// Get the a pixel row at a given position with a length of the tile width. Mirrors pixels which exceeds the edges. - /// - /// The source image. - /// Pre-allocated pixel row span of the size of a the tile width. - /// The x position. - /// The y position. - /// The width in pixels of a tile. - /// The configuration. - private void CopyPixelRow( - ImageFrame source, - Span rowPixels, - int x, - int y, - int tileWidth, - Configuration configuration) - { - if (y < 0) - { - y = ImageMaths.FastAbs(y); - } - else if (y >= source.Height) - { - int diff = y - source.Height; - y = source.Height - diff - 1; - } - - // Special cases for the left and the right border where GetPixelRowSpan can not be used. - if (x < 0) - { - rowPixels.Clear(); - int idx = 0; - for (int dx = x; dx < x + tileWidth; dx++) - { - rowPixels[idx] = source[ImageMaths.FastAbs(dx), y].ToVector4(); - idx++; - } - - return; - } - else if (x + tileWidth > source.Width) - { - rowPixels.Clear(); - int idx = 0; - for (int dx = x; dx < x + tileWidth; dx++) - { - if (dx >= source.Width) - { - int diff = dx - source.Width; - rowPixels[idx] = source[dx - diff - 1, y].ToVector4(); - } - else - { - rowPixels[idx] = source[dx, y].ToVector4(); - } - - idx++; - } + public int NumberOfTiles { get; } - return; - } - - this.CopyPixelRowFast(source, rowPixels, x, y, tileWidth, configuration); - } - - /// - /// Get the a pixel row at a given position with a length of the tile width. - /// - /// The source image. - /// Pre-allocated pixel row span of the size of a the tile width. - /// The x position. - /// The y position. - /// The width in pixels of a tile. - /// The configuration. - [MethodImpl(InliningOptions.ShortMethod)] - private void CopyPixelRowFast( - ImageFrame source, - Span rowPixels, - int x, - int y, - int tileWidth, - Configuration configuration) - => PixelOperations.Instance.ToVector4(configuration, source.GetPixelRowSpan(y).Slice(start: x, length: tileWidth), rowPixels); - - /// - /// Adds a column of grey values to the histogram. - /// - /// The reference to the span of grey values to add. - /// The reference to the histogram span. - /// The number of different luminance levels. - /// The grey values span length. - [MethodImpl(InliningOptions.ShortMethod)] - private void AddPixelsToHistogram(ref Vector4 greyValuesBase, ref int histogramBase, int luminanceLevels, int length) - { - for (int idx = 0; idx < length; idx++) - { - int luminance = GetLuminance(ref Unsafe.Add(ref greyValuesBase, idx), luminanceLevels); - Unsafe.Add(ref histogramBase, luminance)++; - } - } - - /// - /// Removes a column of grey values from the histogram. - /// - /// The reference to the span of grey values to remove. - /// The reference to the histogram span. - /// The number of different luminance levels. - /// The grey values span length. - [MethodImpl(InliningOptions.ShortMethod)] - private void RemovePixelsFromHistogram(ref Vector4 greyValuesBase, ref int histogramBase, int luminanceLevels, int length) - { - for (int idx = 0; idx < length; idx++) - { - int luminance = GetLuminance(ref Unsafe.Add(ref greyValuesBase, idx), luminanceLevels); - Unsafe.Add(ref histogramBase, luminance)--; - } - } - - private class SlidingWindowInfos + /// + public override IImageProcessor CreatePixelSpecificProcessor() { - public SlidingWindowInfos(int tileWidth, int tileHeight, int halfTileWidth, int halfTileHeight, int pixeInTile) - { - this.TileWidth = tileWidth; - this.TileHeight = tileHeight; - this.HalfTileWidth = halfTileWidth; - this.HalfTileHeight = halfTileHeight; - this.PixeInTile = pixeInTile; - } - - public int TileWidth { get; private set; } - - public int TileHeight { get; private set; } - - public int PixeInTile { get; private set; } - - public int HalfTileWidth { get; private set; } - - public int HalfTileHeight { get; private set; } + return new AdaptiveHistEqualizationSWProcessor(this.LuminanceLevels, this.ClipHistogram, this.ClipLimitPercentage, this.NumberOfTiles); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor{TPixel}.cs new file mode 100644 index 0000000000..3584b1a88b --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor{TPixel}.cs @@ -0,0 +1,390 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading.Tasks; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Memory; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Normalization +{ + /// + /// Applies an adaptive histogram equalization to the image using an sliding window approach. + /// + /// The pixel format. + internal class AdaptiveHistEqualizationSWProcessor : HistogramEqualizationProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images + /// or 65536 for 16-bit grayscale images. + /// Indicating whether to clip the histogram bins at a specific value. + /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. + /// The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100. + public AdaptiveHistEqualizationSWProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int tiles) + : base(luminanceLevels, clipHistogram, clipLimitPercentage) + { + Guard.MustBeGreaterThanOrEqualTo(tiles, 2, nameof(tiles)); + Guard.MustBeLessThanOrEqualTo(tiles, 100, nameof(tiles)); + + this.Tiles = tiles; + } + + /// + /// Gets the number of tiles the image is split into (horizontal and vertically) for the adaptive histogram equalization. + /// + private int Tiles { get; } + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + MemoryAllocator memoryAllocator = configuration.MemoryAllocator; + int numberOfPixels = source.Width * source.Height; + Span pixels = source.GetPixelSpan(); + + var parallelOptions = new ParallelOptions() { MaxDegreeOfParallelism = configuration.MaxDegreeOfParallelism }; + int tileWidth = source.Width / this.Tiles; + int tileHeight = tileWidth; + int pixeInTile = tileWidth * tileHeight; + int halfTileHeight = tileHeight / 2; + int halfTileWidth = halfTileHeight; + var slidingWindowInfos = new SlidingWindowInfos(tileWidth, tileHeight, halfTileWidth, halfTileHeight, pixeInTile); + + using (Buffer2D targetPixels = configuration.MemoryAllocator.Allocate2D(source.Width, source.Height)) + { + // Process the inner tiles, which do not require to check the borders. + Parallel.For( + halfTileWidth, + source.Width - halfTileWidth, + parallelOptions, + this.ProcessSlidingWindow( + source, + memoryAllocator, + targetPixels, + slidingWindowInfos, + yStart: halfTileHeight, + yEnd: source.Height - halfTileHeight, + useFastPath: true, + configuration)); + + // Process the left border of the image. + Parallel.For( + 0, + halfTileWidth, + parallelOptions, + this.ProcessSlidingWindow( + source, + memoryAllocator, + targetPixels, + slidingWindowInfos, + yStart: 0, + yEnd: source.Height, + useFastPath: false, + configuration)); + + // Process the right border of the image. + Parallel.For( + source.Width - halfTileWidth, + source.Width, + parallelOptions, + this.ProcessSlidingWindow( + source, + memoryAllocator, + targetPixels, + slidingWindowInfos, + yStart: 0, + yEnd: source.Height, + useFastPath: false, + configuration)); + + // Process the top border of the image. + Parallel.For( + halfTileWidth, + source.Width - halfTileWidth, + parallelOptions, + this.ProcessSlidingWindow( + source, + memoryAllocator, + targetPixels, + slidingWindowInfos, + yStart: 0, + yEnd: halfTileHeight, + useFastPath: false, + configuration)); + + // Process the bottom border of the image. + Parallel.For( + halfTileWidth, + source.Width - halfTileWidth, + parallelOptions, + this.ProcessSlidingWindow( + source, + memoryAllocator, + targetPixels, + slidingWindowInfos, + yStart: source.Height - halfTileHeight, + yEnd: source.Height, + useFastPath: false, + configuration)); + + Buffer2D.SwapOrCopyContent(source.PixelBuffer, targetPixels); + } + } + + /// + /// Applies the sliding window equalization to one column of the image. The window is moved from top to bottom. + /// Moving the window one pixel down requires to remove one row from the top of the window from the histogram and + /// adding a new row at the bottom. + /// + /// The source image. + /// The memory allocator. + /// The target pixels. + /// Informations about the sliding window dimensions. + /// The y start position. + /// The y end position. + /// if set to true the borders of the image will not be checked. + /// The configuration. + /// Action Delegate. + private Action ProcessSlidingWindow( + ImageFrame source, + MemoryAllocator memoryAllocator, + Buffer2D targetPixels, + SlidingWindowInfos swInfos, + int yStart, + int yEnd, + bool useFastPath, + Configuration configuration) + { + return x => + { + using (IMemoryOwner histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) + using (IMemoryOwner histogramBufferCopy = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) + using (IMemoryOwner cdfBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) + using (IMemoryOwner pixelRowBuffer = memoryAllocator.Allocate(swInfos.TileWidth, AllocationOptions.Clean)) + { + Span histogram = histogramBuffer.GetSpan(); + ref int histogramBase = ref MemoryMarshal.GetReference(histogram); + + Span histogramCopy = histogramBufferCopy.GetSpan(); + ref int histogramCopyBase = ref MemoryMarshal.GetReference(histogramCopy); + + ref int cdfBase = ref MemoryMarshal.GetReference(cdfBuffer.GetSpan()); + + Span pixelRow = pixelRowBuffer.GetSpan(); + ref Vector4 pixelRowBase = ref MemoryMarshal.GetReference(pixelRow); + + // Build the initial histogram of grayscale values. + for (int dy = yStart - swInfos.HalfTileHeight; dy < yStart + swInfos.HalfTileHeight; dy++) + { + if (useFastPath) + { + this.CopyPixelRowFast(source, pixelRow, x - swInfos.HalfTileWidth, dy, swInfos.TileWidth, configuration); + } + else + { + this.CopyPixelRow(source, pixelRow, x - swInfos.HalfTileWidth, dy, swInfos.TileWidth, configuration); + } + + this.AddPixelsToHistogram(ref pixelRowBase, ref histogramBase, this.LuminanceLevels, pixelRow.Length); + } + + for (int y = yStart; y < yEnd; y++) + { + if (this.ClipHistogramEnabled) + { + // Clipping the histogram, but doing it on a copy to keep the original un-clipped values for the next iteration. + histogram.CopyTo(histogramCopy); + this.ClipHistogram(histogramCopy, this.ClipLimitPercentage, swInfos.PixeInTile); + } + + // Calculate the cumulative distribution function, which will map each input pixel in the current tile to a new value. + int cdfMin = this.ClipHistogramEnabled + ? this.CalculateCdf(ref cdfBase, ref histogramCopyBase, histogram.Length - 1) + : this.CalculateCdf(ref cdfBase, ref histogramBase, histogram.Length - 1); + + float numberOfPixelsMinusCdfMin = swInfos.PixeInTile - cdfMin; + + // Map the current pixel to the new equalized value. + int luminance = GetLuminance(source[x, y], this.LuminanceLevels); + float luminanceEqualized = Unsafe.Add(ref cdfBase, luminance) / numberOfPixelsMinusCdfMin; + targetPixels[x, y].FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, source[x, y].ToVector4().W)); + + // Remove top most row from the histogram, mirroring rows which exceeds the borders. + if (useFastPath) + { + this.CopyPixelRowFast(source, pixelRow, x - swInfos.HalfTileWidth, y - swInfos.HalfTileWidth, swInfos.TileWidth, configuration); + } + else + { + this.CopyPixelRow(source, pixelRow, x - swInfos.HalfTileWidth, y - swInfos.HalfTileWidth, swInfos.TileWidth, configuration); + } + + this.RemovePixelsFromHistogram(ref pixelRowBase, ref histogramBase, this.LuminanceLevels, pixelRow.Length); + + // Add new bottom row to the histogram, mirroring rows which exceeds the borders. + if (useFastPath) + { + this.CopyPixelRowFast(source, pixelRow, x - swInfos.HalfTileWidth, y + swInfos.HalfTileWidth, swInfos.TileWidth, configuration); + } + else + { + this.CopyPixelRow(source, pixelRow, x - swInfos.HalfTileWidth, y + swInfos.HalfTileWidth, swInfos.TileWidth, configuration); + } + + this.AddPixelsToHistogram(ref pixelRowBase, ref histogramBase, this.LuminanceLevels, pixelRow.Length); + } + } + }; + } + + /// + /// Get the a pixel row at a given position with a length of the tile width. Mirrors pixels which exceeds the edges. + /// + /// The source image. + /// Pre-allocated pixel row span of the size of a the tile width. + /// The x position. + /// The y position. + /// The width in pixels of a tile. + /// The configuration. + private void CopyPixelRow( + ImageFrame source, + Span rowPixels, + int x, + int y, + int tileWidth, + Configuration configuration) + { + if (y < 0) + { + y = ImageMaths.FastAbs(y); + } + else if (y >= source.Height) + { + int diff = y - source.Height; + y = source.Height - diff - 1; + } + + // Special cases for the left and the right border where GetPixelRowSpan can not be used. + if (x < 0) + { + rowPixels.Clear(); + int idx = 0; + for (int dx = x; dx < x + tileWidth; dx++) + { + rowPixels[idx] = source[ImageMaths.FastAbs(dx), y].ToVector4(); + idx++; + } + + return; + } + else if (x + tileWidth > source.Width) + { + rowPixels.Clear(); + int idx = 0; + for (int dx = x; dx < x + tileWidth; dx++) + { + if (dx >= source.Width) + { + int diff = dx - source.Width; + rowPixels[idx] = source[dx - diff - 1, y].ToVector4(); + } + else + { + rowPixels[idx] = source[dx, y].ToVector4(); + } + + idx++; + } + + return; + } + + this.CopyPixelRowFast(source, rowPixels, x, y, tileWidth, configuration); + } + + /// + /// Get the a pixel row at a given position with a length of the tile width. + /// + /// The source image. + /// Pre-allocated pixel row span of the size of a the tile width. + /// The x position. + /// The y position. + /// The width in pixels of a tile. + /// The configuration. + [MethodImpl(InliningOptions.ShortMethod)] + private void CopyPixelRowFast( + ImageFrame source, + Span rowPixels, + int x, + int y, + int tileWidth, + Configuration configuration) + => PixelOperations.Instance.ToVector4(configuration, source.GetPixelRowSpan(y).Slice(start: x, length: tileWidth), rowPixels); + + /// + /// Adds a column of grey values to the histogram. + /// + /// The reference to the span of grey values to add. + /// The reference to the histogram span. + /// The number of different luminance levels. + /// The grey values span length. + [MethodImpl(InliningOptions.ShortMethod)] + private void AddPixelsToHistogram(ref Vector4 greyValuesBase, ref int histogramBase, int luminanceLevels, int length) + { + for (int idx = 0; idx < length; idx++) + { + int luminance = GetLuminance(ref Unsafe.Add(ref greyValuesBase, idx), luminanceLevels); + Unsafe.Add(ref histogramBase, luminance)++; + } + } + + /// + /// Removes a column of grey values from the histogram. + /// + /// The reference to the span of grey values to remove. + /// The reference to the histogram span. + /// The number of different luminance levels. + /// The grey values span length. + [MethodImpl(InliningOptions.ShortMethod)] + private void RemovePixelsFromHistogram(ref Vector4 greyValuesBase, ref int histogramBase, int luminanceLevels, int length) + { + for (int idx = 0; idx < length; idx++) + { + int luminance = GetLuminance(ref Unsafe.Add(ref greyValuesBase, idx), luminanceLevels); + Unsafe.Add(ref histogramBase, luminance)--; + } + } + + private class SlidingWindowInfos + { + public SlidingWindowInfos(int tileWidth, int tileHeight, int halfTileWidth, int halfTileHeight, int pixeInTile) + { + this.TileWidth = tileWidth; + this.TileHeight = tileHeight; + this.HalfTileWidth = halfTileWidth; + this.HalfTileHeight = halfTileHeight; + this.PixeInTile = pixeInTile; + } + + public int TileWidth { get; private set; } + + public int TileHeight { get; private set; } + + public int PixeInTile { get; private set; } + + public int HalfTileWidth { get; private set; } + + public int HalfTileHeight { get; private set; } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs index aadde2424b..62e0185950 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs @@ -1,106 +1,25 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Buffers; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Memory; -using SixLabors.ImageSharp.ParallelUtils; -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Memory; -using SixLabors.Primitives; - namespace SixLabors.ImageSharp.Processing.Processors.Normalization { /// - /// Applies a global histogram equalization to the image. + /// Defines a global histogram equalization applicable to an . /// - /// The pixel format. - internal class GlobalHistogramEqualizationProcessor : HistogramEqualizationProcessor - where TPixel : struct, IPixel + internal class GlobalHistogramEqualizationProcessor : HistogramEqualizationProcessor { - /// - /// Initializes a new instance of the class. - /// - /// - /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images - /// or 65536 for 16-bit grayscale images. - /// - /// Indicating whether to clip the histogram bins at a specific value. - /// Histogram clip limit in percent of the total pixels. Histogram bins which exceed this limit, will be capped at this value. public GlobalHistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage) : base(luminanceLevels, clipHistogram, clipLimitPercentage) { } - /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + /// + public override IImageProcessor CreatePixelSpecificProcessor() { - MemoryAllocator memoryAllocator = configuration.MemoryAllocator; - int numberOfPixels = source.Width * source.Height; - Span pixels = source.GetPixelSpan(); - var workingRect = new Rectangle(0, 0, source.Width, source.Height); - - using (IMemoryOwner histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) - using (IMemoryOwner cdfBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) - { - // Build the histogram of the grayscale levels. - ParallelHelper.IterateRows( - workingRect, - configuration, - rows => - { - ref int histogramBase = ref MemoryMarshal.GetReference(histogramBuffer.GetSpan()); - for (int y = rows.Min; y < rows.Max; y++) - { - ref TPixel pixelBase = ref MemoryMarshal.GetReference(source.GetPixelRowSpan(y)); - - for (int x = 0; x < workingRect.Width; x++) - { - int luminance = GetLuminance(Unsafe.Add(ref pixelBase, x), this.LuminanceLevels); - Unsafe.Add(ref histogramBase, luminance)++; - } - } - }); - - Span histogram = histogramBuffer.GetSpan(); - if (this.ClipHistogramEnabled) - { - this.ClipHistogram(histogram, this.ClipLimitPercentage, numberOfPixels); - } - - // Calculate the cumulative distribution function, which will map each input pixel to a new value. - int cdfMin = this.CalculateCdf( - ref MemoryMarshal.GetReference(cdfBuffer.GetSpan()), - ref MemoryMarshal.GetReference(histogram), - histogram.Length - 1); - - float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin; - - // Apply the cdf to each pixel of the image - ParallelHelper.IterateRows( - workingRect, - configuration, - rows => - { - ref int cdfBase = ref MemoryMarshal.GetReference(cdfBuffer.GetSpan()); - for (int y = rows.Min; y < rows.Max; y++) - { - ref TPixel pixelBase = ref MemoryMarshal.GetReference(source.GetPixelRowSpan(y)); - - for (int x = 0; x < workingRect.Width; x++) - { - ref TPixel pixel = ref Unsafe.Add(ref pixelBase, x); - int luminance = GetLuminance(pixel, this.LuminanceLevels); - float luminanceEqualized = Unsafe.Add(ref cdfBase, luminance) / numberOfPixelsMinusCdfMin; - pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); - } - } - }); - } + return new GlobalHistogramEqualizationProcessor( + this.LuminanceLevels, + this.ClipHistogram, + this.ClipLimitPercentage); } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs new file mode 100644 index 0000000000..b3a1603e6f --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor{TPixel}.cs @@ -0,0 +1,107 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Buffers; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.ParallelUtils; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Memory; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Normalization +{ + /// + /// Applies a global histogram equalization to the image. + /// + /// The pixel format. + internal class GlobalHistogramEqualizationProcessor : HistogramEqualizationProcessor + where TPixel : struct, IPixel + { + /// + /// Initializes a new instance of the class. + /// + /// + /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images + /// or 65536 for 16-bit grayscale images. + /// + /// Indicating whether to clip the histogram bins at a specific value. + /// Histogram clip limit in percent of the total pixels. Histogram bins which exceed this limit, will be capped at this value. + public GlobalHistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage) + : base(luminanceLevels, clipHistogram, clipLimitPercentage) + { + } + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + MemoryAllocator memoryAllocator = configuration.MemoryAllocator; + int numberOfPixels = source.Width * source.Height; + Span pixels = source.GetPixelSpan(); + var workingRect = new Rectangle(0, 0, source.Width, source.Height); + + using (IMemoryOwner histogramBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) + using (IMemoryOwner cdfBuffer = memoryAllocator.Allocate(this.LuminanceLevels, AllocationOptions.Clean)) + { + // Build the histogram of the grayscale levels. + ParallelHelper.IterateRows( + workingRect, + configuration, + rows => + { + ref int histogramBase = ref MemoryMarshal.GetReference(histogramBuffer.GetSpan()); + for (int y = rows.Min; y < rows.Max; y++) + { + ref TPixel pixelBase = ref MemoryMarshal.GetReference(source.GetPixelRowSpan(y)); + + for (int x = 0; x < workingRect.Width; x++) + { + int luminance = GetLuminance(Unsafe.Add(ref pixelBase, x), this.LuminanceLevels); + Unsafe.Add(ref histogramBase, luminance)++; + } + } + }); + + Span histogram = histogramBuffer.GetSpan(); + if (this.ClipHistogramEnabled) + { + this.ClipHistogram(histogram, this.ClipLimitPercentage, numberOfPixels); + } + + // Calculate the cumulative distribution function, which will map each input pixel to a new value. + int cdfMin = this.CalculateCdf( + ref MemoryMarshal.GetReference(cdfBuffer.GetSpan()), + ref MemoryMarshal.GetReference(histogram), + histogram.Length - 1); + + float numberOfPixelsMinusCdfMin = numberOfPixels - cdfMin; + + // Apply the cdf to each pixel of the image + ParallelHelper.IterateRows( + workingRect, + configuration, + rows => + { + ref int cdfBase = ref MemoryMarshal.GetReference(cdfBuffer.GetSpan()); + for (int y = rows.Min; y < rows.Max; y++) + { + ref TPixel pixelBase = ref MemoryMarshal.GetReference(source.GetPixelRowSpan(y)); + + for (int x = 0; x < workingRect.Width; x++) + { + ref TPixel pixel = ref Unsafe.Add(ref pixelBase, x); + int luminance = GetLuminance(pixel, this.LuminanceLevels); + float luminanceEqualized = Unsafe.Add(ref cdfBase, luminance) / numberOfPixelsMinusCdfMin; + pixel.FromVector4(new Vector4(luminanceEqualized, luminanceEqualized, luminanceEqualized, pixel.ToVector4().W)); + } + } + }); + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs index 0d1a378361..1d9d5c986a 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationOptions.cs @@ -38,6 +38,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// /// Gets or sets the number of tiles the image is split into (horizontal and vertically) for the adaptive histogram equalization. Defaults to 10. /// - public int Tiles { get; set; } = 10; + public int NumberOfTiles { get; set; } = 10; } } diff --git a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs index fd1b6b9784..4aad1f564e 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs @@ -1,10 +1,6 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using System.Numerics; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing.Processors.Normalization @@ -12,14 +8,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// /// Defines a processor that normalizes the histogram of an image. /// - /// The pixel format. - internal abstract class HistogramEqualizationProcessor : ImageProcessor - where TPixel : struct, IPixel + internal abstract class HistogramEqualizationProcessor : IImageProcessor { - private readonly float luminanceLevelsFloat; - /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// or 65536 for 16-bit grayscale images. @@ -27,12 +19,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. protected HistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage) { - Guard.MustBeGreaterThan(luminanceLevels, 0, nameof(luminanceLevels)); - Guard.MustBeGreaterThan(clipLimitPercentage, 0F, nameof(clipLimitPercentage)); - this.LuminanceLevels = luminanceLevels; - this.luminanceLevelsFloat = luminanceLevels; - this.ClipHistogramEnabled = clipHistogram; + this.ClipHistogram = clipHistogram; this.ClipLimitPercentage = clipLimitPercentage; } @@ -44,95 +32,61 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// /// Gets a value indicating whether to clip the histogram bins at a specific value. /// - public bool ClipHistogramEnabled { get; } + public bool ClipHistogram { get; } /// /// Gets the histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. /// public float ClipLimitPercentage { get; } - /// - /// Calculates the cumulative distribution function. - /// - /// The reference to the array holding the cdf. - /// The reference to the histogram of the input image. - /// Index of the maximum of the histogram. - /// The first none zero value of the cdf. - public int CalculateCdf(ref int cdfBase, ref int histogramBase, int maxIdx) - { - int histSum = 0; - int cdfMin = 0; - bool cdfMinFound = false; - - for (int i = 0; i <= maxIdx; i++) - { - histSum += Unsafe.Add(ref histogramBase, i); - if (!cdfMinFound && histSum != 0) - { - cdfMin = histSum; - cdfMinFound = true; - } - - // Creating the lookup table: subtracting cdf min, so we do not need to do that inside the for loop. - Unsafe.Add(ref cdfBase, i) = Math.Max(0, histSum - cdfMin); - } - - return cdfMin; - } + /// + public abstract IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel; /// - /// AHE tends to over amplify the contrast in near-constant regions of the image, since the histogram in such regions is highly concentrated. - /// Clipping the histogram is meant to reduce this effect, by cutting of histogram bin's which exceed a certain amount and redistribute - /// the values over the clip limit to all other bins equally. + /// Creates the that implements the algorithm + /// defined by the given . /// - /// The histogram to apply the clipping. - /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. - /// The numbers of pixels inside the tile. - public void ClipHistogram(Span histogram, float clipLimitPercentage, int pixelCount) + /// The . + /// The . + public static HistogramEqualizationProcessor FromOptions(HistogramEqualizationOptions options) { - int clipLimit = (int)MathF.Round(pixelCount * clipLimitPercentage); - int sumOverClip = 0; - ref int histogramBase = ref MemoryMarshal.GetReference(histogram); + HistogramEqualizationProcessor processor; - for (int i = 0; i < histogram.Length; i++) + switch (options.Method) { - ref int histogramLevel = ref Unsafe.Add(ref histogramBase, i); - if (histogramLevel > clipLimit) - { - sumOverClip += histogramLevel - clipLimit; - histogramLevel = clipLimit; - } + case HistogramEqualizationMethod.Global: + processor = new GlobalHistogramEqualizationProcessor( + options.LuminanceLevels, + options.ClipHistogram, + options.ClipLimitPercentage); + break; + + case HistogramEqualizationMethod.AdaptiveTileInterpolation: + processor = new AdaptiveHistEqualizationProcessor( + options.LuminanceLevels, + options.ClipHistogram, + options.ClipLimitPercentage, + options.NumberOfTiles); + break; + + case HistogramEqualizationMethod.AdaptiveSlidingWindow: + processor = new AdaptiveHistEqualizationSWProcessor( + options.LuminanceLevels, + options.ClipHistogram, + options.ClipLimitPercentage, + options.NumberOfTiles); + break; + + default: + processor = new GlobalHistogramEqualizationProcessor( + options.LuminanceLevels, + options.ClipHistogram, + options.ClipLimitPercentage); + break; } - int addToEachBin = sumOverClip > 0 ? (int)MathF.Floor(sumOverClip / this.luminanceLevelsFloat) : 0; - if (addToEachBin > 0) - { - for (int i = 0; i < histogram.Length; i++) - { - Unsafe.Add(ref histogramBase, i) += addToEachBin; - } - } + return processor; } - - /// - /// Convert the pixel values to grayscale using ITU-R Recommendation BT.709. - /// - /// The pixel to get the luminance from - /// The number of luminance levels (256 for 8 bit, 65536 for 16 bit grayscale images) - [MethodImpl(InliningOptions.ShortMethod)] - public static int GetLuminance(TPixel sourcePixel, int luminanceLevels) - { - var vector = sourcePixel.ToVector4(); - return GetLuminance(ref vector, luminanceLevels); - } - - /// - /// Convert the pixel values to grayscale using ITU-R Recommendation BT.709. - /// - /// The vector to get the luminance from - /// The number of luminance levels (256 for 8 bit, 65536 for 16 bit grayscale images) - [MethodImpl(InliningOptions.ShortMethod)] - public static int GetLuminance(ref Vector4 vector, int luminanceLevels) - => (int)MathF.Round(((.2126F * vector.X) + (.7152F * vector.Y) + (.0722F * vector.Y)) * (luminanceLevels - 1)); } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs new file mode 100644 index 0000000000..8dbc903f29 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor{TPixel}.cs @@ -0,0 +1,139 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors.Normalization +{ + /// + /// Defines a processor that normalizes the histogram of an image. + /// + /// The pixel format. + internal abstract class HistogramEqualizationProcessor : ImageProcessor + where TPixel : struct, IPixel + { + private readonly float luminanceLevelsFloat; + + /// + /// Initializes a new instance of the class. + /// + /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images + /// or 65536 for 16-bit grayscale images. + /// Indicates, if histogram bins should be clipped. + /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. + protected HistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage) + { + Guard.MustBeGreaterThan(luminanceLevels, 0, nameof(luminanceLevels)); + Guard.MustBeGreaterThan(clipLimitPercentage, 0F, nameof(clipLimitPercentage)); + + this.LuminanceLevels = luminanceLevels; + this.luminanceLevelsFloat = luminanceLevels; + this.ClipHistogramEnabled = clipHistogram; + this.ClipLimitPercentage = clipLimitPercentage; + } + + /// + /// Gets the number of luminance levels. + /// + public int LuminanceLevels { get; } + + /// + /// Gets a value indicating whether to clip the histogram bins at a specific value. + /// + public bool ClipHistogramEnabled { get; } + + /// + /// Gets the histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. + /// + public float ClipLimitPercentage { get; } + + /// + /// Calculates the cumulative distribution function. + /// + /// The reference to the array holding the cdf. + /// The reference to the histogram of the input image. + /// Index of the maximum of the histogram. + /// The first none zero value of the cdf. + public int CalculateCdf(ref int cdfBase, ref int histogramBase, int maxIdx) + { + int histSum = 0; + int cdfMin = 0; + bool cdfMinFound = false; + + for (int i = 0; i <= maxIdx; i++) + { + histSum += Unsafe.Add(ref histogramBase, i); + if (!cdfMinFound && histSum != 0) + { + cdfMin = histSum; + cdfMinFound = true; + } + + // Creating the lookup table: subtracting cdf min, so we do not need to do that inside the for loop. + Unsafe.Add(ref cdfBase, i) = Math.Max(0, histSum - cdfMin); + } + + return cdfMin; + } + + /// + /// AHE tends to over amplify the contrast in near-constant regions of the image, since the histogram in such regions is highly concentrated. + /// Clipping the histogram is meant to reduce this effect, by cutting of histogram bin's which exceed a certain amount and redistribute + /// the values over the clip limit to all other bins equally. + /// + /// The histogram to apply the clipping. + /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. + /// The numbers of pixels inside the tile. + public void ClipHistogram(Span histogram, float clipLimitPercentage, int pixelCount) + { + int clipLimit = (int)MathF.Round(pixelCount * clipLimitPercentage); + int sumOverClip = 0; + ref int histogramBase = ref MemoryMarshal.GetReference(histogram); + + for (int i = 0; i < histogram.Length; i++) + { + ref int histogramLevel = ref Unsafe.Add(ref histogramBase, i); + if (histogramLevel > clipLimit) + { + sumOverClip += histogramLevel - clipLimit; + histogramLevel = clipLimit; + } + } + + int addToEachBin = sumOverClip > 0 ? (int)MathF.Floor(sumOverClip / this.luminanceLevelsFloat) : 0; + if (addToEachBin > 0) + { + for (int i = 0; i < histogram.Length; i++) + { + Unsafe.Add(ref histogramBase, i) += addToEachBin; + } + } + } + + /// + /// Convert the pixel values to grayscale using ITU-R Recommendation BT.709. + /// + /// The pixel to get the luminance from + /// The number of luminance levels (256 for 8 bit, 65536 for 16 bit grayscale images) + [MethodImpl(InliningOptions.ShortMethod)] + public static int GetLuminance(TPixel sourcePixel, int luminanceLevels) + { + var vector = sourcePixel.ToVector4(); + return GetLuminance(ref vector, luminanceLevels); + } + + /// + /// Convert the pixel values to grayscale using ITU-R Recommendation BT.709. + /// + /// The vector to get the luminance from + /// The number of luminance levels (256 for 8 bit, 65536 for 16 bit grayscale images) + [MethodImpl(InliningOptions.ShortMethod)] + public static int GetLuminance(ref Vector4 vector, int luminanceLevels) + => (int)MathF.Round(((.2126F * vector.X) + (.7152F * vector.Y) + (.0722F * vector.Y)) * (luminanceLevels - 1)); + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs index 84d592bd96..fc9a583dd1 100644 --- a/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs +++ b/tests/ImageSharp.Tests/Processing/Normalization/HistogramEqualizationTests.cs @@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization Method = HistogramEqualizationMethod.AdaptiveSlidingWindow, LuminanceLevels = 256, ClipHistogram = true, - Tiles = 15 + NumberOfTiles = 15 }; image.Mutate(x => x.HistogramEqualization(options)); image.DebugSave(provider); @@ -106,7 +106,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Normalization Method = HistogramEqualizationMethod.AdaptiveTileInterpolation, LuminanceLevels = 256, ClipHistogram = true, - Tiles = 10 + NumberOfTiles = 10 }; image.Mutate(x => x.HistogramEqualization(options)); image.DebugSave(provider); diff --git a/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs index cb901c2a88..91422faaa1 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs @@ -50,8 +50,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization /// The output is visually correct old 32bit runtime, /// but it is very different because of floating point inaccuracies. /// - private static readonly bool SkipAllDitherTests = - !TestEnvironment.Is64BitProcess && string.IsNullOrEmpty(TestEnvironment.NetCoreVersion); + private static readonly bool SkipAllDitherTests = false; [Theory] [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32)] From fa1e0baced645949989929db33a59683855c509e Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 May 2019 17:43:06 +0200 Subject: [PATCH 23/31] cache Image dimensions into a field + re-enable all target frameworks --- .../ImageSharp.Drawing.csproj | 4 +- src/ImageSharp/Image.cs | 39 +++++++++++++--- src/ImageSharp/ImageSharp.csproj | 3 +- src/ImageSharp/Image{TPixel}.cs | 45 +++++++++++++------ 4 files changed, 68 insertions(+), 23 deletions(-) diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj index 1734c48de2..ac5d7fdea6 100644 --- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj +++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj @@ -10,8 +10,8 @@ $(packageversion) 0.0.1 - - netcoreapp2.1 + netcoreapp2.1;netstandard1.3;netstandard2.0 + netstandard2.0 7.3 true diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs index d0a1c4319d..1566fd0eed 100644 --- a/src/ImageSharp/Image.cs +++ b/src/ImageSharp/Image.cs @@ -7,6 +7,7 @@ using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; namespace SixLabors.ImageSharp { @@ -17,19 +18,36 @@ namespace SixLabors.ImageSharp /// public abstract partial class Image : IImage, IConfigurable { + private Size size; + /// /// Initializes a new instance of the class. /// /// The . /// The . /// The . - protected Image(Configuration configuration, PixelTypeInfo pixelType, ImageMetadata metadata) + /// The . + protected Image(Configuration configuration, PixelTypeInfo pixelType, ImageMetadata metadata, Size size) { this.Configuration = configuration ?? Configuration.Default; this.PixelType = pixelType; + this.size = size; this.Metadata = metadata ?? new ImageMetadata(); } + /// + /// Initializes a new instance of the class. + /// + internal Image( + Configuration configuration, + PixelTypeInfo pixelType, + ImageMetadata metadata, + int width, + int height) + : this(configuration, pixelType, metadata, new Size(width, height)) + { + } + /// /// Gets the . /// @@ -39,10 +57,10 @@ namespace SixLabors.ImageSharp public PixelTypeInfo PixelType { get; } /// - public abstract int Width { get; } + public int Width => this.size.Width; /// - public abstract int Height { get; } + public int Height => this.size.Height; /// public ImageMetadata Metadata { get; } @@ -55,8 +73,6 @@ namespace SixLabors.ImageSharp /// public abstract void Dispose(); - internal abstract void AcceptVisitor(IImageVisitor visitor); - /// /// Saves the image to the given stream using the given image encoder. /// @@ -72,6 +88,19 @@ namespace SixLabors.ImageSharp this.AcceptVisitor(visitor); } + /// + /// Accept a . + /// Implemented by invoking + /// with the pixel type of the image. + /// + internal abstract void AcceptVisitor(IImageVisitor visitor); + + /// + /// Update the size of the image after mutation. + /// + /// The . + protected void UpdateSize(Size size) => this.size = size; + private class EncodeVisitor : IImageVisitor { private readonly IImageEncoder encoder; diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index f780ca666a..e763b8ef1c 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -11,8 +11,7 @@ $(packageversion) 0.0.1 - - netcoreapp2.1 + netcoreapp2.1;netstandard1.3;netstandard2.0;net472 true true diff --git a/src/ImageSharp/Image{TPixel}.cs b/src/ImageSharp/Image{TPixel}.cs index 243e2912be..27e2bc593c 100644 --- a/src/ImageSharp/Image{TPixel}.cs +++ b/src/ImageSharp/Image{TPixel}.cs @@ -3,14 +3,13 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; -using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; namespace SixLabors.ImageSharp { @@ -67,14 +66,14 @@ namespace SixLabors.ImageSharp /// The height of the image in pixels. /// The images metadata. internal Image(Configuration configuration, int width, int height, ImageMetadata metadata) - : base(configuration, PixelTypeInfo.Create(), metadata) + : base(configuration, PixelTypeInfo.Create(), metadata, width, height) { this.Frames = new ImageFrameCollection(this, width, height, default(TPixel)); } /// /// Initializes a new instance of the class - /// wrapping an external + /// wrapping an external . /// /// The configuration providing initialization code which allows extending the library. /// The memory source. @@ -82,7 +81,7 @@ namespace SixLabors.ImageSharp /// The height of the image in pixels. /// The images metadata. internal Image(Configuration configuration, MemorySource memorySource, int width, int height, ImageMetadata metadata) - : base(configuration, PixelTypeInfo.Create(), metadata) + : base(configuration, PixelTypeInfo.Create(), metadata, width, height) { this.Frames = new ImageFrameCollection(this, width, height, memorySource); } @@ -97,7 +96,7 @@ namespace SixLabors.ImageSharp /// The color to initialize the pixels with. /// The images metadata. internal Image(Configuration configuration, int width, int height, TPixel backgroundColor, ImageMetadata metadata) - : base(configuration, PixelTypeInfo.Create(), metadata) + : base(configuration, PixelTypeInfo.Create(), metadata, width, height) { this.Frames = new ImageFrameCollection(this, width, height, backgroundColor); } @@ -110,17 +109,11 @@ namespace SixLabors.ImageSharp /// The images metadata. /// The frames that will be owned by this image instance. internal Image(Configuration configuration, ImageMetadata metadata, IEnumerable> frames) - : base(configuration, PixelTypeInfo.Create(), metadata) + : base(configuration, PixelTypeInfo.Create(), metadata, ValidateFramesAndGetSize(frames)) { this.Frames = new ImageFrameCollection(this, frames); } - /// - public override int Width => this.Frames.RootFrame.Width; - - /// - public override int Height => this.Frames.RootFrame.Height; - /// /// Gets the frames. /// @@ -173,7 +166,7 @@ namespace SixLabors.ImageSharp /// /// The pixel format. /// The configuration providing initialization code which allows extending the library. - /// The + /// The . public Image CloneAs(Configuration configuration) where TPixel2 : struct, IPixel { @@ -184,6 +177,7 @@ namespace SixLabors.ImageSharp /// public override void Dispose() => this.Frames.Dispose(); + /// internal override void AcceptVisitor(IImageVisitor visitor) { visitor.Visit(this); @@ -204,6 +198,29 @@ namespace SixLabors.ImageSharp { this.Frames[i].SwapOrCopyPixelsBufferFrom(pixelSource.Frames[i]); } + + this.UpdateSize(pixelSource.Size()); + } + + private static Size ValidateFramesAndGetSize(IEnumerable> frames) + { + Guard.NotNull(frames, nameof(frames)); + + ImageFrame rootFrame = frames.FirstOrDefault(); + + if (rootFrame == null) + { + throw new ArgumentException("Must not be empty.", nameof(frames)); + } + + Size rootSize = rootFrame.Size(); + + if (frames.Any(f => f.Size() != rootSize)) + { + throw new ArgumentException("The provided frames must be of the same size.", nameof(frames)); + } + + return rootSize; } } } \ No newline at end of file From 5215a13cbba354ed0fe1e7fdf5e92e81a1d9912a Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 May 2019 19:33:34 +0200 Subject: [PATCH 24/31] fix Image.FromStream() + add tests --- src/ImageSharp/Formats/Bmp/ImageExtensions.cs | 19 +- src/ImageSharp/Formats/Gif/ImageExtensions.cs | 19 +- .../Formats/Jpeg/ImageExtensions.cs | 19 +- src/ImageSharp/Formats/Png/ImageExtensions.cs | 18 +- src/ImageSharp/Image.FromBytes.cs | 207 +++++++++++++----- src/ImageSharp/Image.FromFile.cs | 16 +- src/ImageSharp/Image.FromStream.cs | 50 +++-- .../Drawing/RecolorImageTest.cs | 4 +- .../Drawing/SolidPolygonTests.cs | 2 +- .../Formats/Bmp/BmpEncoderTests.cs | 4 +- .../Formats/GeneralFormatTests.cs | 6 +- .../Formats/Gif/GifDecoderTests.cs | 6 +- .../Formats/Gif/GifEncoderTests.cs | 6 +- .../Formats/Jpg/JpegDecoderTests.MetaData.cs | 2 +- .../Formats/Jpg/JpegEncoderTests.cs | 8 +- .../Formats/Png/PngDecoderTests.cs | 6 +- .../Formats/Png/PngEncoderTests.cs | 6 +- .../Image/ImageTests.ImageLoadTestBase.cs | 25 ++- .../Image/ImageTests.Load_FileSystemPath.cs | 2 +- .../Image/ImageTests.Load_FromBytes.cs | 120 ---------- ...s.Load_FromBytes_PassLocalConfiguration.cs | 119 ++++++++++ ...s.Load_FromBytes_UseGlobalConfiguration.cs | 101 +++++++++ .../Image/ImageTests.Load_FromStream.cs | 102 --------- ....Load_FromStream_PassLocalConfiguration.cs | 93 ++++++++ ...Load_FromStream_UseDefaultConfiguration.cs | 95 ++++++++ .../Profiles/Exif/ExifProfileTests.cs | 12 +- .../MetaData/Profiles/Exif/ExifValueTests.cs | 2 +- .../PorterDuffCompositorTests.cs | 2 +- tests/ImageSharp.Tests/TestFile.cs | 6 +- tests/ImageSharp.Tests/TestFileSystem.cs | 31 +-- tests/ImageSharp.Tests/TestFormat.cs | 63 +++++- .../Tests/ReferenceDecoderBenchmarks.cs | 2 +- 32 files changed, 761 insertions(+), 412 deletions(-) delete mode 100644 tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes.cs create mode 100644 tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_PassLocalConfiguration.cs create mode 100644 tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs delete mode 100644 tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream.cs create mode 100644 tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_PassLocalConfiguration.cs create mode 100644 tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_UseDefaultConfiguration.cs diff --git a/src/ImageSharp/Formats/Bmp/ImageExtensions.cs b/src/ImageSharp/Formats/Bmp/ImageExtensions.cs index aa1c353db2..0c37907c22 100644 --- a/src/ImageSharp/Formats/Bmp/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Bmp/ImageExtensions.cs @@ -2,38 +2,35 @@ // Licensed under the Apache License, Version 2.0. using System.IO; + using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats.Bmp; -using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp { /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Saves the image to the given stream with the bmp format. /// - /// The pixel format. /// The image this method extends. /// The stream to save the image to. /// Thrown if the stream is null. - public static void SaveAsBmp(this Image source, Stream stream) - where TPixel : struct, IPixel - => source.SaveAsBmp(stream, null); + public static void SaveAsBmp(this Image source, Stream stream) => source.SaveAsBmp(stream, null); /// /// Saves the image to the given stream with the bmp format. /// - /// The pixel format. /// The image this method extends. /// The stream to save the image to. /// The encoder to save the image with. /// Thrown if the stream is null. - public static void SaveAsBmp(this Image source, Stream stream, BmpEncoder encoder) - where TPixel : struct, IPixel - => source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(BmpFormat.Instance)); + public static void SaveAsBmp(this Image source, Stream stream, BmpEncoder encoder) => + source.Save( + stream, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(BmpFormat.Instance)); } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Gif/ImageExtensions.cs b/src/ImageSharp/Formats/Gif/ImageExtensions.cs index 8ddd4247e1..c7ac001ff5 100644 --- a/src/ImageSharp/Formats/Gif/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Gif/ImageExtensions.cs @@ -2,38 +2,35 @@ // Licensed under the Apache License, Version 2.0. using System.IO; + using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats.Gif; -using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp { /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Saves the image to the given stream in the gif format. /// - /// The pixel format. /// The image this method extends. /// The stream to save the image to. /// Thrown if the stream is null. - public static void SaveAsGif(this Image source, Stream stream) - where TPixel : struct, IPixel - => source.SaveAsGif(stream, null); + public static void SaveAsGif(this Image source, Stream stream) => source.SaveAsGif(stream, null); /// /// Saves the image to the given stream in the gif format. /// - /// The pixel format. /// The image this method extends. /// The stream to save the image to. /// The options for the encoder. /// Thrown if the stream is null. - public static void SaveAsGif(this Image source, Stream stream, GifEncoder encoder) - where TPixel : struct, IPixel - => source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(GifFormat.Instance)); + public static void SaveAsGif(this Image source, Stream stream, GifEncoder encoder) => + source.Save( + stream, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(GifFormat.Instance)); } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Jpeg/ImageExtensions.cs b/src/ImageSharp/Formats/Jpeg/ImageExtensions.cs index cb7fc19446..7fec050b4a 100644 --- a/src/ImageSharp/Formats/Jpeg/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Jpeg/ImageExtensions.cs @@ -2,38 +2,35 @@ // Licensed under the Apache License, Version 2.0. using System.IO; + using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.Formats.Jpeg; -using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp { /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Saves the image to the given stream with the jpeg format. /// - /// The pixel format. /// The image this method extends. /// The stream to save the image to. /// Thrown if the stream is null. - public static void SaveAsJpeg(this Image source, Stream stream) - where TPixel : struct, IPixel - => SaveAsJpeg(source, stream, null); + public static void SaveAsJpeg(this Image source, Stream stream) => SaveAsJpeg(source, stream, null); /// /// Saves the image to the given stream with the jpeg format. /// - /// The pixel format. /// The image this method extends. /// The stream to save the image to. /// The options for the encoder. /// Thrown if the stream is null. - public static void SaveAsJpeg(this Image source, Stream stream, JpegEncoder encoder) - where TPixel : struct, IPixel - => source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(JpegFormat.Instance)); + public static void SaveAsJpeg(this Image source, Stream stream, JpegEncoder encoder) => + source.Save( + stream, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(JpegFormat.Instance)); } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Png/ImageExtensions.cs b/src/ImageSharp/Formats/Png/ImageExtensions.cs index c73ec6f57e..af56830f42 100644 --- a/src/ImageSharp/Formats/Png/ImageExtensions.cs +++ b/src/ImageSharp/Formats/Png/ImageExtensions.cs @@ -2,39 +2,35 @@ // Licensed under the Apache License, Version 2.0. using System.IO; + using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Png; -using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp { /// - /// Extension methods for the type. + /// Extension methods for the type. /// public static partial class ImageExtensions { /// /// Saves the image to the given stream with the png format. /// - /// The pixel format. /// The image this method extends. /// The stream to save the image to. /// Thrown if the stream is null. - public static void SaveAsPng(this Image source, Stream stream) - where TPixel : struct, IPixel - => SaveAsPng(source, stream, null); + public static void SaveAsPng(this Image source, Stream stream) => SaveAsPng(source, stream, null); /// /// Saves the image to the given stream with the png format. /// - /// The pixel format. /// The image this method extends. /// The stream to save the image to. /// The options for the encoder. /// Thrown if the stream is null. - public static void SaveAsPng(this Image source, Stream stream, PngEncoder encoder) - where TPixel : struct, IPixel - => source.Save(stream, encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PngFormat.Instance)); + public static void SaveAsPng(this Image source, Stream stream, PngEncoder encoder) => + source.Save( + stream, + encoder ?? source.GetConfiguration().ImageFormatsManager.FindEncoder(PngFormat.Instance)); } } \ No newline at end of file diff --git a/src/ImageSharp/Image.FromBytes.cs b/src/ImageSharp/Image.FromBytes.cs index 24a169f6e9..25dc5a1e0e 100644 --- a/src/ImageSharp/Image.FromBytes.cs +++ b/src/ImageSharp/Image.FromBytes.cs @@ -44,48 +44,6 @@ namespace SixLabors.ImageSharp /// A new . public static Image Load(byte[] data) => Load(Configuration.Default, data); - /// - /// Load a new instance of from the given encoded byte array. - /// - /// The byte array containing encoded image data. - /// The mime type of the decoded image. - /// A new . - public static Image Load(byte[] data, out IImageFormat format) => Load(Configuration.Default, data, out format); - - /// - /// Load a new instance of from the given encoded byte array. - /// - /// The config for the decoder. - /// The byte array containing encoded image data. - /// A new . - public static Image Load(Configuration config, byte[] data) => Load(config, data); - - /// - /// Load a new instance of from the given encoded byte array. - /// - /// The config for the decoder. - /// The byte array containing image data. - /// The mime type of the decoded image. - /// A new . - public static Image Load(Configuration config, byte[] data, out IImageFormat format) => Load(config, data, out format); - - /// - /// Load a new instance of from the given encoded byte array. - /// - /// The byte array containing encoded image data. - /// The decoder. - /// A new . - public static Image Load(byte[] data, IImageDecoder decoder) => Load(data, decoder); - - /// - /// Load a new instance of from the given encoded byte array. - /// - /// The config for the decoder. - /// The byte array containing image data. - /// The decoder. - /// A new . - public static Image Load(Configuration config, byte[] data, IImageDecoder decoder) => Load(config, data, decoder); - /// /// Load a new instance of from the given encoded byte array. /// @@ -117,9 +75,9 @@ namespace SixLabors.ImageSharp public static Image Load(Configuration config, byte[] data) where TPixel : struct, IPixel { - using (var steram = new MemoryStream(data)) + using (var stream = new MemoryStream(data)) { - return Load(config, steram); + return Load(config, stream); } } @@ -211,29 +169,36 @@ namespace SixLabors.ImageSharp } /// - /// Load a new instance of from the given encoded byte span. + /// Load a new instance of from the given encoded byte span. /// - /// The byte span containing image data. - /// A new . - public static Image Load(ReadOnlySpan data) => Load(Configuration.Default, data); + /// The byte span containing encoded image data. + /// The pixel format. + /// A new . + public static Image Load(ReadOnlySpan data) + where TPixel : struct, IPixel + => Load(Configuration.Default, data); /// - /// Load a new instance of from the given encoded byte span. + /// Load a new instance of from the given encoded byte array. /// - /// The config for the decoder. - /// The byte span containing encoded image data. - /// A new . - public static Image Load(Configuration config, ReadOnlySpan data) => Load(config, data); + /// The byte span containing image data. + /// The mime type of the decoded image. + /// The pixel format. + /// A new . + public static Image Load(ReadOnlySpan data, out IImageFormat format) + where TPixel : struct, IPixel + => Load(Configuration.Default, data, out format); /// - /// Load a new instance of from the given encoded byte span. + /// Load a new instance of from the given encoded byte array. /// /// The byte span containing encoded image data. + /// The decoder. /// The pixel format. /// A new . - public static Image Load(ReadOnlySpan data) + public static Image Load(ReadOnlySpan data, IImageDecoder decoder) where TPixel : struct, IPixel - => Load(Configuration.Default, data); + => Load(Configuration.Default, data, decoder); /// /// Load a new instance of from the given encoded byte span. @@ -299,5 +264,135 @@ namespace SixLabors.ImageSharp } } } + + /// + /// Load a new instance of from the given encoded byte array. + /// + /// The byte array containing image data. + /// The detected format. + /// A new . + public static Image Load(byte[] data, out IImageFormat format) => + Load(Configuration.Default, data, out format); + + /// + /// Load a new instance of from the given encoded byte array. + /// + /// The byte array containing encoded image data. + /// The decoder. + /// A new . + public static Image Load(byte[] data, IImageDecoder decoder) => Load(Configuration.Default, data, decoder); + + /// + /// Load a new instance of from the given encoded byte array. + /// + /// The config for the decoder. + /// The byte array containing encoded image data. + /// A new . + public static Image Load(Configuration config, byte[] data) => Load(config, data, out _); + + /// + /// Load a new instance of from the given encoded byte array. + /// + /// The config for the decoder. + /// The byte array containing image data. + /// The decoder. + /// A new . + public static Image Load(Configuration config, byte[] data, IImageDecoder decoder) + { + using (var stream = new MemoryStream(data)) + { + return Load(config, stream, decoder); + } + } + + /// + /// Load a new instance of from the given encoded byte array. + /// + /// The config for the decoder. + /// The byte array containing image data. + /// The mime type of the decoded image. + /// A new . + public static Image Load(Configuration config, byte[] data, out IImageFormat format) + { + using (var stream = new MemoryStream(data)) + { + return Load(config, stream, out format); + } + } + + /// + /// Load a new instance of from the given encoded byte span. + /// + /// The byte span containing image data. + /// A new . + public static Image Load(ReadOnlySpan data) => Load(Configuration.Default, data); + + /// + /// Load a new instance of from the given encoded byte span. + /// + /// The byte span containing image data. + /// The decoder. + /// A new . + public static Image Load(ReadOnlySpan data, IImageDecoder decoder) => + Load(Configuration.Default, data, decoder); + + /// + /// Load a new instance of from the given encoded byte array. + /// + /// The byte span containing image data. + /// The detected format. + /// A new . + public static Image Load(ReadOnlySpan data, out IImageFormat format) => + Load(Configuration.Default, data, out format); + + /// + /// Decodes a new instance of from the given encoded byte span. + /// + /// The configuration options. + /// The byte span containing image data. + /// A new . + public static unsafe Image Load(Configuration config, ReadOnlySpan data) => Load(config, data, out _); + + /// + /// Load a new instance of from the given encoded byte span. + /// + /// The Configuration. + /// The byte span containing image data. + /// The decoder. + /// A new . + public static unsafe Image Load( + Configuration config, + ReadOnlySpan data, + IImageDecoder decoder) + { + fixed (byte* ptr = &data.GetPinnableReference()) + { + using (var stream = new UnmanagedMemoryStream(ptr, data.Length)) + { + return Load(config, stream, decoder); + } + } + } + + /// + /// Load a new instance of from the given encoded byte span. + /// + /// The configuration options. + /// The byte span containing image data. + /// The of the decoded image.> + /// A new . + public static unsafe Image Load( + Configuration config, + ReadOnlySpan data, + out IImageFormat format) + { + fixed (byte* ptr = &data.GetPinnableReference()) + { + using (var stream = new UnmanagedMemoryStream(ptr, data.Length)) + { + return Load(config, stream, out format); + } + } + } } } \ No newline at end of file diff --git a/src/ImageSharp/Image.FromFile.cs b/src/ImageSharp/Image.FromFile.cs index 61500e20d7..08ed381c5a 100644 --- a/src/ImageSharp/Image.FromFile.cs +++ b/src/ImageSharp/Image.FromFile.cs @@ -46,7 +46,7 @@ namespace SixLabors.ImageSharp /// Thrown if the stream is not readable nor seekable. /// /// A new . - public static Image Load(string path) => Load(path); + public static Image Load(string path) => Load(Configuration.Default, path); /// /// Create a new instance of the class from the given file. @@ -57,7 +57,7 @@ namespace SixLabors.ImageSharp /// Thrown if the stream is not readable nor seekable. /// /// A new . - public static Image Load(string path, out IImageFormat format) => Load(path, out format); + public static Image Load(string path, out IImageFormat format) => Load(Configuration.Default, path, out format); /// /// Create a new instance of the class from the given file. @@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp /// Thrown if the stream is not readable nor seekable. /// /// A new . - public static Image Load(Configuration config, string path) => Load(config, path); + public static Image Load(Configuration config, string path) => Load(config, path, out _); /// /// Create a new instance of the class from the given file. @@ -80,7 +80,13 @@ namespace SixLabors.ImageSharp /// Thrown if the stream is not readable nor seekable. /// /// A new . - public static Image Load(Configuration config, string path, IImageDecoder decoder) => Load(config, path, decoder); + public static Image Load(Configuration config, string path, IImageDecoder decoder) + { + using (Stream stream = config.FileSystem.OpenRead(path)) + { + return Load(config, stream, decoder); + } + } /// /// Create a new instance of the class from the given file. @@ -91,7 +97,7 @@ namespace SixLabors.ImageSharp /// Thrown if the stream is not readable nor seekable. /// /// A new . - public static Image Load(string path, IImageDecoder decoder) => Load(path, decoder); + public static Image Load(string path, IImageDecoder decoder) => Load(Configuration.Default, path, decoder); /// /// Create a new instance of the class from the given file. diff --git a/src/ImageSharp/Image.FromStream.cs b/src/ImageSharp/Image.FromStream.cs index a4a9eeec75..74b99dfb3f 100644 --- a/src/ImageSharp/Image.FromStream.cs +++ b/src/ImageSharp/Image.FromStream.cs @@ -56,39 +56,54 @@ namespace SixLabors.ImageSharp => WithSeekableStream(config, stream, s => InternalIdentity(s, config ?? Configuration.Default)); /// - /// Create a new instance of the class from the given stream. + /// Decode a new instance of the class from the given stream. + /// The pixel format is selected by the decoder. /// /// The stream containing image information. /// the mime type of the decoded image. /// Thrown if the stream is not readable. - /// A new .> - public static Image Load(Stream stream, out IImageFormat format) => Load(stream, out format); + /// A new .> + public static Image Load(Stream stream, out IImageFormat format) => Load(Configuration.Default, stream, out format); /// - /// Create a new instance of the class from the given stream. + /// Decode a new instance of the class from the given stream. + /// The pixel format is selected by the decoder. /// /// The stream containing image information. /// Thrown if the stream is not readable. - /// A new .> - public static Image Load(Stream stream) => Load(stream); + /// A new .> + public static Image Load(Stream stream) => Load(Configuration.Default, stream); /// - /// Create a new instance of the class from the given stream. + /// Decode a new instance of the class from the given stream. + /// The pixel format is selected by the decoder. /// /// The stream containing image information. /// The decoder. /// Thrown if the stream is not readable. - /// A new .> - public static Image Load(Stream stream, IImageDecoder decoder) => Load(stream, decoder); + /// A new .> + public static Image Load(Stream stream, IImageDecoder decoder) => Load(Configuration.Default, stream, decoder); /// - /// Create a new instance of the class from the given stream. + /// Decode a new instance of the class from the given stream. + /// The pixel format is selected by the decoder. + /// + /// The config for the decoder. + /// The stream containing image information. + /// The decoder. + /// Thrown if the stream is not readable. + /// A new .> + public static Image Load(Configuration config, Stream stream, IImageDecoder decoder) => + WithSeekableStream(config, stream, s => decoder.Decode(config, s)); + + /// + /// Decode a new instance of the class from the given stream. /// /// The config for the decoder. /// The stream containing image information. /// Thrown if the stream is not readable. - /// A new .> - public static Image Load(Configuration config, Stream stream) => Load(config, stream); + /// A new .> + public static Image Load(Configuration config, Stream stream) => Load(config, stream, out _); /// /// Create a new instance of the class from the given stream. @@ -183,7 +198,16 @@ namespace SixLabors.ImageSharp throw new NotSupportedException(sb.ToString()); } - private static Image Load(Configuration config, Stream stream, out IImageFormat format) + /// + /// Decode a new instance of the class from the given stream. + /// The pixel format is selected by the decoder. + /// + /// The configuration options. + /// The stream containing image information. + /// the mime type of the decoded image. + /// Thrown if the stream is not readable. + /// A new . + public static Image Load(Configuration config, Stream stream, out IImageFormat format) { config = config ?? Configuration.Default; (Image img, IImageFormat format) data = WithSeekableStream(config, stream, s => Decode(s, config)); diff --git a/tests/ImageSharp.Tests/Drawing/RecolorImageTest.cs b/tests/ImageSharp.Tests/Drawing/RecolorImageTest.cs index 2dcd8b3d34..b064d9c964 100644 --- a/tests/ImageSharp.Tests/Drawing/RecolorImageTest.cs +++ b/tests/ImageSharp.Tests/Drawing/RecolorImageTest.cs @@ -19,7 +19,7 @@ namespace SixLabors.ImageSharp.Tests foreach (TestFile file in Files) { - using (Image image = file.CreateImage()) + using (Image image = file.CreateRgba32Image()) { image.Mutate(x => x.Fill(brush)); image.Save($"{path}/{file.FileName}"); @@ -36,7 +36,7 @@ namespace SixLabors.ImageSharp.Tests foreach (TestFile file in Files) { - using (Image image = file.CreateImage()) + using (Image image = file.CreateRgba32Image()) { int imageHeight = image.Height; image.Mutate(x => x.Fill(brush, new Rectangle(0, imageHeight / 2 - imageHeight / 4, image.Width, imageHeight / 2))); diff --git a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs b/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs index d7fb0a3d37..5660518eb4 100644 --- a/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs +++ b/tests/ImageSharp.Tests/Drawing/SolidPolygonTests.cs @@ -96,7 +96,7 @@ namespace SixLabors.ImageSharp.Tests.Drawing new Vector2(50, 300) }; - using (Image brushImage = TestFile.Create(TestImages.Bmp.Car).CreateImage()) + using (Image brushImage = TestFile.Create(TestImages.Bmp.Car).CreateRgba32Image()) using (var image = new Image(500, 500)) { var brush = new ImageBrush(brushImage); diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index 7e054734e3..df3029a7fc 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -49,7 +49,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp var options = new BmpEncoder(); var testFile = TestFile.Create(imagePath); - using (Image input = testFile.CreateImage()) + using (Image input = testFile.CreateRgba32Image()) { using (var memStream = new MemoryStream()) { @@ -74,7 +74,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp var options = new BmpEncoder(); var testFile = TestFile.Create(imagePath); - using (Image input = testFile.CreateImage()) + using (Image input = testFile.CreateRgba32Image()) { using (var memStream = new MemoryStream()) { diff --git a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs index e8fdbd8926..b2f9788ae4 100644 --- a/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs +++ b/tests/ImageSharp.Tests/Formats/GeneralFormatTests.cs @@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Tests foreach (TestFile file in Files) { - using (Image image = file.CreateImage()) + using (Image image = file.CreateRgba32Image()) { string filename = path + "/" + file.FileNameWithoutExtension + ".txt"; File.WriteAllText(filename, image.ToBase64String(PngFormat.Instance)); @@ -55,7 +55,7 @@ namespace SixLabors.ImageSharp.Tests foreach (TestFile file in Files) { - using (Image image = file.CreateImage()) + using (Image image = file.CreateRgba32Image()) { image.Save($"{path}/{file.FileName}"); } @@ -102,7 +102,7 @@ namespace SixLabors.ImageSharp.Tests foreach (TestFile file in Files) { - using (Image image = file.CreateImage()) + using (Image image = file.CreateRgba32Image()) { using (FileStream output = File.OpenWrite($"{path}/{file.FileNameWithoutExtension}.bmp")) { diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index b13362aa3b..784f7ce703 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -165,7 +165,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif var testFile = TestFile.Create(TestImages.Gif.Rings); - using (Image image = testFile.CreateImage(options)) + using (Image image = testFile.CreateRgba32Image(options)) { Assert.Equal(1, image.Metadata.Properties.Count); Assert.Equal("Comments", image.Metadata.Properties[0].Name); @@ -183,7 +183,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif var testFile = TestFile.Create(TestImages.Gif.Rings); - using (Image image = testFile.CreateImage(options)) + using (Image image = testFile.CreateRgba32Image(options)) { Assert.Equal(0, image.Metadata.Properties.Count); } @@ -199,7 +199,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif var testFile = TestFile.Create(TestImages.Gif.Rings); - using (Image image = testFile.CreateImage(options)) + using (Image image = testFile.CreateRgba32Image(options)) { Assert.Equal(1, image.Metadata.Properties.Count); Assert.Equal("浉条卥慨灲", image.Metadata.Properties[0].Value); diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs index 5da1c3bb78..eab30944e9 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifEncoderTests.cs @@ -58,7 +58,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif var options = new GifEncoder(); var testFile = TestFile.Create(imagePath); - using (Image input = testFile.CreateImage()) + using (Image input = testFile.CreateRgba32Image()) { using (var memStream = new MemoryStream()) { @@ -83,7 +83,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif var testFile = TestFile.Create(TestImages.Gif.Rings); - using (Image input = testFile.CreateImage()) + using (Image input = testFile.CreateRgba32Image()) { using (var memStream = new MemoryStream()) { @@ -107,7 +107,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Gif var testFile = TestFile.Create(TestImages.Gif.Rings); - using (Image input = testFile.CreateImage()) + using (Image input = testFile.CreateRgba32Image()) { input.Metadata.Properties.Clear(); using (var memStream = new MemoryStream()) diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.MetaData.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.MetaData.cs index 48acc9ea47..308cf28b35 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.MetaData.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegDecoderTests.MetaData.cs @@ -216,7 +216,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg // Snake.jpg has both Exif and ICC profiles defined: var testFile = TestFile.Create(TestImages.Jpeg.Baseline.Snake); - using (Image image = testFile.CreateImage(decoder)) + using (Image image = testFile.CreateRgba32Image(decoder)) { if (ignoreMetaData) { diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs index 618947130a..d9013b507e 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegEncoderTests.cs @@ -48,7 +48,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var options = new JpegEncoder(); var testFile = TestFile.Create(imagePath); - using (Image input = testFile.CreateImage()) + using (Image input = testFile.CreateRgba32Image()) { using (var memStream = new MemoryStream()) { @@ -137,7 +137,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var testFile = TestFile.Create(TestImages.Jpeg.Baseline.Calliphora); - using (Image input = testFile.CreateImage()) + using (Image input = testFile.CreateRgba32Image()) using (var memStream0 = new MemoryStream()) using (var memStream1 = new MemoryStream()) { @@ -160,7 +160,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var testFile = TestFile.Create(TestImages.Jpeg.Baseline.Calliphora); - using (Image input = testFile.CreateImage()) + using (Image input = testFile.CreateRgba32Image()) using (var memStream0 = new MemoryStream()) using (var memStream1 = new MemoryStream()) { @@ -180,7 +180,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Jpg var options = new JpegEncoder(); var testFile = TestFile.Create(imagePath); - using (Image input = testFile.CreateImage()) + using (Image input = testFile.CreateRgba32Image()) { using (var memStream = new MemoryStream()) { diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index 0dbccd2509..70d191d74e 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -202,7 +202,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png var testFile = TestFile.Create(TestImages.Png.Blur); - using (Image image = testFile.CreateImage(options)) + using (Image image = testFile.CreateRgba32Image(options)) { Assert.Equal(1, image.Metadata.Properties.Count); Assert.Equal("Software", image.Metadata.Properties[0].Name); @@ -220,7 +220,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png var testFile = TestFile.Create(TestImages.Png.Blur); - using (Image image = testFile.CreateImage(options)) + using (Image image = testFile.CreateRgba32Image(options)) { Assert.Equal(0, image.Metadata.Properties.Count); } @@ -236,7 +236,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png var testFile = TestFile.Create(TestImages.Png.Blur); - using (Image image = testFile.CreateImage(options)) + using (Image image = testFile.CreateRgba32Image(options)) { Assert.Equal(1, image.Metadata.Properties.Count); Assert.Equal("潓瑦慷敲", image.Metadata.Properties[0].Name); diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index 5aa69dd6af..b8178fd4f3 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -219,7 +219,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png var options = new PngEncoder(); var testFile = TestFile.Create(imagePath); - using (Image input = testFile.CreateImage()) + using (Image input = testFile.CreateRgba32Image()) { using (var memStream = new MemoryStream()) { @@ -244,7 +244,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png var options = new PngEncoder(); var testFile = TestFile.Create(imagePath); - using (Image input = testFile.CreateImage()) + using (Image input = testFile.CreateRgba32Image()) { using (var memStream = new MemoryStream()) { @@ -268,7 +268,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Png var options = new PngEncoder(); var testFile = TestFile.Create(imagePath); - using (Image input = testFile.CreateImage()) + using (Image input = testFile.CreateRgba32Image()) { PngMetadata inMeta = input.Metadata.GetFormatMetadata(PngFormat.Instance); Assert.True(inMeta.HasTrans); diff --git a/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs b/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs index aabc3f50e5..ec6705d0ec 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.ImageLoadTestBase.cs @@ -16,7 +16,9 @@ namespace SixLabors.ImageSharp.Tests { public abstract class ImageLoadTestBase : IDisposable { - protected Image returnImage; + protected Image localStreamReturnImageRgba32; + + protected Image localStreamReturnImageAgnostic; protected Mock localDecoder; @@ -48,12 +50,14 @@ namespace SixLabors.ImageSharp.Tests protected ImageLoadTestBase() { - this.returnImage = new Image(1, 1); + this.localStreamReturnImageRgba32 = new Image(1, 1); + this.localStreamReturnImageAgnostic = new Image(1, 1); this.localImageFormatMock = new Mock(); this.localDecoder = new Mock(); this.localMimeTypeDetector = new MockImageFormatDetector(this.localImageFormatMock.Object); + this.localDecoder.Setup(x => x.Decode(It.IsAny(), It.IsAny())) .Callback((c, s) => { @@ -63,7 +67,19 @@ namespace SixLabors.ImageSharp.Tests this.DecodedData = ms.ToArray(); } }) - .Returns(this.returnImage); + .Returns(this.localStreamReturnImageRgba32); + + this.localDecoder.Setup(x => x.Decode(It.IsAny(), It.IsAny())) + .Callback((c, s) => + { + using (var ms = new MemoryStream()) + { + s.CopyTo(ms); + this.DecodedData = ms.ToArray(); + } + }) + .Returns(this.localStreamReturnImageAgnostic); + this.LocalConfiguration = new Configuration { @@ -85,7 +101,8 @@ namespace SixLabors.ImageSharp.Tests public void Dispose() { // clean up the global object; - this.returnImage?.Dispose(); + this.localStreamReturnImageRgba32?.Dispose(); + this.localStreamReturnImageAgnostic?.Dispose(); } } } diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath.cs index 1a21d3d105..d6fe43897c 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath.cs @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Tests Assert.NotNull(img); - this.TestFormat.VerifyDecodeCall(this.Marker, this.TopLevelConfiguration); + this.TestFormat.VerifySpecificDecodeCall(this.Marker, this.TopLevelConfiguration); } [Fact] diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes.cs deleted file mode 100644 index eed1a28252..0000000000 --- a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes.cs +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using System.IO; - -using Moq; - -using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; - -using Xunit; - -// ReSharper disable InconsistentNaming -namespace SixLabors.ImageSharp.Tests -{ - public partial class ImageTests - { - public class Load_FromBytes : ImageLoadTestBase - { - private byte[] ByteArray => this.DataStream.ToArray(); - - private ReadOnlySpan ByteSpan => this.ByteArray.AsSpan(); - - private byte[] ActualImageBytes => TestFile.Create(TestImages.Bmp.F).Bytes; - - private ReadOnlySpan ActualImageSpan => this.ActualImageBytes.AsSpan(); - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void BasicCase(bool useSpan) - { - Image img = useSpan - ? Image.Load(this.TopLevelConfiguration, this.ByteSpan) - : Image.Load(this.TopLevelConfiguration, this.ByteArray); - - Assert.NotNull(img); - Assert.Equal(this.TestFormat.Sample(), img); - - this.TestFormat.VerifyDecodeCall(this.Marker, this.TopLevelConfiguration); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void NonDefaultPixelType(bool useSpan) - { - Image img = useSpan - ? Image.Load(this.TopLevelConfiguration, this.ByteSpan) - : Image.Load(this.TopLevelConfiguration, this.ByteArray); - - Assert.NotNull(img); - Assert.Equal(this.TestFormat.Sample(), img); - - this.TestFormat.VerifyDecodeCall(this.Marker, this.TopLevelConfiguration); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void UseLocalConfiguration(bool useSpan) - { - Image img = useSpan - ? Image.Load(this.LocalConfiguration, this.ByteSpan) - : Image.Load(this.LocalConfiguration, this.ByteArray); - - Assert.NotNull(img); - - this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, It.IsAny())); - - Assert.Equal(this.DataStream.ToArray(), this.DecodedData); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void UseCustomDecoder(bool useSpan) - { - Image img = useSpan - ? Image.Load( - this.TopLevelConfiguration, - this.ByteSpan, - this.localDecoder.Object) - : Image.Load( - this.TopLevelConfiguration, - this.ByteArray, - this.localDecoder.Object); - Assert.NotNull(img); - this.localDecoder.Verify(x => x.Decode(this.TopLevelConfiguration, It.IsAny())); - Assert.Equal(this.DataStream.ToArray(), this.DecodedData); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void UseGlobalConfiguration(bool useSpan) - { - using (Image img = - useSpan ? Image.Load(this.ActualImageSpan) : Image.Load(this.ActualImageBytes)) - { - Assert.Equal(new Size(108, 202), img.Size()); - } - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void UseGlobalConfiguration_NonDefaultPixelType(bool useSpan) - { - using (Image img = useSpan - ? Image.Load(this.ActualImageSpan) - : Image.Load(this.ActualImageBytes)) - { - Assert.Equal(new Size(108, 202), img.Size()); - } - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_PassLocalConfiguration.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_PassLocalConfiguration.cs new file mode 100644 index 0000000000..5fe87fedca --- /dev/null +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_PassLocalConfiguration.cs @@ -0,0 +1,119 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public partial class ImageTests + { + public class Load_FromBytes_PassLocalConfiguration : ImageLoadTestBase + { + private byte[] ByteArray => this.DataStream.ToArray(); + + private ReadOnlySpan ByteSpan => this.ByteArray.AsSpan(); + + private byte[] ActualImageBytes => TestFile.Create(TestImages.Bmp.F).Bytes; + + private ReadOnlySpan ActualImageSpan => this.ActualImageBytes.AsSpan(); + + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void Configuration_Bytes_Specific(bool useSpan) + { + var img = useSpan + ? Image.Load(this.TopLevelConfiguration, this.ByteSpan) + : Image.Load(this.TopLevelConfiguration, this.ByteArray); + + Assert.NotNull(img); + Assert.Equal(this.TestFormat.Sample(), img); + + this.TestFormat.VerifySpecificDecodeCall(this.Marker, this.TopLevelConfiguration); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void Configuration_Bytes_Agnostic(bool useSpan) + { + var img = useSpan + ? Image.Load(this.TopLevelConfiguration, this.ByteSpan) + : Image.Load(this.TopLevelConfiguration, this.ByteArray); + + Assert.NotNull(img); + Assert.Equal(this.TestFormat.SampleAgnostic(), img); + + this.TestFormat.VerifyAgnosticDecodeCall(this.Marker, this.TopLevelConfiguration); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void Configuration_Bytes_Decoder_Specific(bool useSpan) + { + TestFormat localFormat = new TestFormat(); + + var img = useSpan ? + Image.Load(this.TopLevelConfiguration, this.ByteSpan, localFormat.Decoder) : + Image.Load(this.TopLevelConfiguration, this.ByteArray, localFormat.Decoder); + + Assert.NotNull(img); + localFormat.VerifySpecificDecodeCall(this.Marker, this.TopLevelConfiguration); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void Configuration_Bytes_Decoder_Agnostic(bool useSpan) + { + TestFormat localFormat = new TestFormat(); + + var img = useSpan ? + Image.Load(this.TopLevelConfiguration, this.ByteSpan, localFormat.Decoder) : + Image.Load(this.TopLevelConfiguration, this.ByteArray, localFormat.Decoder); + + Assert.NotNull(img); + localFormat.VerifyAgnosticDecodeCall(this.Marker, this.TopLevelConfiguration); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void Configuration_Bytes_OutFormat_Specific(bool useSpan) + { + IImageFormat format; + var img = useSpan ? + Image.Load(this.TopLevelConfiguration, this.ByteSpan, out format) : + Image.Load(this.TopLevelConfiguration, this.ByteArray, out format); + + Assert.NotNull(img); + Assert.Equal(this.TestFormat, format); + + this.TestFormat.VerifySpecificDecodeCall(this.Marker, this.TopLevelConfiguration); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void Configuration_Bytes_OutFormat_Agnostic(bool useSpan) + { + IImageFormat format; + var img = useSpan ? + Image.Load(this.TopLevelConfiguration, this.ByteSpan, out format) : + Image.Load(this.TopLevelConfiguration, this.ByteArray, out format); + + Assert.NotNull(img); + Assert.Equal(this.TestFormat, format); + + this.TestFormat.VerifyAgnosticDecodeCall(this.Marker, this.TopLevelConfiguration); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs new file mode 100644 index 0000000000..23c25d9dbb --- /dev/null +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs @@ -0,0 +1,101 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; +using System.IO; + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public partial class ImageTests + { + public class Load_FromBytes_UseGlobalConfiguration + { + private static byte[] ByteArray { get; } = TestFile.Create(TestImages.Bmp.F).Bytes; + + private static Span ByteSpan => new Span(ByteArray); + + private static void VerifyDecodedImage(Image img) + { + Assert.Equal(new Size(108, 202), img.Size()); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void Bytes_Specific(bool useSpan) + { + using (var img = useSpan ? Image.Load(ByteSpan) : Image.Load(ByteArray)) + { + VerifyDecodedImage(img); + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void Bytes_Agnostic(bool useSpan) + { + using (var img = useSpan ? Image.Load(ByteSpan) : Image.Load(ByteArray)) + { + VerifyDecodedImage(img); + } + } + + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void Bytes_Decoder_Specific(bool useSpan) + { + using (var img = useSpan ? Image.Load(ByteSpan, new BmpDecoder()) : Image.Load(ByteArray, new BmpDecoder())) + { + VerifyDecodedImage(img); + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void Bytes_Decoder_Agnostic(bool useSpan) + { + using (var img = useSpan ? Image.Load(ByteSpan, new BmpDecoder()) : Image.Load(ByteArray, new BmpDecoder())) + { + VerifyDecodedImage(img); + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void Bytes_OutFormat_Specific(bool useSpan) + { + IImageFormat format; + using (var img = useSpan ? Image.Load(ByteSpan, out format) : Image.Load(ByteArray, out format)) + { + VerifyDecodedImage(img); + Assert.IsType(format); + } + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void Bytes_OutFormat_Agnostic(bool useSpan) + { + IImageFormat format; + using (var img = useSpan ? Image.Load(ByteSpan, out format) : Image.Load(ByteArray, out format)) + { + VerifyDecodedImage(img); + Assert.IsType(format); + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream.cs deleted file mode 100644 index 6b6acb1b80..0000000000 --- a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream.cs +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System.IO; - -using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.PixelFormats; - -using Xunit; -// ReSharper disable InconsistentNaming - -namespace SixLabors.ImageSharp.Tests -{ - using SixLabors.Primitives; - - public partial class ImageTests - { - /// - /// Tests the class. - /// - public class Load_FromStream : ImageLoadTestBase - { - [Fact] - public void BasicCase() - { - var img = Image.Load(this.TopLevelConfiguration, this.DataStream); - - Assert.NotNull(img); - Assert.Equal(this.TestFormat.Sample(), img); - - this.TestFormat.VerifyDecodeCall(this.Marker, this.TopLevelConfiguration); - } - - [Fact] - public void UseGlobalConfiguration() - { - byte[] data = TestFile.Create(TestImages.Bmp.F).Bytes; - - using (var stream = new MemoryStream(data)) - using (var img = Image.Load(stream)) - { - Assert.Equal(new Size(108, 202), img.Size()); - } - } - - [Fact] - public void NonDefaultPixelTypeImage() - { - var img = Image.Load(this.TopLevelConfiguration, this.DataStream); - - Assert.NotNull(img); - Assert.Equal(this.TestFormat.Sample(), img); - - this.TestFormat.VerifyDecodeCall(this.Marker, this.TopLevelConfiguration); - } - - [Fact] - public void NonSeekableStream() - { - var stream = new NoneSeekableStream(this.DataStream); - var img = Image.Load(this.TopLevelConfiguration, stream); - - Assert.NotNull(img); - - this.TestFormat.VerifyDecodeCall(this.Marker, this.TopLevelConfiguration); - } - - [Fact] - public void UseLocalConfiguration() - { - Stream stream = new MemoryStream(); - var img = Image.Load(this.LocalConfiguration, stream); - - Assert.NotNull(img); - - this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, stream)); - } - - [Fact] - public void UseCustomDecoder() - { - Stream stream = new MemoryStream(); - var img = Image.Load(this.TopLevelConfiguration, stream, this.localDecoder.Object); - - Assert.NotNull(img); - this.localDecoder.Verify(x => x.Decode(this.TopLevelConfiguration, stream)); - } - - // TODO: This should be a png decoder test! - [Fact] - public void LoadsImageWithoutThrowingCrcException() - { - var image1Provider = TestImageProvider.File(TestImages.Png.VersioningImage1); - - using (Image img = image1Provider.GetImage()) - { - Assert.Equal(166036, img.Frames.RootFrame.GetPixelSpan().Length); - } - } - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_PassLocalConfiguration.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_PassLocalConfiguration.cs new file mode 100644 index 0000000000..5ca86c3cee --- /dev/null +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_PassLocalConfiguration.cs @@ -0,0 +1,93 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public partial class ImageTests + { + public class Load_FromStream_PassLocalConfiguration : ImageLoadTestBase + { + [Fact] + public void Configuration_Stream_Specific() + { + var img = Image.Load(this.TopLevelConfiguration, this.DataStream); + + Assert.NotNull(img); + Assert.Equal(this.TestFormat.Sample(), img); + + this.TestFormat.VerifySpecificDecodeCall(this.Marker, this.TopLevelConfiguration); + } + + [Fact] + public void Configuration_Stream_Agnostic() + { + var img = Image.Load(this.TopLevelConfiguration, this.DataStream); + + Assert.NotNull(img); + Assert.Equal(this.TestFormat.SampleAgnostic(), img); + + this.TestFormat.VerifyAgnosticDecodeCall(this.Marker, this.TopLevelConfiguration); + } + + [Fact] + public void NonSeekableStream() + { + var stream = new NoneSeekableStream(this.DataStream); + var img = Image.Load(this.TopLevelConfiguration, stream); + + Assert.NotNull(img); + + this.TestFormat.VerifySpecificDecodeCall(this.Marker, this.TopLevelConfiguration); + } + + [Fact] + public void Configuration_Stream_Decoder_Specific() + { + Stream stream = new MemoryStream(); + var img = Image.Load(this.TopLevelConfiguration, stream, this.localDecoder.Object); + + Assert.NotNull(img); + this.localDecoder.Verify(x => x.Decode(this.TopLevelConfiguration, stream)); + } + + [Fact] + public void Configuration_Stream_Decoder_Agnostic() + { + Stream stream = new MemoryStream(); + var img = Image.Load(this.TopLevelConfiguration, stream, this.localDecoder.Object); + + Assert.NotNull(img); + this.localDecoder.Verify(x => x.Decode(this.TopLevelConfiguration, stream)); + } + + [Fact] + public void Configuration_Stream_OutFormat_Specific() + { + var img = Image.Load(this.TopLevelConfiguration, this.DataStream, out IImageFormat format); + + Assert.NotNull(img); + Assert.Equal(this.TestFormat, format); + + this.TestFormat.VerifySpecificDecodeCall(this.Marker, this.TopLevelConfiguration); + } + + [Fact] + public void Configuration_Stream_OutFormat_Agnostic() + { + var img = Image.Load(this.TopLevelConfiguration, this.DataStream, out IImageFormat format); + + Assert.NotNull(img); + Assert.Equal(this.TestFormat, format); + + this.TestFormat.VerifyAgnosticDecodeCall(this.Marker, this.TopLevelConfiguration); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_UseDefaultConfiguration.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_UseDefaultConfiguration.cs new file mode 100644 index 0000000000..27aba42ead --- /dev/null +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_UseDefaultConfiguration.cs @@ -0,0 +1,95 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System.IO; + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public partial class ImageTests + { + public class Load_FromStream_UseDefaultConfiguration + { + [Fact] + public void Stream_Specific() + { + byte[] data = TestFile.Create(TestImages.Bmp.F).Bytes; + + using (var stream = new MemoryStream(data)) + using (var img = Image.Load(stream)) + { + Assert.Equal(new Size(108, 202), img.Size()); + } + } + + [Fact] + public void Stream_Agnostic() + { + byte[] data = TestFile.Create(TestImages.Bmp.F).Bytes; + + using (var stream = new MemoryStream(data)) + using (var img = Image.Load(stream)) + { + Assert.Equal(new Size(108, 202), img.Size()); + } + } + + [Fact] + public void Stream_OutFormat_Specific() + { + byte[] data = TestFile.Create(TestImages.Bmp.F).Bytes; + + + using (var stream = new MemoryStream(data)) + using (var img = Image.Load(stream, out IImageFormat format)) + { + Assert.Equal(new Size(108, 202), img.Size()); + Assert.IsType(format); + } + } + + [Fact] + public void Stream_Decoder_Specific() + { + byte[] data = TestFile.Create(TestImages.Bmp.F).Bytes; + + using (var stream = new MemoryStream(data)) + using (var img = Image.Load(stream, new BmpDecoder())) + { + Assert.Equal(new Size(108, 202), img.Size()); + } + } + + [Fact] + public void Stream_Decoder_Agnostic() + { + byte[] data = TestFile.Create(TestImages.Bmp.F).Bytes; + + using (var stream = new MemoryStream(data)) + using (var img = Image.Load(stream, new BmpDecoder())) + { + Assert.Equal(new Size(108, 202), img.Size()); + } + } + + [Fact] + public void Stream_OutFormat_Agnostic() + { + byte[] data = TestFile.Create(TestImages.Bmp.F).Bytes; + + using (var stream = new MemoryStream(data)) + using (var img = Image.Load(stream, out IImageFormat format)) + { + Assert.Equal(new Size(108, 202), img.Size()); + Assert.IsType(format); + } + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs index 9d145f3805..668e699c5f 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifProfileTests.cs @@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Tests [InlineData(TestImageWriteFormat.Png)] public void Constructor(TestImageWriteFormat imageFormat) { - Image image = TestFile.Create(TestImages.Jpeg.Baseline.Calliphora).CreateImage(); + Image image = TestFile.Create(TestImages.Jpeg.Baseline.Calliphora).CreateRgba32Image(); Assert.Null(image.Metadata.ExifProfile); @@ -126,7 +126,7 @@ namespace SixLabors.ImageSharp.Tests [InlineData(TestImageWriteFormat.Png)] public void ReadWriteInfinity(TestImageWriteFormat imageFormat) { - Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage(); + Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image(); image.Metadata.ExifProfile.SetValue(ExifTag.ExposureBiasValue, new SignedRational(double.PositiveInfinity)); image = WriteAndReadJpeg(image); @@ -156,7 +156,7 @@ namespace SixLabors.ImageSharp.Tests { var latitude = new Rational[] { new Rational(12.3), new Rational(4.56), new Rational(789.0) }; - Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage(); + Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image(); image.Metadata.ExifProfile.SetValue(ExifTag.Software, "ImageSharp"); ExifValue value = image.Metadata.ExifProfile.GetValue(ExifTag.Software); @@ -314,7 +314,7 @@ namespace SixLabors.ImageSharp.Tests // This image contains an 802 byte EXIF profile // It has a tag with an index offset of 18,481,152 bytes (overrunning the data) - Image image = TestFile.Create(TestImages.Jpeg.Progressive.Bad.ExifUndefType).CreateImage(); + Image image = TestFile.Create(TestImages.Jpeg.Progressive.Bad.ExifUndefType).CreateRgba32Image(); Assert.NotNull(image); ExifProfile profile = image.Metadata.ExifProfile; @@ -333,7 +333,7 @@ namespace SixLabors.ImageSharp.Tests public void TestArrayValueWithUnspecifiedSize() { // This images contains array in the exif profile that has zero components. - Image image = TestFile.Create(TestImages.Jpeg.Issues.InvalidCast520).CreateImage(); + Image image = TestFile.Create(TestImages.Jpeg.Issues.InvalidCast520).CreateRgba32Image(); ExifProfile profile = image.Metadata.ExifProfile; Assert.NotNull(profile); @@ -408,7 +408,7 @@ namespace SixLabors.ImageSharp.Tests internal static ExifProfile GetExifProfile() { - Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage(); + Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image(); ExifProfile profile = image.Metadata.ExifProfile; Assert.NotNull(profile); diff --git a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs index 4327cae335..8d786811cf 100644 --- a/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs +++ b/tests/ImageSharp.Tests/MetaData/Profiles/Exif/ExifValueTests.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Tests private static ExifValue GetExifValue() { ExifProfile profile; - using (Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateImage()) + using (Image image = TestFile.Create(TestImages.Jpeg.Baseline.Floorplan).CreateRgba32Image()) { profile = image.Metadata.ExifProfile; } diff --git a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs index 9c3ea90d53..0c7a760818 100644 --- a/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs +++ b/tests/ImageSharp.Tests/PixelFormats/PixelBlenders/PorterDuffCompositorTests.cs @@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Tests.PixelFormats.PixelBlenders public void PorterDuffOutputIsCorrect(TestImageProvider provider, PixelAlphaCompositionMode mode) { var srcFile = TestFile.Create(TestImages.Png.PDSrc); - using (Image src = srcFile.CreateImage()) + using (Image src = srcFile.CreateRgba32Image()) using (Image dest = provider.GetImage()) { GraphicsOptions options = new GraphicsOptions diff --git a/tests/ImageSharp.Tests/TestFile.cs b/tests/ImageSharp.Tests/TestFile.cs index 089249e217..6ca86ced6e 100644 --- a/tests/ImageSharp.Tests/TestFile.cs +++ b/tests/ImageSharp.Tests/TestFile.cs @@ -134,7 +134,7 @@ namespace SixLabors.ImageSharp.Tests /// /// The . /// - public Image CreateImage() + public Image CreateRgba32Image() { return this.Image.Clone(); } @@ -145,9 +145,9 @@ namespace SixLabors.ImageSharp.Tests /// /// The . /// - public Image CreateImage(IImageDecoder decoder) + public Image CreateRgba32Image(IImageDecoder decoder) { - return ImageSharp.Image.Load(this.Image.GetConfiguration(), this.Bytes, decoder); + return ImageSharp.Image.Load(this.Image.GetConfiguration(), this.Bytes, decoder); } } } diff --git a/tests/ImageSharp.Tests/TestFileSystem.cs b/tests/ImageSharp.Tests/TestFileSystem.cs index 21ad4d2c1a..9211e70f79 100644 --- a/tests/ImageSharp.Tests/TestFileSystem.cs +++ b/tests/ImageSharp.Tests/TestFileSystem.cs @@ -12,29 +12,24 @@ namespace SixLabors.ImageSharp.Tests /// public class TestFileSystem : ImageSharp.IO.IFileSystem { - - public static TestFileSystem Global { get; } = new TestFileSystem(); - - public static void RegisterGlobalTestFormat() - { - Configuration.Default.FileSystem = Global; - } - - Dictionary fileSystem = new Dictionary(StringComparer.OrdinalIgnoreCase); + private readonly Dictionary fileSystem = new Dictionary(StringComparer.OrdinalIgnoreCase); public void AddFile(string path, Stream data) { - fileSystem.Add(path, data); + lock (this.fileSystem) + { + this.fileSystem.Add(path, data); + } } public Stream Create(string path) { // if we have injected a fake file use it instead - lock (fileSystem) + lock (this.fileSystem) { - if (fileSystem.ContainsKey(path)) + if (this.fileSystem.ContainsKey(path)) { - Stream stream = fileSystem[path]; + Stream stream = this.fileSystem[path]; stream.Position = 0; return stream; } @@ -43,15 +38,14 @@ namespace SixLabors.ImageSharp.Tests return File.Create(path); } - public Stream OpenRead(string path) { // if we have injected a fake file use it instead - lock (fileSystem) + lock (this.fileSystem) { - if (fileSystem.ContainsKey(path)) + if (this.fileSystem.ContainsKey(path)) { - Stream stream = fileSystem[path]; + Stream stream = this.fileSystem[path]; stream.Position = 0; return stream; } @@ -60,5 +54,4 @@ namespace SixLabors.ImageSharp.Tests return File.OpenRead(path); } } -} - +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestFormat.cs b/tests/ImageSharp.Tests/TestFormat.cs index 91e31b356e..23bd0a54c3 100644 --- a/tests/ImageSharp.Tests/TestFormat.cs +++ b/tests/ImageSharp.Tests/TestFormat.cs @@ -6,6 +6,7 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Numerics; using System.Reflection; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.PixelFormats; @@ -18,6 +19,8 @@ namespace SixLabors.ImageSharp.Tests /// public class TestFormat : IConfigurationModule, IImageFormat { + private readonly Dictionary sampleImages = new Dictionary(); + // We should not change Configuration.Default in individual tests! // Create new configuration instances with new Configuration(TestFormat.GlobalTestFormat) instead! public static TestFormat GlobalTestFormat { get; } = new TestFormat(); @@ -49,12 +52,23 @@ namespace SixLabors.ImageSharp.Tests return ms; } - Dictionary _sampleImages = new Dictionary(); + public void VerifySpecificDecodeCall(byte[] marker, Configuration config) + where TPixel : struct, IPixel + { + DecodeOperation[] discovered = this.DecodeCalls.Where(x => x.IsMatch(marker, config, typeof(TPixel))).ToArray(); - public void VerifyDecodeCall(byte[] marker, Configuration config) + Assert.True(discovered.Any(), "No calls to decode on this formate with the proveded options happend"); + + foreach (DecodeOperation d in discovered) + { + this.DecodeCalls.Remove(d); + } + } + + public void VerifyAgnosticDecodeCall(byte[] marker, Configuration config) { - DecodeOperation[] discovered = this.DecodeCalls.Where(x => x.IsMatch(marker, config)).ToArray(); + DecodeOperation[] discovered = this.DecodeCalls.Where(x => x.IsMatch(marker, config, typeof(TestPixelForAgnosticDecode))).ToArray(); Assert.True(discovered.Any(), "No calls to decode on this formate with the proveded options happend"); @@ -68,17 +82,19 @@ namespace SixLabors.ImageSharp.Tests public Image Sample() where TPixel : struct, IPixel { - lock (this._sampleImages) + lock (this.sampleImages) { - if (!this._sampleImages.ContainsKey(typeof(TPixel))) + if (!this.sampleImages.ContainsKey(typeof(TPixel))) { - this._sampleImages.Add(typeof(TPixel), new Image(1, 1)); + this.sampleImages.Add(typeof(TPixel), new Image(1, 1)); } - return (Image)this._sampleImages[typeof(TPixel)]; + return (Image)this.sampleImages[typeof(TPixel)]; } } + public Image SampleAgnostic() => this.Sample(); + public string MimeType => "img/test"; public string Extension => "test_ext"; @@ -123,10 +139,12 @@ namespace SixLabors.ImageSharp.Tests public byte[] marker; internal Configuration config; - public bool IsMatch(byte[] testMarker, Configuration config) + public Type pixelType; + + public bool IsMatch(byte[] testMarker, Configuration config, Type pixelType) { - if (this.config != config) + if (this.config != config || this.pixelType != pixelType) { return false; } @@ -191,7 +209,8 @@ namespace SixLabors.ImageSharp.Tests this.testFormat.DecodeCalls.Add(new DecodeOperation { marker = marker, - config = config + config = config, + pixelType = typeof(TPixel) }); // TODO record this happend so we can verify it. @@ -200,7 +219,7 @@ namespace SixLabors.ImageSharp.Tests public bool IsSupportedFileFormat(Span header) => testFormat.IsSupportedFileFormat(header); - public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); + public Image Decode(Configuration configuration, Stream stream) => this.Decode(configuration, stream); } public class TestEncoder : ImageSharp.Formats.IImageEncoder @@ -221,5 +240,27 @@ namespace SixLabors.ImageSharp.Tests // TODO record this happend so we can verify it. } } + + + struct TestPixelForAgnosticDecode : IPixel + { + public PixelOperations CreatePixelOperations() => new PixelOperations(); + public void FromScaledVector4(Vector4 vector) { } + public Vector4 ToScaledVector4() => default; + public void FromVector4(Vector4 vector) { } + public Vector4 ToVector4() => default; + public void FromArgb32(Argb32 source) { } + public void FromBgra5551(Bgra5551 source) { } + public void FromBgr24(Bgr24 source) { } + public void FromBgra32(Bgra32 source) { } + public void FromGray8(Gray8 source) { } + public void FromGray16(Gray16 source) { } + public void FromRgb24(Rgb24 source) { } + public void FromRgba32(Rgba32 source) { } + public void ToRgba32(ref Rgba32 dest) { } + public void FromRgb48(Rgb48 source) { } + public void FromRgba64(Rgba64 source) { } + public bool Equals(TestPixelForAgnosticDecode other) => false; + } } } diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceDecoderBenchmarks.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceDecoderBenchmarks.cs index 033f0866a3..15213d5d32 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceDecoderBenchmarks.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/ReferenceDecoderBenchmarks.cs @@ -86,7 +86,7 @@ namespace SixLabors.ImageSharp.Tests.TestUtilities.Tests { foreach (string testFile in testFiles) { - Image image = TestFile.Create(testFile).CreateImage(decoder); + Image image = TestFile.Create(testFile).CreateRgba32Image(decoder); image.Dispose(); } }, From 35640879f22c362f32d6a8e24a26cb09f5b6709b Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 May 2019 21:19:35 +0200 Subject: [PATCH 25/31] full coverage for Image.Load (I hope) --- .../Image/ImageTests.Load_FileSystemPath.cs | 83 -------------- ...d_FileSystemPath_PassLocalConfiguration.cs | 100 +++++++++++++++++ ..._FileSystemPath_UseDefaultConfiguration.cs | 103 ++++++++++++++++++ ...s.Load_FromBytes_UseGlobalConfiguration.cs | 4 +- ...Load_FromStream_UseDefaultConfiguration.cs | 60 +++++----- .../ImageSharp.Tests/ImageSharp.Tests.csproj | 3 +- 6 files changed, 234 insertions(+), 119 deletions(-) delete mode 100644 tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath.cs create mode 100644 tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_PassLocalConfiguration.cs create mode 100644 tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_UseDefaultConfiguration.cs diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath.cs deleted file mode 100644 index d6fe43897c..0000000000 --- a/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath.cs +++ /dev/null @@ -1,83 +0,0 @@ -// Copyright (c) Six Labors and contributors. -// Licensed under the Apache License, Version 2.0. - -using System; -using SixLabors.ImageSharp.PixelFormats; -using Xunit; -// ReSharper disable InconsistentNaming - -namespace SixLabors.ImageSharp.Tests -{ - using Moq; - - using SixLabors.ImageSharp.IO; - - public partial class ImageTests - { - public class Load_FileSystemPath : ImageLoadTestBase - { - [Fact] - public void BasicCase() - { - var img = Image.Load(this.TopLevelConfiguration, this.MockFilePath); - - Assert.NotNull(img); - - this.TestFormat.VerifySpecificDecodeCall(this.Marker, this.TopLevelConfiguration); - } - - [Fact] - public void UseLocalConfiguration() - { - var img = Image.Load(this.LocalConfiguration, this.MockFilePath); - - Assert.NotNull(img); - - this.localDecoder.Verify(x => x.Decode(this.LocalConfiguration, this.DataStream)); - } - - [Fact] - public void UseCustomDecoder() - { - var img = Image.Load(this.TopLevelConfiguration, this.MockFilePath, this.localDecoder.Object); - - Assert.NotNull(img); - this.localDecoder.Verify(x => x.Decode(this.TopLevelConfiguration, this.DataStream)); - } - - - [Fact] - public void UseGlobalConfigration() - { - var file = TestFile.Create(TestImages.Bmp.Car); - using (var image = Image.Load(file.FullPath)) - { - Assert.Equal(600, image.Width); - Assert.Equal(450, image.Height); - } - } - - [Fact] - public void WhenFileNotFound_Throws() - { - System.IO.FileNotFoundException ex = Assert.Throws( - () => - { - Image.Load(Guid.NewGuid().ToString()); - }); - } - - [Fact] - public void WhenPathIsNull_Throws() - { - ArgumentNullException ex = Assert.Throws( - () => - { - Image.Load((string)null); - }); - } - } - - - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_PassLocalConfiguration.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_PassLocalConfiguration.cs new file mode 100644 index 0000000000..92159f0c9a --- /dev/null +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_PassLocalConfiguration.cs @@ -0,0 +1,100 @@ +// // Copyright (c) Six Labors and contributors. +// // Licensed under the Apache License, Version 2.0. + +using System; + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.PixelFormats; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public partial class ImageTests + { + public class Load_FileSystemPath_PassLocalConfiguration : ImageLoadTestBase + { + [Fact] + public void Configuration_Path_Specific() + { + var img = Image.Load(this.TopLevelConfiguration, this.MockFilePath); + + Assert.NotNull(img); + Assert.Equal(this.TestFormat.Sample(), img); + + this.TestFormat.VerifySpecificDecodeCall(this.Marker, this.TopLevelConfiguration); + } + + [Fact] + public void Configuration_Path_Agnostic() + { + var img = Image.Load(this.TopLevelConfiguration, this.MockFilePath); + + Assert.NotNull(img); + Assert.Equal(this.TestFormat.SampleAgnostic(), img); + + this.TestFormat.VerifyAgnosticDecodeCall(this.Marker, this.TopLevelConfiguration); + } + + [Fact] + public void Configuration_Path_Decoder_Specific() + { + var img = Image.Load(this.TopLevelConfiguration, this.MockFilePath, this.localDecoder.Object); + + Assert.NotNull(img); + this.localDecoder.Verify(x => x.Decode(this.TopLevelConfiguration, this.DataStream)); + } + + [Fact] + public void Configuration_Path_Decoder_Agnostic() + { + var img = Image.Load(this.TopLevelConfiguration, this.MockFilePath, this.localDecoder.Object); + + Assert.NotNull(img); + this.localDecoder.Verify(x => x.Decode(this.TopLevelConfiguration, this.DataStream)); + } + + [Fact] + public void Configuration_Path_OutFormat_Specific() + { + var img = Image.Load(this.TopLevelConfiguration, this.MockFilePath, out IImageFormat format); + + Assert.NotNull(img); + Assert.Equal(this.TestFormat, format); + + this.TestFormat.VerifySpecificDecodeCall(this.Marker, this.TopLevelConfiguration); + } + + [Fact] + public void Configuration_Path_OutFormat_Agnostic() + { + var img = Image.Load(this.TopLevelConfiguration, this.MockFilePath, out IImageFormat format); + + Assert.NotNull(img); + Assert.Equal(this.TestFormat, format); + + this.TestFormat.VerifyAgnosticDecodeCall(this.Marker, this.TopLevelConfiguration); + } + + [Fact] + public void WhenFileNotFound_Throws() + { + System.IO.FileNotFoundException ex = Assert.Throws( + () => + { + Image.Load(this.TopLevelConfiguration, Guid.NewGuid().ToString()); + }); + } + + [Fact] + public void WhenPathIsNull_Throws() + { + ArgumentNullException ex = Assert.Throws( + () => + { + Image.Load(this.TopLevelConfiguration,(string)null); + }); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_UseDefaultConfiguration.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_UseDefaultConfiguration.cs new file mode 100644 index 0000000000..19cf7ee647 --- /dev/null +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FileSystemPath_UseDefaultConfiguration.cs @@ -0,0 +1,103 @@ +// // Copyright (c) Six Labors and contributors. +// // Licensed under the Apache License, Version 2.0. + +using System; + +using SixLabors.ImageSharp.Formats; +using SixLabors.ImageSharp.Formats.Bmp; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +using Xunit; + +namespace SixLabors.ImageSharp.Tests +{ + public partial class ImageTests + { + public class Load_FileSystemPath_UseDefaultConfiguration + { + private string Path { get; } = TestFile.GetInputFileFullPath(TestImages.Bmp.Bit8); + + private static void VerifyDecodedImage(Image img) + { + Assert.Equal(new Size(127, 64), img.Size()); + } + + [Fact] + public void Path_Specific() + { + using (var img = Image.Load(this.Path)) + { + VerifyDecodedImage(img); + } + } + + [Fact] + public void Path_Agnostic() + { + using (var img = Image.Load(this.Path)) + { + VerifyDecodedImage(img); + } + } + + + [Fact] + public void Path_Decoder_Specific() + { + using (var img = Image.Load(this.Path, new BmpDecoder())) + { + VerifyDecodedImage(img); + } + } + + [Fact] + public void Path_Decoder_Agnostic() + { + using (var img = Image.Load(this.Path, new BmpDecoder())) + { + VerifyDecodedImage(img); + } + } + + [Fact] + public void Path_OutFormat_Specific() + { + using (var img = Image.Load(this.Path, out IImageFormat format)) + { + VerifyDecodedImage(img); + Assert.IsType(format); + } + } + + [Fact] + public void Path_OutFormat_Agnostic() + { + using (var img = Image.Load(this.Path, out IImageFormat format)) + { + VerifyDecodedImage(img); + Assert.IsType(format); + } + } + [Fact] + public void WhenFileNotFound_Throws() + { + System.IO.FileNotFoundException ex = Assert.Throws( + () => + { + Image.Load(Guid.NewGuid().ToString()); + }); + } + + [Fact] + public void WhenPathIsNull_Throws() + { + ArgumentNullException ex = Assert.Throws( + () => + { + Image.Load((string)null); + }); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs index 23c25d9dbb..67b19a0867 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromBytes_UseGlobalConfiguration.cs @@ -17,13 +17,13 @@ namespace SixLabors.ImageSharp.Tests { public class Load_FromBytes_UseGlobalConfiguration { - private static byte[] ByteArray { get; } = TestFile.Create(TestImages.Bmp.F).Bytes; + private static byte[] ByteArray { get; } = TestFile.Create(TestImages.Bmp.Bit8).Bytes; private static Span ByteSpan => new Span(ByteArray); private static void VerifyDecodedImage(Image img) { - Assert.Equal(new Size(108, 202), img.Size()); + Assert.Equal(new Size(127, 64), img.Size()); } [Theory] diff --git a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_UseDefaultConfiguration.cs b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_UseDefaultConfiguration.cs index 27aba42ead..980ed17ceb 100644 --- a/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_UseDefaultConfiguration.cs +++ b/tests/ImageSharp.Tests/Image/ImageTests.Load_FromStream_UseDefaultConfiguration.cs @@ -1,6 +1,7 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. +using System; using System.IO; using SixLabors.ImageSharp.Formats; @@ -14,42 +15,41 @@ namespace SixLabors.ImageSharp.Tests { public partial class ImageTests { - public class Load_FromStream_UseDefaultConfiguration + public class Load_FromStream_UseDefaultConfiguration : IDisposable { + private static readonly byte[] Data = TestFile.Create(TestImages.Bmp.Bit8).Bytes; + + private MemoryStream Stream { get; } = new MemoryStream(Data); + + private static void VerifyDecodedImage(Image img) + { + Assert.Equal(new Size(127, 64), img.Size()); + } + [Fact] public void Stream_Specific() { - byte[] data = TestFile.Create(TestImages.Bmp.F).Bytes; - - using (var stream = new MemoryStream(data)) - using (var img = Image.Load(stream)) + using (var img = Image.Load(this.Stream)) { - Assert.Equal(new Size(108, 202), img.Size()); + VerifyDecodedImage(img); } } [Fact] public void Stream_Agnostic() { - byte[] data = TestFile.Create(TestImages.Bmp.F).Bytes; - - using (var stream = new MemoryStream(data)) - using (var img = Image.Load(stream)) + using (var img = Image.Load(this.Stream)) { - Assert.Equal(new Size(108, 202), img.Size()); + VerifyDecodedImage(img); } } [Fact] public void Stream_OutFormat_Specific() { - byte[] data = TestFile.Create(TestImages.Bmp.F).Bytes; - - - using (var stream = new MemoryStream(data)) - using (var img = Image.Load(stream, out IImageFormat format)) + using (var img = Image.Load(this.Stream, out IImageFormat format)) { - Assert.Equal(new Size(108, 202), img.Size()); + VerifyDecodedImage(img); Assert.IsType(format); } } @@ -57,39 +57,35 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void Stream_Decoder_Specific() { - byte[] data = TestFile.Create(TestImages.Bmp.F).Bytes; - - using (var stream = new MemoryStream(data)) - using (var img = Image.Load(stream, new BmpDecoder())) + using (var img = Image.Load(this.Stream, new BmpDecoder())) { - Assert.Equal(new Size(108, 202), img.Size()); + VerifyDecodedImage(img); } } [Fact] public void Stream_Decoder_Agnostic() { - byte[] data = TestFile.Create(TestImages.Bmp.F).Bytes; - - using (var stream = new MemoryStream(data)) - using (var img = Image.Load(stream, new BmpDecoder())) + using (var img = Image.Load(this.Stream, new BmpDecoder())) { - Assert.Equal(new Size(108, 202), img.Size()); + VerifyDecodedImage(img); } } [Fact] public void Stream_OutFormat_Agnostic() { - byte[] data = TestFile.Create(TestImages.Bmp.F).Bytes; - - using (var stream = new MemoryStream(data)) - using (var img = Image.Load(stream, out IImageFormat format)) + using (var img = Image.Load(this.Stream, out IImageFormat format)) { - Assert.Equal(new Size(108, 202), img.Size()); + VerifyDecodedImage(img); Assert.IsType(format); } } + + public void Dispose() + { + this.Stream?.Dispose(); + } } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj index efdf89964c..0b727f30ce 100644 --- a/tests/ImageSharp.Tests/ImageSharp.Tests.csproj +++ b/tests/ImageSharp.Tests/ImageSharp.Tests.csproj @@ -1,7 +1,6 @@  - - netcoreapp2.1 + netcoreapp2.1;net462;net472 True latest full From 3998b6d895c01551356979689320c3ad458546ed Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 May 2019 21:32:54 +0200 Subject: [PATCH 26/31] fix changes applied by mistake --- src/ImageSharp.Drawing/ImageSharp.Drawing.csproj | 2 +- src/ImageSharp/ImageSharp.csproj | 2 +- .../Processing/Processors/Dithering/DitherTests.cs | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj index ac5d7fdea6..915915f6cc 100644 --- a/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj +++ b/src/ImageSharp.Drawing/ImageSharp.Drawing.csproj @@ -10,8 +10,8 @@ $(packageversion) 0.0.1 + netcoreapp2.1;netstandard1.3;netstandard2.0 - netstandard2.0 7.3 true diff --git a/src/ImageSharp/ImageSharp.csproj b/src/ImageSharp/ImageSharp.csproj index e763b8ef1c..d734648f41 100644 --- a/src/ImageSharp/ImageSharp.csproj +++ b/src/ImageSharp/ImageSharp.csproj @@ -10,7 +10,7 @@ $(packageversion) 0.0.1 - + netcoreapp2.1;netstandard1.3;netstandard2.0;net472 true diff --git a/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs index 91422faaa1..cb901c2a88 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Dithering/DitherTests.cs @@ -50,7 +50,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Binarization /// The output is visually correct old 32bit runtime, /// but it is very different because of floating point inaccuracies. /// - private static readonly bool SkipAllDitherTests = false; + private static readonly bool SkipAllDitherTests = + !TestEnvironment.Is64BitProcess && string.IsNullOrEmpty(TestEnvironment.NetCoreVersion); [Theory] [WithFile(TestImages.Png.CalliphoraPartial, PixelTypes.Rgba32)] From f4e497de5a322efe9db633c4ec3f1780739b8249 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 May 2019 22:13:29 +0200 Subject: [PATCH 27/31] Fix docs for processing extensions --- .../Processing/AutoOrientExtensions.cs | 3 ++- .../Processing/BackgroundColorExtensions.cs | 3 ++- .../Processing/BinaryDiffuseExtensions.cs | 11 ++++---- .../Processing/BinaryDitherExtensions.cs | 11 ++++---- .../Processing/BinaryThresholdExtensions.cs | 11 ++++---- .../Processing/BlackWhiteExtensions.cs | 7 ++--- .../Processing/BoxBlurExtensions.cs | 9 ++++--- .../Processing/BrightnessExtensions.cs | 7 ++--- .../Processing/ColorBlindnessExtensions.cs | 8 +++--- .../Processing/ContrastExtensions.cs | 8 +++--- src/ImageSharp/Processing/CropExtensions.cs | 7 ++--- .../Processing/DetectEdgesExtensions.cs | 16 +++++------ .../Processing/DiffuseExtensions.cs | 15 ++++++----- src/ImageSharp/Processing/DitherExtensions.cs | 13 ++++----- .../Processing/EntropyCropExtensions.cs | 7 ++--- src/ImageSharp/Processing/FilterExtensions.cs | 7 ++--- src/ImageSharp/Processing/FlipExtensions.cs | 5 ++-- .../Processing/GaussianBlurExtensions.cs | 9 ++++--- .../Processing/GaussianSharpenExtensions.cs | 9 ++++--- src/ImageSharp/Processing/GlowExtensions.cs | 27 ++++++++++--------- .../Processing/GrayscaleExtensions.cs | 3 ++- .../HistogramEqualizationExtension.cs | 6 ++--- src/ImageSharp/Processing/HueExtensions.cs | 7 ++--- src/ImageSharp/Processing/InvertExtensions.cs | 7 ++--- .../Processing/KodachromeExtensions.cs | 7 ++--- .../Processing/LomographExtensions.cs | 7 ++--- .../Processing/OilPaintExtensions.cs | 11 ++++---- .../Processing/OpacityExtensions.cs | 7 ++--- src/ImageSharp/Processing/PadExtensions.cs | 5 ++-- .../Processing/PixelateExtensions.cs | 9 ++++--- .../Processing/PolaroidExtensions.cs | 7 ++--- .../Processing/QuantizeExtensions.cs | 7 ++--- src/ImageSharp/Processing/ResizeExtensions.cs | 23 ++++++++-------- src/ImageSharp/Processing/RotateExtensions.cs | 9 ++++--- .../Processing/RotateFlipExtensions.cs | 5 ++-- .../Processing/SaturateExtensions.cs | 7 ++--- src/ImageSharp/Processing/SepiaExtensions.cs | 11 ++++---- src/ImageSharp/Processing/SkewExtensions.cs | 7 ++--- .../Processing/TransformExtensions.cs | 17 ++++++------ .../Processing/VignetteExtensions.cs | 23 ++++++++-------- 40 files changed, 207 insertions(+), 171 deletions(-) diff --git a/src/ImageSharp/Processing/AutoOrientExtensions.cs b/src/ImageSharp/Processing/AutoOrientExtensions.cs index d11fc96237..d065d4583d 100644 --- a/src/ImageSharp/Processing/AutoOrientExtensions.cs +++ b/src/ImageSharp/Processing/AutoOrientExtensions.cs @@ -7,7 +7,8 @@ using SixLabors.ImageSharp.Processing.Processors.Transforms; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of auto-orientation operations to the type. + /// Defines extensions that allow the application of auto-orientation operations to an + /// using Mutate/Clone. /// public static class AutoOrientExtensions { diff --git a/src/ImageSharp/Processing/BackgroundColorExtensions.cs b/src/ImageSharp/Processing/BackgroundColorExtensions.cs index 1ad2c92371..3b794e3351 100644 --- a/src/ImageSharp/Processing/BackgroundColorExtensions.cs +++ b/src/ImageSharp/Processing/BackgroundColorExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of a background color to the type. + /// Defines extension methods to replace the background color of an + /// using Mutate/Clone. /// public static class BackgroundColorExtensions { diff --git a/src/ImageSharp/Processing/BinaryDiffuseExtensions.cs b/src/ImageSharp/Processing/BinaryDiffuseExtensions.cs index 788942dde4..487b64e1c4 100644 --- a/src/ImageSharp/Processing/BinaryDiffuseExtensions.cs +++ b/src/ImageSharp/Processing/BinaryDiffuseExtensions.cs @@ -9,7 +9,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds binary diffusion extensions to the type. + /// Defines extension methods to apply binary diffusion on an + /// using Mutate/Clone. /// public static class BinaryDiffuseExtensions { @@ -20,7 +21,7 @@ namespace SixLabors.ImageSharp.Processing /// The image this method extends. /// The diffusion algorithm to apply. /// The threshold to apply binarization of the image. Must be between 0 and 1. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BinaryDiffuse(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold) where TPixel : struct, IPixel => source.ApplyProcessor(new BinaryErrorDiffusionProcessor(diffuser, threshold)); @@ -35,7 +36,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BinaryDiffuse(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold, Rectangle rectangle) where TPixel : struct, IPixel => source.ApplyProcessor(new BinaryErrorDiffusionProcessor(diffuser, threshold), rectangle); @@ -49,7 +50,7 @@ namespace SixLabors.ImageSharp.Processing /// The threshold to apply binarization of the image. Must be between 0 and 1. /// The color to use for pixels that are above the threshold. /// The color to use for pixels that are below the threshold - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BinaryDiffuse(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold, TPixel upperColor, TPixel lowerColor) where TPixel : struct, IPixel => source.ApplyProcessor(new BinaryErrorDiffusionProcessor(diffuser, threshold, upperColor, lowerColor)); @@ -66,7 +67,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BinaryDiffuse(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold, TPixel upperColor, TPixel lowerColor, Rectangle rectangle) where TPixel : struct, IPixel => source.ApplyProcessor(new BinaryErrorDiffusionProcessor(diffuser, threshold, upperColor, lowerColor), rectangle); diff --git a/src/ImageSharp/Processing/BinaryDitherExtensions.cs b/src/ImageSharp/Processing/BinaryDitherExtensions.cs index 6177701964..d8843dafac 100644 --- a/src/ImageSharp/Processing/BinaryDitherExtensions.cs +++ b/src/ImageSharp/Processing/BinaryDitherExtensions.cs @@ -9,7 +9,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds binary dithering extensions to the type. + /// Defines extensions to apply binary dithering on an + /// using Mutate/Clone. /// public static class BinaryDitherExtensions { @@ -19,7 +20,7 @@ namespace SixLabors.ImageSharp.Processing /// The pixel format. /// The image this method extends. /// The ordered ditherer. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BinaryDither(this IImageProcessingContext source, IOrderedDither dither) where TPixel : struct, IPixel => source.ApplyProcessor(new BinaryOrderedDitherProcessor(dither)); @@ -32,7 +33,7 @@ namespace SixLabors.ImageSharp.Processing /// The ordered ditherer. /// The color to use for pixels that are above the threshold. /// The color to use for pixels that are below the threshold - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BinaryDither(this IImageProcessingContext source, IOrderedDither dither, TPixel upperColor, TPixel lowerColor) where TPixel : struct, IPixel => source.ApplyProcessor(new BinaryOrderedDitherProcessor(dither, upperColor, lowerColor)); @@ -46,7 +47,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BinaryDither(this IImageProcessingContext source, IOrderedDither dither, Rectangle rectangle) where TPixel : struct, IPixel => source.ApplyProcessor(new BinaryOrderedDitherProcessor(dither), rectangle); @@ -62,7 +63,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BinaryDither(this IImageProcessingContext source, IOrderedDither dither, TPixel upperColor, TPixel lowerColor, Rectangle rectangle) where TPixel : struct, IPixel => source.ApplyProcessor(new BinaryOrderedDitherProcessor(dither, upperColor, lowerColor), rectangle); diff --git a/src/ImageSharp/Processing/BinaryThresholdExtensions.cs b/src/ImageSharp/Processing/BinaryThresholdExtensions.cs index 31f81ba4b1..aecb784848 100644 --- a/src/ImageSharp/Processing/BinaryThresholdExtensions.cs +++ b/src/ImageSharp/Processing/BinaryThresholdExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds binary thresholding extensions to the type. + /// Defines extension methods to apply binary thresholding on an + /// using Mutate/Clone. /// public static class BinaryThresholdExtensions { @@ -18,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing /// The pixel format. /// The image this method extends. /// The threshold to apply binarization of the image. Must be between 0 and 1. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold) where TPixel : struct, IPixel => source.ApplyProcessor(new BinaryThresholdProcessor(threshold)); @@ -32,7 +33,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold, Rectangle rectangle) where TPixel : struct, IPixel => source.ApplyProcessor(new BinaryThresholdProcessor(threshold), rectangle); @@ -45,7 +46,7 @@ namespace SixLabors.ImageSharp.Processing /// The threshold to apply binarization of the image. Must be between 0 and 1. /// The color to use for pixels that are above the threshold. /// The color to use for pixels that are below the threshold - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold, TPixel upperColor, TPixel lowerColor) where TPixel : struct, IPixel => source.ApplyProcessor(new BinaryThresholdProcessor(threshold, upperColor, lowerColor)); @@ -61,7 +62,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BinaryThreshold(this IImageProcessingContext source, float threshold, TPixel upperColor, TPixel lowerColor, Rectangle rectangle) where TPixel : struct, IPixel => source.ApplyProcessor(new BinaryThresholdProcessor(threshold, upperColor, lowerColor), rectangle); diff --git a/src/ImageSharp/Processing/BlackWhiteExtensions.cs b/src/ImageSharp/Processing/BlackWhiteExtensions.cs index 5dc2341e57..ee34cd99e2 100644 --- a/src/ImageSharp/Processing/BlackWhiteExtensions.cs +++ b/src/ImageSharp/Processing/BlackWhiteExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of black and white toning to the type. + /// Defines extension methods that allow the application of black and white toning to an + /// using Mutate/Clone. /// public static class BlackWhiteExtensions { @@ -16,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing /// Applies black and white toning to the image. /// /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BlackWhite(this IImageProcessingContext source) => source.ApplyProcessor(new BlackWhiteProcessor()); @@ -27,7 +28,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BlackWhite(this IImageProcessingContext source, Rectangle rectangle) => source.ApplyProcessor(new BlackWhiteProcessor(), rectangle); } diff --git a/src/ImageSharp/Processing/BoxBlurExtensions.cs b/src/ImageSharp/Processing/BoxBlurExtensions.cs index bc6e06dd67..f3400c24e6 100644 --- a/src/ImageSharp/Processing/BoxBlurExtensions.cs +++ b/src/ImageSharp/Processing/BoxBlurExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds box blurring extensions to the type. + /// Defines extensions methods to apply box blurring to an + /// using Mutate/Clone. /// public static class BoxBlurExtensions { @@ -16,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing /// Applies a box blur to the image. /// /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BoxBlur(this IImageProcessingContext source) => source.ApplyProcessor(new BoxBlurProcessor()); @@ -25,7 +26,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// The 'radius' value representing the size of the area to sample. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius) => source.ApplyProcessor(new BoxBlurProcessor(radius)); @@ -37,7 +38,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext BoxBlur(this IImageProcessingContext source, int radius, Rectangle rectangle) => source.ApplyProcessor(new BoxBlurProcessor(radius), rectangle); } diff --git a/src/ImageSharp/Processing/BrightnessExtensions.cs b/src/ImageSharp/Processing/BrightnessExtensions.cs index 4de4dc897b..db84091763 100644 --- a/src/ImageSharp/Processing/BrightnessExtensions.cs +++ b/src/ImageSharp/Processing/BrightnessExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the alteration of the brightness component to the type. + /// Defines extensions that allow the alteration of the brightness component of an + /// using Mutate/Clone. /// public static class BrightnessExtensions { @@ -21,7 +22,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// The proportion of the conversion. Must be greater than or equal to 0. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Brightness(this IImageProcessingContext source, float amount) => source.ApplyProcessor(new BrightnessProcessor(amount)); @@ -37,7 +38,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Brightness(this IImageProcessingContext source, float amount, Rectangle rectangle) => source.ApplyProcessor(new BrightnessProcessor(amount), rectangle); } diff --git a/src/ImageSharp/Processing/ColorBlindnessExtensions.cs b/src/ImageSharp/Processing/ColorBlindnessExtensions.cs index 000d47bd3e..b8d503955e 100644 --- a/src/ImageSharp/Processing/ColorBlindnessExtensions.cs +++ b/src/ImageSharp/Processing/ColorBlindnessExtensions.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.ImageSharp.Processing.Processors; using SixLabors.ImageSharp.Processing.Processors.Filters; using SixLabors.Primitives; @@ -9,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that simulate the effects of various color blindness disorders to the type. + /// Defines extensions that simulate the effects of various color blindness disorders on an + /// using Mutate/Clone. /// public static class ColorBlindnessExtensions { @@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// The type of color blindness simulator to apply. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext ColorBlindness(this IImageProcessingContext source, ColorBlindnessMode colorBlindness) => source.ApplyProcessor(GetProcessor(colorBlindness)); @@ -30,7 +30,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext ColorBlindness(this IImageProcessingContext source, ColorBlindnessMode colorBlindnessMode, Rectangle rectangle) => source.ApplyProcessor(GetProcessor(colorBlindnessMode), rectangle); diff --git a/src/ImageSharp/Processing/ContrastExtensions.cs b/src/ImageSharp/Processing/ContrastExtensions.cs index b03844d93b..bdfd7c98a4 100644 --- a/src/ImageSharp/Processing/ContrastExtensions.cs +++ b/src/ImageSharp/Processing/ContrastExtensions.cs @@ -1,14 +1,14 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Processing.Processors.Filters; using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the alteration of the contrast component to the type. + /// Defines extensions that allow the alteration of the contrast component of an + /// using Mutate/Clone. /// public static class ContrastExtensions { @@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// The proportion of the conversion. Must be greater than or equal to 0. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Contrast(this IImageProcessingContext source, float amount) => source.ApplyProcessor(new ContrastProcessor(amount)); @@ -37,7 +37,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Contrast(this IImageProcessingContext source, float amount, Rectangle rectangle) => source.ApplyProcessor(new ContrastProcessor(amount), rectangle); } diff --git a/src/ImageSharp/Processing/CropExtensions.cs b/src/ImageSharp/Processing/CropExtensions.cs index 6aaff5656f..7ec85169e2 100644 --- a/src/ImageSharp/Processing/CropExtensions.cs +++ b/src/ImageSharp/Processing/CropExtensions.cs @@ -7,7 +7,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of cropping operations to the type. + /// Defines extensions that allow the application of cropping operations on an + /// using Mutate/Clone. /// public static class CropExtensions { @@ -17,7 +18,7 @@ namespace SixLabors.ImageSharp.Processing /// The image to resize. /// The target image width. /// The target image height. - /// The + /// The to allow chaining of operations. public static IImageProcessingContext Crop(this IImageProcessingContext source, int width, int height) => Crop(source, new Rectangle(0, 0, width, height)); @@ -28,7 +29,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to retain. /// - /// The + /// The to allow chaining of operations. public static IImageProcessingContext Crop(this IImageProcessingContext source, Rectangle cropRectangle) => source.ApplyProcessor(new CropProcessor(cropRectangle, source.GetCurrentSize())); } diff --git a/src/ImageSharp/Processing/DetectEdgesExtensions.cs b/src/ImageSharp/Processing/DetectEdgesExtensions.cs index 7527d601df..837b26910d 100644 --- a/src/ImageSharp/Processing/DetectEdgesExtensions.cs +++ b/src/ImageSharp/Processing/DetectEdgesExtensions.cs @@ -8,7 +8,7 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds edge detection extensions to the type. + /// Defines edge detection extensions applicable on an using Mutate/Clone. /// public static class DetectEdgesExtensions { @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing /// operating in grayscale mode. /// /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext DetectEdges(this IImageProcessingContext source) => DetectEdges(source, new SobelProcessor(true)); @@ -29,7 +29,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext DetectEdges(this IImageProcessingContext source, Rectangle rectangle) => DetectEdges(source, rectangle, new SobelProcessor(true)); @@ -38,7 +38,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// The filter for detecting edges. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, EdgeDetectionOperators filter) => @@ -50,7 +50,7 @@ namespace SixLabors.ImageSharp.Processing /// The image this method extends. /// The filter for detecting edges. /// Whether to convert the image to grayscale first. Defaults to true. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, EdgeDetectionOperators filter, @@ -66,7 +66,7 @@ namespace SixLabors.ImageSharp.Processing /// The structure that specifies the portion of the image object to alter. /// /// Whether to convert the image to grayscale first. Defaults to true. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext DetectEdges( this IImageProcessingContext source, EdgeDetectionOperators filter, @@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// The filter for detecting edges. - /// The . + /// The to allow chaining of operations. private static IImageProcessingContext DetectEdges(this IImageProcessingContext source, IImageProcessor filter) { return source.ApplyProcessor(filter); @@ -93,7 +93,7 @@ namespace SixLabors.ImageSharp.Processing /// The structure that specifies the portion of the image object to alter. /// /// The filter for detecting edges. - /// The . + /// The to allow chaining of operations. private static IImageProcessingContext DetectEdges( this IImageProcessingContext source, Rectangle rectangle, diff --git a/src/ImageSharp/Processing/DiffuseExtensions.cs b/src/ImageSharp/Processing/DiffuseExtensions.cs index 768d28116b..4668363e9a 100644 --- a/src/ImageSharp/Processing/DiffuseExtensions.cs +++ b/src/ImageSharp/Processing/DiffuseExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Dithering { /// - /// Adds diffusion extensions to the type. + /// Defines extension methods to apply diffusion to an + /// using Mutate/Clone. /// public static class DiffuseExtensions { @@ -17,7 +18,7 @@ namespace SixLabors.ImageSharp.Processing.Dithering /// /// The pixel format. /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Diffuse(this IImageProcessingContext source) where TPixel : struct, IPixel => Diffuse(source, KnownDiffusers.FloydSteinberg, .5F); @@ -28,7 +29,7 @@ namespace SixLabors.ImageSharp.Processing.Dithering /// The pixel format. /// The image this method extends. /// The threshold to apply binarization of the image. Must be between 0 and 1. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Diffuse(this IImageProcessingContext source, float threshold) where TPixel : struct, IPixel => Diffuse(source, KnownDiffusers.FloydSteinberg, threshold); @@ -40,7 +41,7 @@ namespace SixLabors.ImageSharp.Processing.Dithering /// The image this method extends. /// The diffusion algorithm to apply. /// The threshold to apply binarization of the image. Must be between 0 and 1. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Diffuse(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold) where TPixel : struct, IPixel => source.ApplyProcessor(new ErrorDiffusionPaletteProcessor(diffuser, threshold)); @@ -55,7 +56,7 @@ namespace SixLabors.ImageSharp.Processing.Dithering /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Diffuse(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold, Rectangle rectangle) where TPixel : struct, IPixel => source.ApplyProcessor(new ErrorDiffusionPaletteProcessor(diffuser, threshold), rectangle); @@ -68,7 +69,7 @@ namespace SixLabors.ImageSharp.Processing.Dithering /// The diffusion algorithm to apply. /// The threshold to apply binarization of the image. Must be between 0 and 1. /// The palette to select substitute colors from. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Diffuse(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold, TPixel[] palette) where TPixel : struct, IPixel => source.ApplyProcessor(new ErrorDiffusionPaletteProcessor(diffuser, threshold, palette)); @@ -84,7 +85,7 @@ namespace SixLabors.ImageSharp.Processing.Dithering /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Diffuse(this IImageProcessingContext source, IErrorDiffuser diffuser, float threshold, TPixel[] palette, Rectangle rectangle) where TPixel : struct, IPixel => source.ApplyProcessor(new ErrorDiffusionPaletteProcessor(diffuser, threshold, palette), rectangle); diff --git a/src/ImageSharp/Processing/DitherExtensions.cs b/src/ImageSharp/Processing/DitherExtensions.cs index 795561e702..aeb975d1c1 100644 --- a/src/ImageSharp/Processing/DitherExtensions.cs +++ b/src/ImageSharp/Processing/DitherExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds dithering extensions to the type. + /// Defines dithering extensions to apply on an + /// using Mutate/Clone. /// public static class DitherExtensions { @@ -17,7 +18,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The pixel format. /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Dither(this IImageProcessingContext source) where TPixel : struct, IPixel => Dither(source, KnownDitherers.BayerDither4x4); @@ -28,7 +29,7 @@ namespace SixLabors.ImageSharp.Processing /// The pixel format. /// The image this method extends. /// The ordered ditherer. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Dither(this IImageProcessingContext source, IOrderedDither dither) where TPixel : struct, IPixel => source.ApplyProcessor(new OrderedDitherPaletteProcessor(dither)); @@ -40,7 +41,7 @@ namespace SixLabors.ImageSharp.Processing /// The image this method extends. /// The ordered ditherer. /// The palette to select substitute colors from. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Dither(this IImageProcessingContext source, IOrderedDither dither, TPixel[] palette) where TPixel : struct, IPixel => source.ApplyProcessor(new OrderedDitherPaletteProcessor(dither, palette)); @@ -54,7 +55,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Dither(this IImageProcessingContext source, IOrderedDither dither, Rectangle rectangle) where TPixel : struct, IPixel => source.ApplyProcessor(new OrderedDitherPaletteProcessor(dither), rectangle); @@ -69,7 +70,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Dither(this IImageProcessingContext source, IOrderedDither dither, TPixel[] palette, Rectangle rectangle) where TPixel : struct, IPixel => source.ApplyProcessor(new OrderedDitherPaletteProcessor(dither, palette), rectangle); diff --git a/src/ImageSharp/Processing/EntropyCropExtensions.cs b/src/ImageSharp/Processing/EntropyCropExtensions.cs index 34bc5daebf..de5296d83e 100644 --- a/src/ImageSharp/Processing/EntropyCropExtensions.cs +++ b/src/ImageSharp/Processing/EntropyCropExtensions.cs @@ -6,7 +6,8 @@ using SixLabors.ImageSharp.Processing.Processors.Transforms; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of entropy cropping operations to the type. + /// Defines extensions that allow the application of entropy cropping operations on an + /// using Mutate/Clone. /// public static class EntropyCropExtensions { @@ -14,7 +15,7 @@ namespace SixLabors.ImageSharp.Processing /// Crops an image to the area of greatest entropy using a threshold for entropic density of .5F. /// /// The image to crop. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext EntropyCrop(this IImageProcessingContext source) => source.ApplyProcessor(new EntropyCropProcessor()); @@ -23,7 +24,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image to crop. /// The threshold for entropic density. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext EntropyCrop(this IImageProcessingContext source, float threshold) => source.ApplyProcessor(new EntropyCropProcessor(threshold)); } diff --git a/src/ImageSharp/Processing/FilterExtensions.cs b/src/ImageSharp/Processing/FilterExtensions.cs index 2a1c6bc7f9..5a66502ce5 100644 --- a/src/ImageSharp/Processing/FilterExtensions.cs +++ b/src/ImageSharp/Processing/FilterExtensions.cs @@ -9,7 +9,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of composable filters to the type. + /// Defines extensions that allow the application of composable filters to an + /// using Mutate/Clone. /// public static class FilterExtensions { @@ -18,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// The filter color matrix - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Filter(this IImageProcessingContext source, ColorMatrix matrix) => source.ApplyProcessor(new FilterProcessor(matrix)); @@ -30,7 +31,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Filter(this IImageProcessingContext source, ColorMatrix matrix, Rectangle rectangle) => source.ApplyProcessor(new FilterProcessor(matrix), rectangle); } diff --git a/src/ImageSharp/Processing/FlipExtensions.cs b/src/ImageSharp/Processing/FlipExtensions.cs index bc862972e5..f6b3c0c374 100644 --- a/src/ImageSharp/Processing/FlipExtensions.cs +++ b/src/ImageSharp/Processing/FlipExtensions.cs @@ -6,7 +6,8 @@ using SixLabors.ImageSharp.Processing.Processors.Transforms; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of flipping operations to the type. + /// Defines extensions that allow the application of flipping operations on an + /// using Mutate/Clone. /// public static class FlipExtensions { @@ -15,7 +16,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image to rotate, flip, or both. /// The to perform the flip. - /// The + /// The to allow chaining of operations. public static IImageProcessingContext Flip(this IImageProcessingContext source, FlipMode flipMode) => source.ApplyProcessor(new FlipProcessor(flipMode)); } diff --git a/src/ImageSharp/Processing/GaussianBlurExtensions.cs b/src/ImageSharp/Processing/GaussianBlurExtensions.cs index 1dab054579..e527a14b73 100644 --- a/src/ImageSharp/Processing/GaussianBlurExtensions.cs +++ b/src/ImageSharp/Processing/GaussianBlurExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds Gaussian blurring extensions to the type. + /// Defines Gaussian blurring extensions to apply on an + /// using Mutate/Clone. /// public static class GaussianBlurExtensions { @@ -16,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing /// Applies a Gaussian blur to the image. /// /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source) => source.ApplyProcessor(new GaussianBlurProcessor()); @@ -25,7 +26,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// The 'sigma' value representing the weight of the blur. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma) => source.ApplyProcessor(new GaussianBlurProcessor(sigma)); @@ -37,7 +38,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext GaussianBlur(this IImageProcessingContext source, float sigma, Rectangle rectangle) => source.ApplyProcessor(new GaussianBlurProcessor(sigma), rectangle); } diff --git a/src/ImageSharp/Processing/GaussianSharpenExtensions.cs b/src/ImageSharp/Processing/GaussianSharpenExtensions.cs index 64aa7b5db3..79f4a0cc30 100644 --- a/src/ImageSharp/Processing/GaussianSharpenExtensions.cs +++ b/src/ImageSharp/Processing/GaussianSharpenExtensions.cs @@ -7,7 +7,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds Gaussian sharpening extensions to the type. + /// Defines Gaussian sharpening extensions to apply on an + /// using Mutate/Clone. /// public static class GaussianSharpenExtensions { @@ -15,7 +16,7 @@ namespace SixLabors.ImageSharp.Processing /// Applies a Gaussian sharpening filter to the image. /// /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source) => source.ApplyProcessor(new GaussianSharpenProcessor()); @@ -24,7 +25,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// The 'sigma' value representing the weight of the blur. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext GaussianSharpen(this IImageProcessingContext source, float sigma) => source.ApplyProcessor(new GaussianSharpenProcessor(sigma)); @@ -36,7 +37,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext GaussianSharpen( this IImageProcessingContext source, float sigma, diff --git a/src/ImageSharp/Processing/GlowExtensions.cs b/src/ImageSharp/Processing/GlowExtensions.cs index 8b6e8ffc22..759fdccbea 100644 --- a/src/ImageSharp/Processing/GlowExtensions.cs +++ b/src/ImageSharp/Processing/GlowExtensions.cs @@ -9,7 +9,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of a radial glow to the type. + /// Defines extensions that allow the application of a radial glow on an + /// using Mutate/Clone. /// public static class GlowExtensions { @@ -18,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The pixel format. /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Glow(this IImageProcessingContext source) where TPixel : struct, IPixel => Glow(source, GraphicsOptions.Default); @@ -29,7 +30,7 @@ namespace SixLabors.ImageSharp.Processing /// The pixel format. /// The image this method extends. /// The color to set as the glow. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Glow(this IImageProcessingContext source, TPixel color) where TPixel : struct, IPixel { @@ -42,7 +43,7 @@ namespace SixLabors.ImageSharp.Processing /// The pixel format. /// The image this method extends. /// The the radius. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Glow(this IImageProcessingContext source, float radius) where TPixel : struct, IPixel => Glow(source, GraphicsOptions.Default, radius); @@ -55,7 +56,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Glow(this IImageProcessingContext source, Rectangle rectangle) where TPixel : struct, IPixel => source.Glow(GraphicsOptions.Default, rectangle); @@ -70,7 +71,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Glow(this IImageProcessingContext source, TPixel color, float radius, Rectangle rectangle) where TPixel : struct, IPixel => source.Glow(GraphicsOptions.Default, color, ValueSize.Absolute(radius), rectangle); @@ -81,7 +82,7 @@ namespace SixLabors.ImageSharp.Processing /// The pixel format. /// The image this method extends. /// The options effecting things like blending. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options) where TPixel : struct, IPixel => source.Glow(options, NamedColors.Black, ValueSize.PercentageOfWidth(0.5f)); @@ -93,7 +94,7 @@ namespace SixLabors.ImageSharp.Processing /// The image this method extends. /// The options effecting things like blending. /// The color to set as the glow. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options, TPixel color) where TPixel : struct, IPixel => source.Glow(options, color, ValueSize.PercentageOfWidth(0.5f)); @@ -105,7 +106,7 @@ namespace SixLabors.ImageSharp.Processing /// The image this method extends. /// The options effecting things like blending. /// The the radius. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options, float radius) where TPixel : struct, IPixel => source.Glow(options, NamedColors.Black, ValueSize.Absolute(radius)); @@ -119,7 +120,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options, Rectangle rectangle) where TPixel : struct, IPixel => source.Glow(options, NamedColors.Black, ValueSize.PercentageOfWidth(0.5f), rectangle); @@ -135,7 +136,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options, TPixel color, float radius, Rectangle rectangle) where TPixel : struct, IPixel => source.Glow(options, color, ValueSize.Absolute(radius), rectangle); @@ -151,7 +152,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. private static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options, TPixel color, ValueSize radius, Rectangle rectangle) where TPixel : struct, IPixel => source.ApplyProcessor(new GlowProcessor(color, radius, options), rectangle); @@ -164,7 +165,7 @@ namespace SixLabors.ImageSharp.Processing /// The options effecting things like blending. /// The color to set as the glow. /// The the radius. - /// The . + /// The to allow chaining of operations. private static IImageProcessingContext Glow(this IImageProcessingContext source, GraphicsOptions options, TPixel color, ValueSize radius) where TPixel : struct, IPixel => source.ApplyProcessor(new GlowProcessor(color, radius, options)); diff --git a/src/ImageSharp/Processing/GrayscaleExtensions.cs b/src/ImageSharp/Processing/GrayscaleExtensions.cs index d68a385e47..a87341025d 100644 --- a/src/ImageSharp/Processing/GrayscaleExtensions.cs +++ b/src/ImageSharp/Processing/GrayscaleExtensions.cs @@ -9,7 +9,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of grayscale toning to the type. + /// Defines extensions that allow the application of grayscale toning to an + /// using Mutate/Clone. /// public static class GrayscaleExtensions { diff --git a/src/ImageSharp/Processing/HistogramEqualizationExtension.cs b/src/ImageSharp/Processing/HistogramEqualizationExtension.cs index aae3e997fe..01c14fc093 100644 --- a/src/ImageSharp/Processing/HistogramEqualizationExtension.cs +++ b/src/ImageSharp/Processing/HistogramEqualizationExtension.cs @@ -6,7 +6,7 @@ using SixLabors.ImageSharp.Processing.Processors.Normalization; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extension that allow the adjustment of the contrast of an image via its histogram. + /// Defines extension that allow the adjustment of the contrast of an image via its histogram. /// public static class HistogramEqualizationExtension { @@ -14,7 +14,7 @@ namespace SixLabors.ImageSharp.Processing /// Equalizes the histogram of an image to increases the contrast. /// /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext HistogramEqualization(this IImageProcessingContext source) => HistogramEqualization(source, HistogramEqualizationOptions.Default); @@ -23,7 +23,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// The histogram equalization options to use. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext HistogramEqualization( this IImageProcessingContext source, HistogramEqualizationOptions options) => diff --git a/src/ImageSharp/Processing/HueExtensions.cs b/src/ImageSharp/Processing/HueExtensions.cs index 17296e1db5..3c1239da67 100644 --- a/src/ImageSharp/Processing/HueExtensions.cs +++ b/src/ImageSharp/Processing/HueExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the alteration of the hue component to the type. + /// Defines extensions that allow the alteration of the hue component of an + /// using Mutate/Clone. /// public static class HueExtensions { @@ -17,7 +18,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// The rotation angle in degrees to adjust the hue. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Hue(this IImageProcessingContext source, float degrees) => source.ApplyProcessor(new HueProcessor(degrees)); @@ -29,7 +30,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Hue(this IImageProcessingContext source, float degrees, Rectangle rectangle) => source.ApplyProcessor(new HueProcessor(degrees), rectangle); } diff --git a/src/ImageSharp/Processing/InvertExtensions.cs b/src/ImageSharp/Processing/InvertExtensions.cs index 828aaa1bbc..c45f24c2ea 100644 --- a/src/ImageSharp/Processing/InvertExtensions.cs +++ b/src/ImageSharp/Processing/InvertExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the inversion of colors to the type. + /// Defines extensions that allow the inversion of colors of an + /// using Mutate/Clone. /// public static class InvertExtensions { @@ -16,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing /// Inverts the colors of the image. /// /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Invert(this IImageProcessingContext source) => source.ApplyProcessor(new InvertProcessor(1F)); @@ -27,7 +28,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Invert(this IImageProcessingContext source, Rectangle rectangle) => source.ApplyProcessor(new InvertProcessor(1F), rectangle); } diff --git a/src/ImageSharp/Processing/KodachromeExtensions.cs b/src/ImageSharp/Processing/KodachromeExtensions.cs index 24bd51e016..810094a180 100644 --- a/src/ImageSharp/Processing/KodachromeExtensions.cs +++ b/src/ImageSharp/Processing/KodachromeExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the recreation of an old Kodachrome camera effect to the type. + /// Defines extensions that allow the recreation of an old Kodachrome camera effect on an + /// using Mutate/Clone. /// public static class KodachromeExtensions { @@ -16,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing /// Alters the colors of the image recreating an old Kodachrome camera effect. /// /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Kodachrome(this IImageProcessingContext source) => source.ApplyProcessor(new KodachromeProcessor()); @@ -27,7 +28,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Kodachrome(this IImageProcessingContext source, Rectangle rectangle) => source.ApplyProcessor(new KodachromeProcessor(), rectangle); } diff --git a/src/ImageSharp/Processing/LomographExtensions.cs b/src/ImageSharp/Processing/LomographExtensions.cs index a3fd3d3b50..dd7ab21ec1 100644 --- a/src/ImageSharp/Processing/LomographExtensions.cs +++ b/src/ImageSharp/Processing/LomographExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the recreation of an old Lomograph camera effect to the type. + /// Defines extensions that allow the recreation of an old Lomograph camera effect on an + /// using Mutate/Clone. /// public static class LomographExtensions { @@ -16,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing /// Alters the colors of the image recreating an old Lomograph camera effect. /// /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Lomograph(this IImageProcessingContext source) => source.ApplyProcessor(new LomographProcessor()); @@ -27,7 +28,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Lomograph(this IImageProcessingContext source, Rectangle rectangle) => source.ApplyProcessor(new LomographProcessor(), rectangle); } diff --git a/src/ImageSharp/Processing/OilPaintExtensions.cs b/src/ImageSharp/Processing/OilPaintExtensions.cs index ee51b31fab..1aa98c8c1b 100644 --- a/src/ImageSharp/Processing/OilPaintExtensions.cs +++ b/src/ImageSharp/Processing/OilPaintExtensions.cs @@ -7,7 +7,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds oil painting effect extensions to the type. + /// Defines oil painting effect extensions applicable on an + /// using Mutate/Clone. /// public static class OilPaintExtensions { @@ -16,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing /// set to 10 and 15 respectively. /// /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext OilPaint(this IImageProcessingContext source) => OilPaint(source, 10, 15); /// @@ -27,7 +28,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext OilPaint(this IImageProcessingContext source, Rectangle rectangle) => OilPaint(source, 10, 15, rectangle); @@ -37,7 +38,7 @@ namespace SixLabors.ImageSharp.Processing /// The image this method extends. /// The number of intensity levels. Higher values result in a broader range of color intensities forming part of the result image. /// The number of neighboring pixels used in calculating each individual pixel value. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext OilPaint(this IImageProcessingContext source, int levels, int brushSize) => source.ApplyProcessor(new OilPaintingProcessor(levels, brushSize)); @@ -51,7 +52,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext OilPaint( this IImageProcessingContext source, int levels, diff --git a/src/ImageSharp/Processing/OpacityExtensions.cs b/src/ImageSharp/Processing/OpacityExtensions.cs index 6d9198648c..ecf6ce783e 100644 --- a/src/ImageSharp/Processing/OpacityExtensions.cs +++ b/src/ImageSharp/Processing/OpacityExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the alteration of the opacity component to the type. + /// Defines extensions that allow the alteration of the opacity component of an + /// using Mutate/Clone. /// public static class OpacityExtensions { @@ -17,7 +18,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// The proportion of the conversion. Must be between 0 and 1. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Opacity(this IImageProcessingContext source, float amount) => source.ApplyProcessor(new OpacityProcessor(amount)); @@ -29,7 +30,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Opacity(this IImageProcessingContext source, float amount, Rectangle rectangle) => source.ApplyProcessor(new OpacityProcessor(amount), rectangle); } diff --git a/src/ImageSharp/Processing/PadExtensions.cs b/src/ImageSharp/Processing/PadExtensions.cs index 0422f7c59d..29f80b43ff 100644 --- a/src/ImageSharp/Processing/PadExtensions.cs +++ b/src/ImageSharp/Processing/PadExtensions.cs @@ -7,7 +7,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of padding operations to the type. + /// Defines extensions that allow the application of padding operations on an + /// using Mutate/Clone. /// public static class PadExtensions { @@ -17,7 +18,7 @@ namespace SixLabors.ImageSharp.Processing /// The source image to pad. /// The new width. /// The new height. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Pad(this IImageProcessingContext source, int width, int height) { var options = new ResizeOptions diff --git a/src/ImageSharp/Processing/PixelateExtensions.cs b/src/ImageSharp/Processing/PixelateExtensions.cs index d9f7f9be50..bf40af91ad 100644 --- a/src/ImageSharp/Processing/PixelateExtensions.cs +++ b/src/ImageSharp/Processing/PixelateExtensions.cs @@ -7,7 +7,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds pixelation effect extensions to the type. + /// Defines pixelation effect extensions applicable on an + /// using Mutate/Clone. /// public static class PixelateExtensions { @@ -15,7 +16,7 @@ namespace SixLabors.ImageSharp.Processing /// Pixelates an image with the given pixel size. /// /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Pixelate(this IImageProcessingContext source) => Pixelate(source, 4); /// @@ -23,7 +24,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// The size of the pixels. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Pixelate(this IImageProcessingContext source, int size) => source.ApplyProcessor(new PixelateProcessor(size)); @@ -35,7 +36,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Pixelate( this IImageProcessingContext source, int size, diff --git a/src/ImageSharp/Processing/PolaroidExtensions.cs b/src/ImageSharp/Processing/PolaroidExtensions.cs index 4e78411689..eace463579 100644 --- a/src/ImageSharp/Processing/PolaroidExtensions.cs +++ b/src/ImageSharp/Processing/PolaroidExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the recreation of an old Polaroid camera effect to the type. + /// Defines extensions that allow the recreation of an old Polaroid camera effect on an + /// using Mutate/Clone. /// public static class PolaroidExtensions { @@ -16,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing /// Alters the colors of the image recreating an old Polaroid camera effect. /// /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Polaroid(this IImageProcessingContext source) => source.ApplyProcessor(new PolaroidProcessor()); @@ -27,7 +28,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Polaroid(this IImageProcessingContext source, Rectangle rectangle) => source.ApplyProcessor(new PolaroidProcessor(), rectangle); } diff --git a/src/ImageSharp/Processing/QuantizeExtensions.cs b/src/ImageSharp/Processing/QuantizeExtensions.cs index 5bd2f49bd4..ad351afa99 100644 --- a/src/ImageSharp/Processing/QuantizeExtensions.cs +++ b/src/ImageSharp/Processing/QuantizeExtensions.cs @@ -7,7 +7,8 @@ using SixLabors.ImageSharp.Processing.Processors.Quantization; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of quantizing algorithms to the type. + /// Defines extensions that allow the application of quantizing algorithms on an + /// using Mutate/Clone. /// public static class QuantizeExtensions { @@ -16,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The pixel format. /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Quantize(this IImageProcessingContext source) where TPixel : struct, IPixel => Quantize(source, KnownQuantizers.Octree); @@ -27,7 +28,7 @@ namespace SixLabors.ImageSharp.Processing /// The pixel format. /// The image this method extends. /// The quantizer to apply to perform the operation. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Quantize(this IImageProcessingContext source, IQuantizer quantizer) where TPixel : struct, IPixel => source.ApplyProcessor(new QuantizeProcessor(quantizer)); diff --git a/src/ImageSharp/Processing/ResizeExtensions.cs b/src/ImageSharp/Processing/ResizeExtensions.cs index 57dbcb1d16..4578b4353f 100644 --- a/src/ImageSharp/Processing/ResizeExtensions.cs +++ b/src/ImageSharp/Processing/ResizeExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of resize operations to the type. + /// Defines extensions that allow the application of resize operations on an + /// using Mutate/Clone. /// public static class ResizeExtensions { @@ -17,7 +18,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image to resize. /// The resize options. - /// The + /// The to allow chaining of operations. /// Passing zero for one of height or width within the resize options will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize(this IImageProcessingContext source, ResizeOptions options) => source.ApplyProcessor(new ResizeProcessor(options, source.GetCurrentSize())); @@ -27,7 +28,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image to resize. /// The target image size. - /// The + /// The to allow chaining of operations. /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size) => Resize(source, size.Width, size.Height, KnownResamplers.Bicubic, false); @@ -38,7 +39,7 @@ namespace SixLabors.ImageSharp.Processing /// The image to resize. /// The target image size. /// Whether to compress and expand the image color-space to gamma correct the image during processing. - /// The + /// The to allow chaining of operations. /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size, bool compand) => Resize(source, size.Width, size.Height, KnownResamplers.Bicubic, compand); @@ -49,7 +50,7 @@ namespace SixLabors.ImageSharp.Processing /// The image to resize. /// The target image width. /// The target image height. - /// The + /// The to allow chaining of operations. /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height) => Resize(source, width, height, KnownResamplers.Bicubic, false); @@ -61,7 +62,7 @@ namespace SixLabors.ImageSharp.Processing /// The target image width. /// The target image height. /// Whether to compress and expand the image color-space to gamma correct the image during processing. - /// The + /// The to allow chaining of operations. /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, bool compand) => Resize(source, width, height, KnownResamplers.Bicubic, compand); @@ -73,7 +74,7 @@ namespace SixLabors.ImageSharp.Processing /// The target image width. /// The target image height. /// The to perform the resampling. - /// The + /// The to allow chaining of operations. /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, IResampler sampler) => Resize(source, width, height, sampler, false); @@ -85,7 +86,7 @@ namespace SixLabors.ImageSharp.Processing /// The target image size. /// The to perform the resampling. /// Whether to compress and expand the image color-space to gamma correct the image during processing. - /// The + /// The to allow chaining of operations. /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize(this IImageProcessingContext source, Size size, IResampler sampler, bool compand) => Resize(source, size.Width, size.Height, sampler, new Rectangle(0, 0, size.Width, size.Height), compand); @@ -98,7 +99,7 @@ namespace SixLabors.ImageSharp.Processing /// The target image height. /// The to perform the resampling. /// Whether to compress and expand the image color-space to gamma correct the image during processing. - /// The + /// The to allow chaining of operations. /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize(this IImageProcessingContext source, int width, int height, IResampler sampler, bool compand) => Resize(source, width, height, sampler, new Rectangle(0, 0, width, height), compand); @@ -118,7 +119,7 @@ namespace SixLabors.ImageSharp.Processing /// The structure that specifies the portion of the target image object to draw to. /// /// Whether to compress and expand the image color-space to gamma correct the image during processing. - /// The + /// The to allow chaining of operations. /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize( this IImageProcessingContext source, @@ -141,7 +142,7 @@ namespace SixLabors.ImageSharp.Processing /// The structure that specifies the portion of the target image object to draw to. /// /// Whether to compress and expand the image color-space to gamma correct the image during processing. - /// The + /// The to allow chaining of operations. /// Passing zero for one of height or width will automatically preserve the aspect ratio of the original image or the nearest possible ratio. public static IImageProcessingContext Resize( this IImageProcessingContext source, diff --git a/src/ImageSharp/Processing/RotateExtensions.cs b/src/ImageSharp/Processing/RotateExtensions.cs index cb637a1b84..395462ae33 100644 --- a/src/ImageSharp/Processing/RotateExtensions.cs +++ b/src/ImageSharp/Processing/RotateExtensions.cs @@ -6,7 +6,8 @@ using SixLabors.ImageSharp.Processing.Processors.Transforms; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of rotate operations to the type. + /// Defines extensions that allow the application of rotate operations on an + /// using Mutate/Clone. /// public static class RotateExtensions { @@ -15,7 +16,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image to rotate. /// The to perform the rotation. - /// The + /// The to allow chaining of operations. public static IImageProcessingContext Rotate(this IImageProcessingContext source, RotateMode rotateMode) => Rotate(source, (float)rotateMode); @@ -24,7 +25,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image to rotate. /// The angle in degrees to perform the rotation. - /// The + /// The to allow chaining of operations. public static IImageProcessingContext Rotate(this IImageProcessingContext source, float degrees) => Rotate(source, degrees, KnownResamplers.Bicubic); @@ -34,7 +35,7 @@ namespace SixLabors.ImageSharp.Processing /// The image to rotate. /// The angle in degrees to perform the rotation. /// The to perform the resampling. - /// The + /// The to allow chaining of operations. public static IImageProcessingContext Rotate( this IImageProcessingContext source, float degrees, diff --git a/src/ImageSharp/Processing/RotateFlipExtensions.cs b/src/ImageSharp/Processing/RotateFlipExtensions.cs index 5030c942ef..4d5d90c30e 100644 --- a/src/ImageSharp/Processing/RotateFlipExtensions.cs +++ b/src/ImageSharp/Processing/RotateFlipExtensions.cs @@ -6,7 +6,8 @@ using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of rotate-flip operations to the type. + /// Defines extensions that allow the application of rotate-flip operations on an + /// using Mutate/Clone. /// public static class RotateFlipExtensions { @@ -16,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing /// The image to rotate, flip, or both. /// The to perform the rotation. /// The to perform the flip. - /// The + /// The to allow chaining of operations. public static IImageProcessingContext RotateFlip(this IImageProcessingContext source, RotateMode rotateMode, FlipMode flipMode) => source.Rotate(rotateMode).Flip(flipMode); } diff --git a/src/ImageSharp/Processing/SaturateExtensions.cs b/src/ImageSharp/Processing/SaturateExtensions.cs index fe2cf5f348..e9ba820b6c 100644 --- a/src/ImageSharp/Processing/SaturateExtensions.cs +++ b/src/ImageSharp/Processing/SaturateExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the alteration of the saturation component to the type. + /// Defines extensions that allow the alteration of the saturation component of an + /// using Mutate/Clone. /// public static class SaturateExtensions { @@ -21,7 +22,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// The proportion of the conversion. Must be greater than or equal to 0. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Saturate(this IImageProcessingContext source, float amount) => source.ApplyProcessor(new SaturateProcessor(amount)); @@ -37,7 +38,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Saturate(this IImageProcessingContext source, float amount, Rectangle rectangle) => source.ApplyProcessor(new SaturateProcessor(amount), rectangle); } diff --git a/src/ImageSharp/Processing/SepiaExtensions.cs b/src/ImageSharp/Processing/SepiaExtensions.cs index 5b23ec11af..5ee5151fae 100644 --- a/src/ImageSharp/Processing/SepiaExtensions.cs +++ b/src/ImageSharp/Processing/SepiaExtensions.cs @@ -8,7 +8,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of sepia toning to the type. + /// Defines extensions that allow the application of sepia toning on an + /// using Mutate/Clone. /// public static class SepiaExtensions { @@ -16,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing /// Applies sepia toning to the image. /// /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Sepia(this IImageProcessingContext source) => Sepia(source, 1F); @@ -25,7 +26,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image this method extends. /// The proportion of the conversion. Must be between 0 and 1. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Sepia(this IImageProcessingContext source, float amount) => source.ApplyProcessor(new SepiaProcessor(amount)); @@ -36,7 +37,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Sepia(this IImageProcessingContext source, Rectangle rectangle) => Sepia(source, 1F, rectangle); @@ -48,7 +49,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Sepia(this IImageProcessingContext source, float amount, Rectangle rectangle) => source.ApplyProcessor(new SepiaProcessor(amount), rectangle); } diff --git a/src/ImageSharp/Processing/SkewExtensions.cs b/src/ImageSharp/Processing/SkewExtensions.cs index 7f378d2483..77a46af0d9 100644 --- a/src/ImageSharp/Processing/SkewExtensions.cs +++ b/src/ImageSharp/Processing/SkewExtensions.cs @@ -6,7 +6,8 @@ using SixLabors.ImageSharp.Processing.Processors.Transforms; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of skew operations to the type. + /// Defines extensions that allow the application of skew operations on an + /// using Mutate/Clone. /// public static class SkewExtensions { @@ -16,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing /// The image to skew. /// The angle in degrees to perform the skew along the x-axis. /// The angle in degrees to perform the skew along the y-axis. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Skew(this IImageProcessingContext source, float degreesX, float degreesY) => Skew(source, degreesX, degreesY, KnownResamplers.Bicubic); @@ -28,7 +29,7 @@ namespace SixLabors.ImageSharp.Processing /// The angle in degrees to perform the skew along the x-axis. /// The angle in degrees to perform the skew along the y-axis. /// The to perform the resampling. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Skew( this IImageProcessingContext source, float degreesX, diff --git a/src/ImageSharp/Processing/TransformExtensions.cs b/src/ImageSharp/Processing/TransformExtensions.cs index 35f374d013..7fffb71d20 100644 --- a/src/ImageSharp/Processing/TransformExtensions.cs +++ b/src/ImageSharp/Processing/TransformExtensions.cs @@ -9,7 +9,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of composable transform operations to the type. + /// Defines extensions that allow the application of composable transform operations on an + /// using Mutate/Clone. /// public static class TransformExtensions { @@ -30,7 +31,7 @@ namespace SixLabors.ImageSharp.Processing /// The . /// The affine transform builder. /// The to perform the resampling. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Transform( this IImageProcessingContext ctx, AffineTransformBuilder builder, @@ -44,7 +45,7 @@ namespace SixLabors.ImageSharp.Processing /// The source rectangle /// The affine transform builder. /// The to perform the resampling. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Transform( this IImageProcessingContext ctx, Rectangle sourceRectangle, @@ -64,7 +65,7 @@ namespace SixLabors.ImageSharp.Processing /// The transformation matrix. /// The size of the result image. /// The to perform the resampling. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Transform( this IImageProcessingContext ctx, Rectangle sourceRectangle, @@ -82,7 +83,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The image to transform. /// The affine transform builder. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Transform( this IImageProcessingContext source, ProjectiveTransformBuilder builder) => @@ -94,7 +95,7 @@ namespace SixLabors.ImageSharp.Processing /// The . /// The projective transform builder. /// The to perform the resampling. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Transform( this IImageProcessingContext ctx, ProjectiveTransformBuilder builder, @@ -108,7 +109,7 @@ namespace SixLabors.ImageSharp.Processing /// The source rectangle /// The projective transform builder. /// The to perform the resampling. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Transform( this IImageProcessingContext ctx, Rectangle sourceRectangle, @@ -128,7 +129,7 @@ namespace SixLabors.ImageSharp.Processing /// The transformation matrix. /// The size of the result image. /// The to perform the resampling. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Transform( this IImageProcessingContext ctx, Rectangle sourceRectangle, diff --git a/src/ImageSharp/Processing/VignetteExtensions.cs b/src/ImageSharp/Processing/VignetteExtensions.cs index 18dd8064c6..63cdee3f83 100644 --- a/src/ImageSharp/Processing/VignetteExtensions.cs +++ b/src/ImageSharp/Processing/VignetteExtensions.cs @@ -9,7 +9,8 @@ using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing { /// - /// Adds extensions that allow the application of a radial glow to the type. + /// Defines extensions that allow the application of a radial glow to an + /// using Mutate/Clone. /// public static class VignetteExtensions { @@ -18,7 +19,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The pixel format. /// The image this method extends. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Vignette(this IImageProcessingContext source) where TPixel : struct, IPixel => Vignette(source, GraphicsOptions.Default); @@ -29,7 +30,7 @@ namespace SixLabors.ImageSharp.Processing /// The pixel format. /// The image this method extends. /// The color to set as the vignette. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Vignette(this IImageProcessingContext source, TPixel color) where TPixel : struct, IPixel => Vignette(source, GraphicsOptions.Default, color); @@ -41,7 +42,7 @@ namespace SixLabors.ImageSharp.Processing /// The image this method extends. /// The the x-radius. /// The the y-radius. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Vignette(this IImageProcessingContext source, float radiusX, float radiusY) where TPixel : struct, IPixel => Vignette(source, GraphicsOptions.Default, radiusX, radiusY); @@ -54,7 +55,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Vignette(this IImageProcessingContext source, Rectangle rectangle) where TPixel : struct, IPixel => Vignette(source, GraphicsOptions.Default, rectangle); @@ -70,7 +71,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Vignette(this IImageProcessingContext source, TPixel color, float radiusX, float radiusY, Rectangle rectangle) where TPixel : struct, IPixel => source.Vignette(GraphicsOptions.Default, color, radiusX, radiusY, rectangle); @@ -81,7 +82,7 @@ namespace SixLabors.ImageSharp.Processing /// The pixel format. /// The image this method extends. /// The options effecting pixel blending. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Vignette(this IImageProcessingContext source, GraphicsOptions options) where TPixel : struct, IPixel => source.VignetteInternal(options, NamedColors.Black, ValueSize.PercentageOfWidth(.5f), ValueSize.PercentageOfHeight(.5f)); @@ -93,7 +94,7 @@ namespace SixLabors.ImageSharp.Processing /// The image this method extends. /// The options effecting pixel blending. /// The color to set as the vignette. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Vignette(this IImageProcessingContext source, GraphicsOptions options, TPixel color) where TPixel : struct, IPixel => source.VignetteInternal(options, color, ValueSize.PercentageOfWidth(.5f), ValueSize.PercentageOfHeight(.5f)); @@ -106,7 +107,7 @@ namespace SixLabors.ImageSharp.Processing /// The options effecting pixel blending. /// The the x-radius. /// The the y-radius. - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Vignette(this IImageProcessingContext source, GraphicsOptions options, float radiusX, float radiusY) where TPixel : struct, IPixel => source.VignetteInternal(options, NamedColors.Black, radiusX, radiusY); @@ -120,7 +121,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Vignette(this IImageProcessingContext source, GraphicsOptions options, Rectangle rectangle) where TPixel : struct, IPixel => source.VignetteInternal(options, NamedColors.Black, ValueSize.PercentageOfWidth(.5f), ValueSize.PercentageOfHeight(.5f), rectangle); @@ -137,7 +138,7 @@ namespace SixLabors.ImageSharp.Processing /// /// The structure that specifies the portion of the image object to alter. /// - /// The . + /// The to allow chaining of operations. public static IImageProcessingContext Vignette(this IImageProcessingContext source, GraphicsOptions options, TPixel color, float radiusX, float radiusY, Rectangle rectangle) where TPixel : struct, IPixel => source.VignetteInternal(options, color, radiusX, radiusY, rectangle); From fefb0e66a467ff5ade373802afc3a5b7ddbbbc21 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 May 2019 22:31:06 +0200 Subject: [PATCH 28/31] publish non-generic QuantizeProcessor --- ImageSharp.sln.DotSettings | 1 + .../Quantization/QuantizeProcessor.cs | 41 +++---------- .../Quantization/QuantizeProcessor{TPixel}.cs | 57 +++++++++++++++++++ .../Processing/QuantizeExtensions.cs | 13 ++--- 4 files changed, 70 insertions(+), 42 deletions(-) create mode 100644 src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs diff --git a/ImageSharp.sln.DotSettings b/ImageSharp.sln.DotSettings index 8e7b5dd488..5268172429 100644 --- a/ImageSharp.sln.DotSettings +++ b/ImageSharp.sln.DotSettings @@ -388,4 +388,5 @@ True True True + True \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor.cs index e99f504b42..0aee0b4831 100644 --- a/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor.cs @@ -1,59 +1,34 @@ // Copyright (c) Six Labors and contributors. // Licensed under the Apache License, Version 2.0. -using System; -using SixLabors.ImageSharp.Advanced; using SixLabors.ImageSharp.PixelFormats; -using SixLabors.Primitives; namespace SixLabors.ImageSharp.Processing.Processors.Quantization { /// - /// Enables the quantization of images to reduce the number of colors used in the image palette. + /// Defines quantization processing for images to reduce the number of colors used in the image palette. /// - /// The pixel format. - internal class QuantizeProcessor : ImageProcessor - where TPixel : struct, IPixel + public class QuantizeProcessor : IImageProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// - /// The quantizer used to reduce the color palette + /// The quantizer used to reduce the color palette. public QuantizeProcessor(IQuantizer quantizer) { - Guard.NotNull(quantizer, nameof(quantizer)); this.Quantizer = quantizer; } /// - /// Gets the quantizer + /// Gets the quantizer. /// public IQuantizer Quantizer { get; } /// - protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + public IImageProcessor CreatePixelSpecificProcessor() + where TPixel : struct, IPixel { - using (IFrameQuantizer executor = this.Quantizer.CreateFrameQuantizer(configuration)) - using (QuantizedFrame quantized = executor.QuantizeFrame(source)) - { - int paletteCount = quantized.Palette.Length - 1; - - // Not parallel to remove "quantized" closure allocation. - // We can operate directly on the source here as we've already read it to get the - // quantized result - for (int y = 0; y < source.Height; y++) - { - Span row = source.GetPixelRowSpan(y); - ReadOnlySpan quantizedPixelSpan = quantized.GetPixelSpan(); - int yy = y * source.Width; - - for (int x = 0; x < source.Width; x++) - { - int i = x + yy; - row[x] = quantized.Palette[Math.Min(paletteCount, quantizedPixelSpan[i])]; - } - } - } + return new QuantizeProcessor(this.Quantizer); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs new file mode 100644 index 0000000000..b52343a2ae --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Quantization/QuantizeProcessor{TPixel}.cs @@ -0,0 +1,57 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +using System; + +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.Primitives; + +namespace SixLabors.ImageSharp.Processing.Processors.Quantization +{ + /// + /// Enables the quantization of images to reduce the number of colors used in the image palette. + /// + /// The pixel format. + internal class QuantizeProcessor : ImageProcessor + where TPixel : struct, IPixel + { + private readonly IQuantizer quantizer; + + /// + /// Initializes a new instance of the class. + /// + /// The quantizer used to reduce the color palette. + public QuantizeProcessor(IQuantizer quantizer) + { + Guard.NotNull(quantizer, nameof(quantizer)); + this.quantizer = quantizer; + } + + /// + protected override void OnFrameApply(ImageFrame source, Rectangle sourceRectangle, Configuration configuration) + { + using (IFrameQuantizer executor = this.quantizer.CreateFrameQuantizer(configuration)) + using (QuantizedFrame quantized = executor.QuantizeFrame(source)) + { + int paletteCount = quantized.Palette.Length - 1; + + // Not parallel to remove "quantized" closure allocation. + // We can operate directly on the source here as we've already read it to get the + // quantized result + for (int y = 0; y < source.Height; y++) + { + Span row = source.GetPixelRowSpan(y); + ReadOnlySpan quantizedPixelSpan = quantized.GetPixelSpan(); + int yy = y * source.Width; + + for (int x = 0; x < source.Width; x++) + { + int i = x + yy; + row[x] = quantized.Palette[Math.Min(paletteCount, quantizedPixelSpan[i])]; + } + } + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/QuantizeExtensions.cs b/src/ImageSharp/Processing/QuantizeExtensions.cs index ad351afa99..3410ee6bec 100644 --- a/src/ImageSharp/Processing/QuantizeExtensions.cs +++ b/src/ImageSharp/Processing/QuantizeExtensions.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.ImageSharp.Processing.Processors.Quantization; namespace SixLabors.ImageSharp.Processing @@ -15,22 +14,18 @@ namespace SixLabors.ImageSharp.Processing /// /// Applies quantization to the image using the . /// - /// The pixel format. /// The image this method extends. /// The to allow chaining of operations. - public static IImageProcessingContext Quantize(this IImageProcessingContext source) - where TPixel : struct, IPixel - => Quantize(source, KnownQuantizers.Octree); + public static IImageProcessingContext Quantize(this IImageProcessingContext source) => + Quantize(source, KnownQuantizers.Octree); /// /// Applies quantization to the image. /// - /// The pixel format. /// The image this method extends. /// The quantizer to apply to perform the operation. /// The to allow chaining of operations. - public static IImageProcessingContext Quantize(this IImageProcessingContext source, IQuantizer quantizer) - where TPixel : struct, IPixel - => source.ApplyProcessor(new QuantizeProcessor(quantizer)); + public static IImageProcessingContext Quantize(this IImageProcessingContext source, IQuantizer quantizer) => + source.ApplyProcessor(new QuantizeProcessor(quantizer)); } } \ No newline at end of file From a7720e53cbe9570fc56e251a44aa96b574a8d34c Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 9 May 2019 22:18:18 +0200 Subject: [PATCH 29/31] verbose naming for Histogram Equalization stuff + make it public --- ... AdaptiveHistogramEqualizationProcessor.cs} | 18 +++++++++++++----- ...eHistogramEqualizationProcessor{TPixel}.cs} | 6 +++--- ...ogramEqualizationSlidingWindowProcessor.cs} | 16 ++++++++++++---- ...alizationSlidingWindowProcessor{TPixel}.cs} | 6 +++--- .../GlobalHistogramEqualizationProcessor.cs | 8 +++++++- .../HistogramEqualizationProcessor.cs | 6 +++--- 6 files changed, 41 insertions(+), 19 deletions(-) rename src/ImageSharp/Processing/Processors/Normalization/{AdaptiveHistEqualizationProcessor.cs => AdaptiveHistogramEqualizationProcessor.cs} (76%) rename src/ImageSharp/Processing/Processors/Normalization/{AdaptiveHistEqualizationProcessor{TPixel}.cs => AdaptiveHistogramEqualizationProcessor{TPixel}.cs} (98%) rename src/ImageSharp/Processing/Processors/Normalization/{AdaptiveHistEqualizationSWProcessor.cs => AdaptiveHistogramEqualizationSlidingWindowProcessor.cs} (72%) rename src/ImageSharp/Processing/Processors/Normalization/{AdaptiveHistEqualizationSWProcessor{TPixel}.cs => AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs} (98%) diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs similarity index 76% rename from src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs rename to src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs index 4fd0f853d0..ad27ae020c 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor.cs @@ -7,17 +7,21 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// Applies an adaptive histogram equalization to the image. The image is split up in tiles. For each tile a cumulative distribution function (cdf) is calculated. /// To calculate the final equalized pixel value, the cdf value of four adjacent tiles will be interpolated. /// - internal class AdaptiveHistEqualizationProcessor : HistogramEqualizationProcessor + public class AdaptiveHistogramEqualizationProcessor : HistogramEqualizationProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// or 65536 for 16-bit grayscale images. /// Indicating whether to clip the histogram bins at a specific value. /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. /// The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100. - public AdaptiveHistEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int numberOfTiles) + public AdaptiveHistogramEqualizationProcessor( + int luminanceLevels, + bool clipHistogram, + float clipLimitPercentage, + int numberOfTiles) : base(luminanceLevels, clipHistogram, clipLimitPercentage) { this.NumberOfTiles = numberOfTiles; @@ -31,7 +35,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// public override IImageProcessor CreatePixelSpecificProcessor() { - return new AdaptiveHistEqualizationProcessor(this.LuminanceLevels, this.ClipHistogram, this.ClipLimitPercentage, this.NumberOfTiles); + return new AdaptiveHistogramEqualizationProcessor( + this.LuminanceLevels, + this.ClipHistogram, + this.ClipLimitPercentage, + this.NumberOfTiles); } } -} +} \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs similarity index 98% rename from src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor{TPixel}.cs rename to src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs index 930c4010fa..333444eb39 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationProcessor{TPixel}.cs @@ -21,18 +21,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// To calculate the final equalized pixel value, the cdf value of four adjacent tiles will be interpolated. /// /// The pixel format. - internal class AdaptiveHistEqualizationProcessor : HistogramEqualizationProcessor + internal class AdaptiveHistogramEqualizationProcessor : HistogramEqualizationProcessor where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// or 65536 for 16-bit grayscale images. /// Indicating whether to clip the histogram bins at a specific value. /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. /// The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100. - public AdaptiveHistEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int tiles) + public AdaptiveHistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int tiles) : base(luminanceLevels, clipHistogram, clipLimitPercentage) { Guard.MustBeGreaterThanOrEqualTo(tiles, 2, nameof(tiles)); diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor.cs similarity index 72% rename from src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs rename to src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor.cs index cd4a9644ff..36f798975a 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor.cs @@ -6,17 +6,21 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// /// Applies an adaptive histogram equalization to the image using an sliding window approach. /// - internal class AdaptiveHistEqualizationSWProcessor : HistogramEqualizationProcessor + public class AdaptiveHistogramEqualizationSlidingWindowProcessor : HistogramEqualizationProcessor { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// or 65536 for 16-bit grayscale images. /// Indicating whether to clip the histogram bins at a specific value. /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. /// The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100. - public AdaptiveHistEqualizationSWProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int numberOfTiles) + public AdaptiveHistogramEqualizationSlidingWindowProcessor( + int luminanceLevels, + bool clipHistogram, + float clipLimitPercentage, + int numberOfTiles) : base(luminanceLevels, clipHistogram, clipLimitPercentage) { this.NumberOfTiles = numberOfTiles; @@ -30,7 +34,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// public override IImageProcessor CreatePixelSpecificProcessor() { - return new AdaptiveHistEqualizationSWProcessor(this.LuminanceLevels, this.ClipHistogram, this.ClipLimitPercentage, this.NumberOfTiles); + return new AdaptiveHistogramEqualizationSlidingWindowProcessor( + this.LuminanceLevels, + this.ClipHistogram, + this.ClipLimitPercentage, + this.NumberOfTiles); } } } \ No newline at end of file diff --git a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs similarity index 98% rename from src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor{TPixel}.cs rename to src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs index 3584b1a88b..40e2d41d9b 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistEqualizationSWProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/AdaptiveHistogramEqualizationSlidingWindowProcessor{TPixel}.cs @@ -20,18 +20,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// Applies an adaptive histogram equalization to the image using an sliding window approach. /// /// The pixel format. - internal class AdaptiveHistEqualizationSWProcessor : HistogramEqualizationProcessor + internal class AdaptiveHistogramEqualizationSlidingWindowProcessor : HistogramEqualizationProcessor where TPixel : struct, IPixel { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The number of different luminance levels. Typical values are 256 for 8-bit grayscale images /// or 65536 for 16-bit grayscale images. /// Indicating whether to clip the histogram bins at a specific value. /// Histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. /// The number of tiles the image is split into (horizontal and vertically). Minimum value is 2. Maximum value is 100. - public AdaptiveHistEqualizationSWProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int tiles) + public AdaptiveHistogramEqualizationSlidingWindowProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage, int tiles) : base(luminanceLevels, clipHistogram, clipLimitPercentage) { Guard.MustBeGreaterThanOrEqualTo(tiles, 2, nameof(tiles)); diff --git a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs index 62e0185950..9af2c8352b 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/GlobalHistogramEqualizationProcessor.cs @@ -6,8 +6,14 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// /// Defines a global histogram equalization applicable to an . /// - internal class GlobalHistogramEqualizationProcessor : HistogramEqualizationProcessor + public class GlobalHistogramEqualizationProcessor : HistogramEqualizationProcessor { + /// + /// Initializes a new instance of the class. + /// + /// The number of luminance levels. + /// A value indicating whether to clip the histogram bins at a specific value. + /// The histogram clip limit in percent of the total pixels in the tile. Histogram bins which exceed this limit, will be capped at this value. public GlobalHistogramEqualizationProcessor(int luminanceLevels, bool clipHistogram, float clipLimitPercentage) : base(luminanceLevels, clipHistogram, clipLimitPercentage) { diff --git a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs index 4aad1f564e..b1d12f8478 100644 --- a/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Normalization/HistogramEqualizationProcessor.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization /// /// Defines a processor that normalizes the histogram of an image. /// - internal abstract class HistogramEqualizationProcessor : IImageProcessor + public abstract class HistogramEqualizationProcessor : IImageProcessor { /// /// Initializes a new instance of the class. @@ -63,7 +63,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization break; case HistogramEqualizationMethod.AdaptiveTileInterpolation: - processor = new AdaptiveHistEqualizationProcessor( + processor = new AdaptiveHistogramEqualizationProcessor( options.LuminanceLevels, options.ClipHistogram, options.ClipLimitPercentage, @@ -71,7 +71,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Normalization break; case HistogramEqualizationMethod.AdaptiveSlidingWindow: - processor = new AdaptiveHistEqualizationSWProcessor( + processor = new AdaptiveHistogramEqualizationSlidingWindowProcessor( options.LuminanceLevels, options.ClipHistogram, options.ClipLimitPercentage, From b92731896daab0ff627c7a8e65b07a4ec8f3bffb Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 9 May 2019 22:32:27 +0200 Subject: [PATCH 30/31] formatting --- .../AffineTransformProcessor{TPixel}.cs | 21 ++++-- .../Transforms/AutoOrientProcessor{TPixel}.cs | 5 +- .../ProjectiveTransformProcessor{TPixel}.cs | 27 ++++++-- .../Transforms/RotateProcessor{TPixel}.cs | 68 +++++++++++-------- 4 files changed, 80 insertions(+), 41 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs index e57ce826bd..5a043cb207 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineTransformProcessor{TPixel}.cs @@ -25,18 +25,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms this.Definition = definition; } + protected AffineTransformProcessor Definition { get; } + private Size TargetDimensions => this.Definition.TargetDimensions; private Matrix3x2 TransformMatrix => this.Definition.TransformMatrix; - protected AffineTransformProcessor Definition { get; } - /// protected override Image CreateDestination(Image source, Rectangle sourceRectangle) { // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = - source.Frames.Select(x => new ImageFrame(source.GetConfiguration(), this.TargetDimensions, x.Metadata.DeepClone())); + IEnumerable> frames = source.Frames.Select( + x => new ImageFrame(source.GetConfiguration(), this.TargetDimensions, x.Metadata.DeepClone())); // Use the overload to prevent an extra frame being added return new Image(source.GetConfiguration(), source.Metadata.DeepClone(), frames); @@ -111,10 +111,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms // Use the single precision position to calculate correct bounding pixels // otherwise we get rogue pixels outside of the bounds. var point = Vector2.Transform(new Vector2(x, y), matrix); - kernel.Convolve(point, x, ref ySpanRef, ref xSpanRef, source.PixelBuffer, vectorSpan); + kernel.Convolve( + point, + x, + ref ySpanRef, + ref xSpanRef, + source.PixelBuffer, + vectorSpan); } - PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan, targetRowSpan); + PixelOperations.Instance.FromVector4Destructive( + configuration, + vectorSpan, + targetRowSpan); } }); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs index 257c223dc4..8b3ec8690e 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AutoOrientProcessor{TPixel}.cs @@ -61,7 +61,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } /// - protected override void OnFrameApply(ImageFrame sourceBase, Rectangle sourceRectangle, Configuration config) + protected override void OnFrameApply( + ImageFrame sourceBase, + Rectangle sourceRectangle, + Configuration config) { // All processing happens at the image level within BeforeImageApply(); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs index e6d885803b..ab07040f71 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ProjectiveTransformProcessor{TPixel}.cs @@ -33,15 +33,23 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms protected override Image CreateDestination(Image source, Rectangle sourceRectangle) { // We will always be creating the clone even for mutate because we may need to resize the canvas - IEnumerable> frames = - source.Frames.Select(x => new ImageFrame(source.GetConfiguration(), this.TargetDimensions.Width, this.TargetDimensions.Height, x.Metadata.DeepClone())); + IEnumerable> frames = source.Frames.Select( + x => new ImageFrame( + source.GetConfiguration(), + this.TargetDimensions.Width, + this.TargetDimensions.Height, + x.Metadata.DeepClone())); // Use the overload to prevent an extra frame being added return new Image(source.GetConfiguration(), source.Metadata.DeepClone(), frames); } /// - protected override void OnFrameApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) + protected override void OnFrameApply( + ImageFrame source, + ImageFrame destination, + Rectangle sourceRectangle, + Configuration configuration) { Matrix4x4 transformMatrix = this.definition.TransformMatrix; @@ -110,10 +118,19 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms // Use the single precision position to calculate correct bounding pixels // otherwise we get rogue pixels outside of the bounds. Vector2 point = TransformUtils.ProjectiveTransform2D(x, y, matrix); - kernel.Convolve(point, x, ref ySpanRef, ref xSpanRef, source.PixelBuffer, vectorSpan); + kernel.Convolve( + point, + x, + ref ySpanRef, + ref xSpanRef, + source.PixelBuffer, + vectorSpan); } - PixelOperations.Instance.FromVector4Destructive(configuration, vectorSpan, targetRowSpan); + PixelOperations.Instance.FromVector4Destructive( + configuration, + vectorSpan, + targetRowSpan); } }); } diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs index aac6f65148..252cb77aba 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs @@ -27,18 +27,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms private float Degrees { get; } /// - protected override void OnFrameApply(ImageFrame source, ImageFrame destination, Rectangle sourceRectangle, Configuration configuration) - { - if (this.OptimizedApply(source, destination, configuration)) - { - return; - } - - base.OnFrameApply(source, destination, sourceRectangle, configuration); - } - - /// - protected override void AfterImageApply(Image source, Image destination, Rectangle sourceRectangle) + protected override void AfterImageApply( + Image source, + Image destination, + Rectangle sourceRectangle) { ExifProfile profile = destination.Metadata.ExifProfile; if (profile is null) @@ -57,6 +49,21 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms base.AfterImageApply(source, destination, sourceRectangle); } + /// + protected override void OnFrameApply( + ImageFrame source, + ImageFrame destination, + Rectangle sourceRectangle, + Configuration configuration) + { + if (this.OptimizedApply(source, destination, configuration)) + { + return; + } + + base.OnFrameApply(source, destination, sourceRectangle, configuration); + } + /// /// Wraps a given angle in degrees so that it falls withing the 0-360 degree range /// @@ -83,7 +90,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// The /// - private bool OptimizedApply(ImageFrame source, ImageFrame destination, Configuration configuration) + private bool OptimizedApply( + ImageFrame source, + ImageFrame destination, + Configuration configuration) { // Wrap the degrees to keep within 0-360 so we can apply optimizations when possible. float degrees = WrapDegrees(this.Degrees); @@ -117,16 +127,15 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms } /// - /// Rotates the image 270 degrees clockwise at the centre point. + /// Rotates the image 180 degrees clockwise at the centre point. /// /// The source image. /// The destination image. /// The configuration. - private void Rotate270(ImageFrame source, ImageFrame destination, Configuration configuration) + private void Rotate180(ImageFrame source, ImageFrame destination, Configuration configuration) { int width = source.Width; int height = source.Height; - Rectangle destinationBounds = destination.Bounds(); ParallelHelper.IterateRows( source.Bounds(), @@ -136,31 +145,27 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms for (int y = rows.Min; y < rows.Max; y++) { Span sourceRow = source.GetPixelRowSpan(y); + Span targetRow = destination.GetPixelRowSpan(height - y - 1); + for (int x = 0; x < width; x++) { - int newX = height - y - 1; - newX = height - newX - 1; - int newY = width - x - 1; - - if (destinationBounds.Contains(newX, newY)) - { - destination[newX, newY] = sourceRow[x]; - } + targetRow[width - x - 1] = sourceRow[x]; } } }); } /// - /// Rotates the image 180 degrees clockwise at the centre point. + /// Rotates the image 270 degrees clockwise at the centre point. /// /// The source image. /// The destination image. /// The configuration. - private void Rotate180(ImageFrame source, ImageFrame destination, Configuration configuration) + private void Rotate270(ImageFrame source, ImageFrame destination, Configuration configuration) { int width = source.Width; int height = source.Height; + Rectangle destinationBounds = destination.Bounds(); ParallelHelper.IterateRows( source.Bounds(), @@ -170,11 +175,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms for (int y = rows.Min; y < rows.Max; y++) { Span sourceRow = source.GetPixelRowSpan(y); - Span targetRow = destination.GetPixelRowSpan(height - y - 1); - for (int x = 0; x < width; x++) { - targetRow[width - x - 1] = sourceRow[x]; + int newX = height - y - 1; + newX = height - newX - 1; + int newY = width - x - 1; + + if (destinationBounds.Contains(newX, newY)) + { + destination[newX, newY] = sourceRow[x]; + } } } }); From 139a0000ce5efedc2a3d2a1080f49cc64976f754 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 9 May 2019 23:49:58 +0200 Subject: [PATCH 31/31] made AutoOrientExtensions non-generic --- src/ImageSharp/Processing/AutoOrientExtensions.cs | 10 ++++------ .../Processing/Transforms/AutoOrientTests.cs | 2 +- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/ImageSharp/Processing/AutoOrientExtensions.cs b/src/ImageSharp/Processing/AutoOrientExtensions.cs index d065d4583d..a831e2d9af 100644 --- a/src/ImageSharp/Processing/AutoOrientExtensions.cs +++ b/src/ImageSharp/Processing/AutoOrientExtensions.cs @@ -7,7 +7,7 @@ using SixLabors.ImageSharp.Processing.Processors.Transforms; namespace SixLabors.ImageSharp.Processing { /// - /// Defines extensions that allow the application of auto-orientation operations to an + /// Defines extensions that allow the application of auto-orientation operations to an /// using Mutate/Clone. /// public static class AutoOrientExtensions @@ -15,11 +15,9 @@ namespace SixLabors.ImageSharp.Processing /// /// Adjusts an image so that its orientation is suitable for viewing. Adjustments are based on EXIF metadata embedded in the image. /// - /// The pixel format. /// The image to auto rotate. - /// The - public static IImageProcessingContext AutoOrient(this IImageProcessingContext source) - where TPixel : struct, IPixel - => source.ApplyProcessor(new AutoOrientProcessor()); + /// The to allow chaining of operations. + public static IImageProcessingContext AutoOrient(this IImageProcessingContext source) + => source.ApplyProcessor(new AutoOrientProcessor()); } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Processing/Transforms/AutoOrientTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/AutoOrientTests.cs index bba4661db0..0547b46e3b 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/AutoOrientTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/AutoOrientTests.cs @@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms public void AutoOrient_AutoOrientProcessor() { this.operations.AutoOrient(); - this.Verify>(); + this.Verify(); } } } \ No newline at end of file