From f3c3cb6413a462a9a8abf02839d72d82209ca475 Mon Sep 17 00:00:00 2001 From: Evangelink Date: Fri, 16 Oct 2020 11:14:19 +0200 Subject: [PATCH 1/8] wip - Add basic structure for the swizzler --- .../Transforms/SwizzleExtensions.cs | 24 ++++++++++++++ .../Processors/Transforms/ISwizzler.cs | 18 ++++++++++ .../SwizzleProcessor{TSwizzler,TPixel}.cs | 33 +++++++++++++++++++ .../Transforms/SwizzleProcessor{TSwizzler}.cs | 32 ++++++++++++++++++ .../Processing/Transforms/SwizzleTests.cs | 31 +++++++++++++++++ 5 files changed, 138 insertions(+) create mode 100644 src/ImageSharp/Processing/Extensions/Transforms/SwizzleExtensions.cs create mode 100644 src/ImageSharp/Processing/Processors/Transforms/ISwizzler.cs create mode 100644 src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler,TPixel}.cs create mode 100644 src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler}.cs create mode 100644 tests/ImageSharp.Tests/Processing/Transforms/SwizzleTests.cs diff --git a/src/ImageSharp/Processing/Extensions/Transforms/SwizzleExtensions.cs b/src/ImageSharp/Processing/Extensions/Transforms/SwizzleExtensions.cs new file mode 100644 index 000000000..c02b3a00d --- /dev/null +++ b/src/ImageSharp/Processing/Extensions/Transforms/SwizzleExtensions.cs @@ -0,0 +1,24 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Processing.Processors.Transforms; + +namespace SixLabors.ImageSharp.Processing.Extensions.Transforms +{ + /// + /// Defines extensions that allow the application of swizzle operations on an + /// + public static class SwizzleExtensions + { + /// + /// Swizzles an image. + /// + /// The image to swizzle. + /// The swizzler function. + /// The swizzler function type. + /// The to allow chaining of operations. + public static IImageProcessingContext Swizzle(this IImageProcessingContext source, TSwizzler swizzler) + where TSwizzler : struct, ISwizzler + => source.ApplyProcessor(new SwizzleProcessor(swizzler)); + } +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/ISwizzler.cs b/src/ImageSharp/Processing/Processors/Transforms/ISwizzler.cs new file mode 100644 index 000000000..0230b7a86 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/ISwizzler.cs @@ -0,0 +1,18 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Processing.Processors.Transforms +{ + /// + /// Encapsulate an algorithm to swizzle pixels in an image. + /// + public interface ISwizzler + { + /// + /// Applies the swizzle transformation to a given point. + /// + /// Point to transform. + /// Transformed point. + Point Transform(Point point); + } +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler,TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler,TPixel}.cs new file mode 100644 index 000000000..a42a4bf77 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler,TPixel}.cs @@ -0,0 +1,33 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors.Transforms +{ + internal class SwizzleProcessor : ImageProcessor + where TSwizzler : struct, ISwizzler + where TPixel : unmanaged, IPixel + { + private readonly TSwizzler swizzler; + + public SwizzleProcessor(Configuration configuration, TSwizzler swizzler, Image source, Rectangle sourceRectangle) + : base(configuration, source, sourceRectangle) + { + this.swizzler = swizzler; + } + + /// + protected override void OnFrameApply(ImageFrame source) + { + for (int y = 0; y < source.Height; y++) + { + var pixelRowSpan = source.GetPixelRowSpan(y); + for (int x = 0; x < source.Width; x++) + { + var newPoint = this.swizzler.Transform(new Point(x, y)); + } + } + } + } +} diff --git a/src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler}.cs b/src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler}.cs new file mode 100644 index 000000000..e5b95e673 --- /dev/null +++ b/src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler}.cs @@ -0,0 +1,32 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; + +namespace SixLabors.ImageSharp.Processing.Processors.Transforms +{ + /// + /// Defines a swizzle operation on an image. + /// + /// The swizzle function type. + public sealed class SwizzleProcessor : IImageProcessor + where TSwizzler : struct, ISwizzler + { + /// + /// Initializes a new instance of the class. + /// + /// The swizzler operation. + public SwizzleProcessor(TSwizzler swizzler) + => this.Swizzler = swizzler; + + /// + /// Gets the swizzler operation. + /// + public TSwizzler Swizzler { get; } + + /// + public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) + where TPixel : unmanaged, IPixel + => new SwizzleProcessor(configuration, this, source, sourceRectangle); + } +} diff --git a/tests/ImageSharp.Tests/Processing/Transforms/SwizzleTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/SwizzleTests.cs new file mode 100644 index 000000000..2862df212 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Transforms/SwizzleTests.cs @@ -0,0 +1,31 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.Processing.Extensions.Transforms; +using SixLabors.ImageSharp.Processing.Processors.Transforms; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Transforms +{ + public class SwizzleTests : BaseImageOperationsExtensionTest + { + private struct InvertXAndYSwizzler : ISwizzler + { + public Point Transform(Point point) => new Point(point.Y, point.X); + } + + [Fact] + public void RotateDegreesFloatRotateProcessorWithAnglesSet() + { + this.operations.Swizzle(default(InvertXAndYSwizzler)); + SwizzleProcessor processor = this.Verify>(); + + // assert that pixels have been changed + + this.operations.Swizzle(default(InvertXAndYSwizzler)); + SwizzleProcessor processor2 = this.Verify>(); + + // assert that pixels have been changed (i.e. back to original) + } + } +} From a5e969466747fe87fde8b802c945d34786afd848 Mon Sep 17 00:00:00 2001 From: Evangelink Date: Tue, 24 Nov 2020 15:05:09 +0100 Subject: [PATCH 2/8] More progress toward a working swizzler --- .../Processors/Transforms/ISwizzler.cs | 9 +++-- .../SwizzleProcessor{TSwizzler,TPixel}.cs | 21 +++++++---- .../Transforms/SwizzleProcessor{TSwizzler}.cs | 6 ++-- .../Processors/Transforms/SwizzleTests.cs | 35 +++++++++++++++++++ .../Processing/Transforms/SwizzleTests.cs | 18 +++++++--- 5 files changed, 73 insertions(+), 16 deletions(-) create mode 100644 tests/ImageSharp.Tests/Processing/Processors/Transforms/SwizzleTests.cs diff --git a/src/ImageSharp/Processing/Processors/Transforms/ISwizzler.cs b/src/ImageSharp/Processing/Processors/Transforms/ISwizzler.cs index 0230b7a86..075ea2d52 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ISwizzler.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ISwizzler.cs @@ -8,11 +8,16 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// public interface ISwizzler { + /// + /// Gets the size of the image after transformation. + /// + Size DestinationSize { get; } + /// /// Applies the swizzle transformation to a given point. /// /// Point to transform. - /// Transformed point. - Point Transform(Point point); + /// The transformed point. + void Transform(Point point, out Point newPoint); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler,TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler,TPixel}.cs index a42a4bf77..c9b9b4570 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler,TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler,TPixel}.cs @@ -1,31 +1,38 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing.Processors.Transforms { - internal class SwizzleProcessor : ImageProcessor + internal class SwizzleProcessor : TransformProcessor where TSwizzler : struct, ISwizzler where TPixel : unmanaged, IPixel { private readonly TSwizzler swizzler; + private readonly Size destinationSize; public SwizzleProcessor(Configuration configuration, TSwizzler swizzler, Image source, Rectangle sourceRectangle) : base(configuration, source, sourceRectangle) { this.swizzler = swizzler; + this.destinationSize = swizzler.DestinationSize; } - /// - protected override void OnFrameApply(ImageFrame source) + protected override Size GetDestinationSize() + => this.destinationSize; + + protected override void OnFrameApply(ImageFrame source, ImageFrame destination) { - for (int y = 0; y < source.Height; y++) + Point p = default; + Point newPoint; + for (p.Y = 0; p.Y < source.Height; p.Y++) { - var pixelRowSpan = source.GetPixelRowSpan(y); - for (int x = 0; x < source.Width; x++) + for (p.X = 0; p.X < source.Width; p.X++) { - var newPoint = this.swizzler.Transform(new Point(x, y)); + this.swizzler.Transform(p, out newPoint); + destination[newPoint.X, newPoint.Y] = source[p.X, p.Y]; } } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler}.cs b/src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler}.cs index e5b95e673..d48257334 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler}.cs @@ -17,7 +17,9 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// /// The swizzler operation. public SwizzleProcessor(TSwizzler swizzler) - => this.Swizzler = swizzler; + { + this.Swizzler = swizzler; + } /// /// Gets the swizzler operation. @@ -27,6 +29,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : unmanaged, IPixel - => new SwizzleProcessor(configuration, this, source, sourceRectangle); + => new SwizzleProcessor(configuration, this.Swizzler, source, sourceRectangle); } } diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/SwizzleTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/SwizzleTests.cs new file mode 100644 index 000000000..b951a7fb0 --- /dev/null +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/SwizzleTests.cs @@ -0,0 +1,35 @@ +// Copyright (c) Six Labors. +// Licensed under the Apache License, Version 2.0. + +using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing.Extensions.Transforms; +using SixLabors.ImageSharp.Processing.Processors.Transforms; +using Xunit; + +namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms +{ + [GroupOutput("Transforms")] + public class SwizzleTests + { + private struct InvertXAndYSwizzler : ISwizzler + { + public Size DestinationSize => new Size(10, 10); + + public void Transform(Point point, out Point newPoint) + => newPoint = new Point(point.Y, point.X); + } + + [Theory] + [WithTestPatternImages(20, 37, PixelTypes.Rgba32)] + [WithTestPatternImages(53, 37, PixelTypes.Byte4)] + [WithTestPatternImages(17, 32, PixelTypes.Rgba32)] + public void InvertXAndYSwizzle(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + provider.RunValidatingProcessorTest( + ctx => ctx.Swizzle(default(InvertXAndYSwizzler)), + testOutputDetails: nameof(InvertXAndYSwizzler), + appendPixelTypeToFileName: false); + } + } +} diff --git a/tests/ImageSharp.Tests/Processing/Transforms/SwizzleTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/SwizzleTests.cs index 2862df212..c54bfa25e 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/SwizzleTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/SwizzleTests.cs @@ -11,21 +11,29 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms { private struct InvertXAndYSwizzler : ISwizzler { - public Point Transform(Point point) => new Point(point.Y, point.X); + public Size DestinationSize => new Size(10, 10); + + public void Transform(Point point, out Point newPoint) + => newPoint = new Point(point.Y, point.X); } [Fact] - public void RotateDegreesFloatRotateProcessorWithAnglesSet() + public void InvertXAndYSwizzlerSetsCorrectSizes() { + int width = 5; + int height = 10; + this.operations.Swizzle(default(InvertXAndYSwizzler)); SwizzleProcessor processor = this.Verify>(); - // assert that pixels have been changed + Assert.Equal(processor.Swizzler.DestinationSize.Width, height); + Assert.Equal(processor.Swizzler.DestinationSize.Height, width); this.operations.Swizzle(default(InvertXAndYSwizzler)); - SwizzleProcessor processor2 = this.Verify>(); + SwizzleProcessor processor2 = this.Verify>(1); - // assert that pixels have been changed (i.e. back to original) + Assert.Equal(processor2.Swizzler.DestinationSize.Width, width); + Assert.Equal(processor2.Swizzler.DestinationSize.Height, height); } } } From e9738f21eb964fb7a1d0db23c2a3f6c8336dd18d Mon Sep 17 00:00:00 2001 From: Evangelink Date: Tue, 24 Nov 2020 15:38:55 +0100 Subject: [PATCH 3/8] Fix unit test --- .../Processing/Transforms/SwizzleTests.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/ImageSharp.Tests/Processing/Transforms/SwizzleTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/SwizzleTests.cs index c54bfa25e..9c2701ae7 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/SwizzleTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/SwizzleTests.cs @@ -11,7 +11,12 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms { private struct InvertXAndYSwizzler : ISwizzler { - public Size DestinationSize => new Size(10, 10); + public InvertXAndYSwizzler(Size sourceSize) + { + this.DestinationSize = new Size(sourceSize.Height, sourceSize.Width); + } + + public Size DestinationSize { get; } public void Transform(Point point, out Point newPoint) => newPoint = new Point(point.Y, point.X); @@ -23,13 +28,13 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms int width = 5; int height = 10; - this.operations.Swizzle(default(InvertXAndYSwizzler)); + this.operations.Swizzle(new InvertXAndYSwizzler(new Size(width, height))); SwizzleProcessor processor = this.Verify>(); Assert.Equal(processor.Swizzler.DestinationSize.Width, height); Assert.Equal(processor.Swizzler.DestinationSize.Height, width); - this.operations.Swizzle(default(InvertXAndYSwizzler)); + this.operations.Swizzle(new InvertXAndYSwizzler(processor.Swizzler.DestinationSize)); SwizzleProcessor processor2 = this.Verify>(1); Assert.Equal(processor2.Swizzler.DestinationSize.Width, width); From a665bce50e370effd28ec92ee34e15c5d2dd78dd Mon Sep 17 00:00:00 2001 From: Evangelink Date: Tue, 24 Nov 2020 15:55:59 +0100 Subject: [PATCH 4/8] Update integration tests --- .../Processing/Processors/Transforms/SwizzleTests.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/SwizzleTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/SwizzleTests.cs index b951a7fb0..4d3b60494 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/SwizzleTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/SwizzleTests.cs @@ -13,7 +13,12 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms { private struct InvertXAndYSwizzler : ISwizzler { - public Size DestinationSize => new Size(10, 10); + public InvertXAndYSwizzler(Size sourceSize) + { + this.DestinationSize = new Size(sourceSize.Height, sourceSize.Width); + } + + public Size DestinationSize { get; } public void Transform(Point point, out Point newPoint) => newPoint = new Point(point.Y, point.X); @@ -26,8 +31,9 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms public void InvertXAndYSwizzle(TestImageProvider provider) where TPixel : unmanaged, IPixel { + Image image = provider.GetImage(); provider.RunValidatingProcessorTest( - ctx => ctx.Swizzle(default(InvertXAndYSwizzler)), + ctx => ctx.Swizzle(new InvertXAndYSwizzler(new Size(image.Width, image.Height))), testOutputDetails: nameof(InvertXAndYSwizzler), appendPixelTypeToFileName: false); } From 14129427519ac33a9fe7188c40473577f13c4907 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Thu, 26 Nov 2020 09:36:38 +0100 Subject: [PATCH 5/8] Apply suggested change for test Avoid relying on the submodule change by implementing the invert swizzle operation --- .../Processors/Transforms/SwizzleTests.cs | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/SwizzleTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/SwizzleTests.cs index 4d3b60494..97b6a6414 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/SwizzleTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/SwizzleTests.cs @@ -2,8 +2,10 @@ // Licensed under the Apache License, Version 2.0. using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Processing; using SixLabors.ImageSharp.Processing.Extensions.Transforms; using SixLabors.ImageSharp.Processing.Processors.Transforms; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using Xunit; namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms @@ -31,11 +33,26 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms public void InvertXAndYSwizzle(TestImageProvider provider) where TPixel : unmanaged, IPixel { - Image image = provider.GetImage(); - provider.RunValidatingProcessorTest( - ctx => ctx.Swizzle(new InvertXAndYSwizzler(new Size(image.Width, image.Height))), - testOutputDetails: nameof(InvertXAndYSwizzler), - appendPixelTypeToFileName: false); + using Image expectedImage = provider.GetImage(); + using Image image = provider.GetImage(); + + image.Mutate(ctx => ctx.Swizzle(new InvertXAndYSwizzler(new Size(image.Width, image.Height)))); + + image.DebugSave( + provider, + nameof(InvertXAndYSwizzler), + appendPixelTypeToFileName: false, + appendSourceFileOrDescription: true); + + image.Mutate(ctx => ctx.Swizzle(new InvertXAndYSwizzler(new Size(image.Width, image.Height)))); + + image.DebugSave( + provider, + "Unswizzle", + appendPixelTypeToFileName: false, + appendSourceFileOrDescription: true); + + ImageComparer.Exact.VerifySimilarity(expectedImage, image); } } } From e687f7b10acdd13facceb3e80d2d0cb0fdfb5b07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Thu, 26 Nov 2020 23:12:33 +0100 Subject: [PATCH 6/8] Address review comments --- src/ImageSharp/Processing/Processors/Transforms/ISwizzler.cs | 4 ++-- .../Transforms/SwizzleProcessor{TSwizzler,TPixel}.cs | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Transforms/ISwizzler.cs b/src/ImageSharp/Processing/Processors/Transforms/ISwizzler.cs index 075ea2d52..efa3e35a4 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ISwizzler.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ISwizzler.cs @@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// Applies the swizzle transformation to a given point. /// /// Point to transform. - /// The transformed point. - void Transform(Point point, out Point newPoint); + /// The transformed point. + Point Transform(Point point); } } diff --git a/src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler,TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler,TPixel}.cs index c9b9b4570..aab17d292 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler,TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/SwizzleProcessor{TSwizzler,TPixel}.cs @@ -29,10 +29,11 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms Point newPoint; for (p.Y = 0; p.Y < source.Height; p.Y++) { + Span rowSpan = source.GetPixelRowSpan(p.Y); for (p.X = 0; p.X < source.Width; p.X++) { - this.swizzler.Transform(p, out newPoint); - destination[newPoint.X, newPoint.Y] = source[p.X, p.Y]; + newPoint = this.swizzler.Transform(p); + destination[newPoint.X, newPoint.Y] = rowSpan[p.X]; } } } From 1547379bdd95974b8e756f6ce8f60c9c215e4f87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Amaury=20Lev=C3=A9?= Date: Thu, 26 Nov 2020 23:25:36 +0100 Subject: [PATCH 7/8] Forgot to commit tests --- .../Processing/Processors/Transforms/SwizzleTests.cs | 4 ++-- tests/ImageSharp.Tests/Processing/Transforms/SwizzleTests.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/ImageSharp.Tests/Processing/Processors/Transforms/SwizzleTests.cs b/tests/ImageSharp.Tests/Processing/Processors/Transforms/SwizzleTests.cs index 97b6a6414..f508744fa 100644 --- a/tests/ImageSharp.Tests/Processing/Processors/Transforms/SwizzleTests.cs +++ b/tests/ImageSharp.Tests/Processing/Processors/Transforms/SwizzleTests.cs @@ -22,8 +22,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Processors.Transforms public Size DestinationSize { get; } - public void Transform(Point point, out Point newPoint) - => newPoint = new Point(point.Y, point.X); + public Point Transform(Point point) + => new Point(point.Y, point.X); } [Theory] diff --git a/tests/ImageSharp.Tests/Processing/Transforms/SwizzleTests.cs b/tests/ImageSharp.Tests/Processing/Transforms/SwizzleTests.cs index 9c2701ae7..cde6aeca3 100644 --- a/tests/ImageSharp.Tests/Processing/Transforms/SwizzleTests.cs +++ b/tests/ImageSharp.Tests/Processing/Transforms/SwizzleTests.cs @@ -18,8 +18,8 @@ namespace SixLabors.ImageSharp.Tests.Processing.Transforms public Size DestinationSize { get; } - public void Transform(Point point, out Point newPoint) - => newPoint = new Point(point.Y, point.X); + public Point Transform(Point point) + => new Point(point.Y, point.X); } [Fact] From c71f9e3393ec74ae95478b4f9f87cce3ebc6f874 Mon Sep 17 00:00:00 2001 From: Sergio Pedri Date: Fri, 27 Nov 2020 17:12:49 +0100 Subject: [PATCH 8/8] Reduce code duplication due to reified generics --- .../Convolution/BokehBlurProcessor.cs | 56 +++++++++++++++++++ .../Convolution/BokehBlurProcessor{TPixel}.cs | 50 +---------------- 2 files changed, 58 insertions(+), 48 deletions(-) diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs index 8a4c703e0..352960f41 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor.cs @@ -1,6 +1,11 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using System; +using System.Numerics; +using System.Runtime.CompilerServices; +using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Processing.Processors.Convolution @@ -77,5 +82,56 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution public IImageProcessor CreatePixelSpecificProcessor(Configuration configuration, Image source, Rectangle sourceRectangle) where TPixel : unmanaged, IPixel => new BokehBlurProcessor(configuration, this, source, sourceRectangle); + + /// + /// A implementing the horizontal convolution logic for . + /// + /// + /// This type is located in the non-generic class and not in , where + /// it is actually used, because it does not use any generic parameters internally. Defining in a non-generic class means that there will only + /// ever be a single instantiation of this type for the JIT/AOT compilers to process, instead of having duplicate versions for each pixel type. + /// + internal readonly struct ApplyHorizontalConvolutionRowOperation : IRowOperation + { + private readonly Rectangle bounds; + private readonly Buffer2D targetValues; + private readonly Buffer2D sourceValues; + private readonly Complex64[] kernel; + private readonly float z; + private readonly float w; + private readonly int maxY; + private readonly int maxX; + + [MethodImpl(InliningOptions.ShortMethod)] + public ApplyHorizontalConvolutionRowOperation( + Rectangle bounds, + Buffer2D targetValues, + Buffer2D sourceValues, + Complex64[] kernel, + float z, + float w) + { + this.bounds = bounds; + this.maxY = this.bounds.Bottom - 1; + this.maxX = this.bounds.Right - 1; + this.targetValues = targetValues; + this.sourceValues = sourceValues; + this.kernel = kernel; + this.z = z; + this.w = w; + } + + /// + [MethodImpl(InliningOptions.ShortMethod)] + public void Invoke(int y) + { + Span targetRowSpan = this.targetValues.GetRowSpan(y).Slice(this.bounds.X); + + for (int x = 0; x < this.bounds.Width; x++) + { + Buffer2DUtils.Convolve4AndAccumulatePartials(this.kernel, this.sourceValues, targetRowSpan, y, x, this.bounds.Y, this.maxY, this.bounds.X, this.maxX, this.z, this.w); + } + } + } } } diff --git a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs index cf5367343..dfe54bf2e 100644 --- a/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Convolution/BokehBlurProcessor{TPixel}.cs @@ -127,7 +127,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution in verticalOperation); // Compute the horizontal 1D convolutions and accumulate the partial results on the target buffer - var horizontalOperation = new ApplyHorizontalConvolutionRowOperation(sourceRectangle, processingBuffer, firstPassBuffer, kernel, parameters.Z, parameters.W); + var horizontalOperation = new BokehBlurProcessor.ApplyHorizontalConvolutionRowOperation(sourceRectangle, processingBuffer, firstPassBuffer, kernel, parameters.Z, parameters.W); ParallelRowIterator.IterateRows( configuration, sourceRectangle, @@ -175,52 +175,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution } } - /// - /// A implementing the horizontal convolution logic for . - /// - private readonly struct ApplyHorizontalConvolutionRowOperation : IRowOperation - { - private readonly Rectangle bounds; - private readonly Buffer2D targetValues; - private readonly Buffer2D sourceValues; - private readonly Complex64[] kernel; - private readonly float z; - private readonly float w; - private readonly int maxY; - private readonly int maxX; - - [MethodImpl(InliningOptions.ShortMethod)] - public ApplyHorizontalConvolutionRowOperation( - Rectangle bounds, - Buffer2D targetValues, - Buffer2D sourceValues, - Complex64[] kernel, - float z, - float w) - { - this.bounds = bounds; - this.maxY = this.bounds.Bottom - 1; - this.maxX = this.bounds.Right - 1; - this.targetValues = targetValues; - this.sourceValues = sourceValues; - this.kernel = kernel; - this.z = z; - this.w = w; - } - - /// - [MethodImpl(InliningOptions.ShortMethod)] - public void Invoke(int y) - { - Span targetRowSpan = this.targetValues.GetRowSpan(y).Slice(this.bounds.X); - - for (int x = 0; x < this.bounds.Width; x++) - { - Buffer2DUtils.Convolve4AndAccumulatePartials(this.kernel, this.sourceValues, targetRowSpan, y, x, this.bounds.Y, this.maxY, this.bounds.X, this.maxX, this.z, this.w); - } - } - } - /// /// A implementing the gamma exposure logic for . /// @@ -304,7 +258,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Convolution for (int x = 0; x < this.bounds.Width; x++) { ref Vector4 v = ref Unsafe.Add(ref sourceRef, x); - var clamp = Numerics.Clamp(v, low, high); + Vector4 clamp = Numerics.Clamp(v, low, high); v.X = MathF.Pow(clamp.X, this.inverseGamma); v.Y = MathF.Pow(clamp.Y, this.inverseGamma); v.Z = MathF.Pow(clamp.Z, this.inverseGamma);