diff --git a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs index 142068a487..bf03ce319b 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/RotateProcessor{TPixel}.cs @@ -2,7 +2,9 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Advanced; +using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata.Profiles.Exif; using SixLabors.ImageSharp.PixelFormats; @@ -15,6 +17,8 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms internal class RotateProcessor : AffineTransformProcessor where TPixel : struct, IPixel { + private float degrees; + /// /// Initializes a new instance of the class. /// @@ -24,11 +28,18 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// The source area to process for the current processor instance. public RotateProcessor(Configuration configuration, RotateProcessor definition, Image source, Rectangle sourceRectangle) : base(configuration, definition, source, sourceRectangle) + => this.degrees = definition.Degrees; + + /// + protected override void OnFrameApply(ImageFrame source, ImageFrame destination) { - this.Degrees = definition.Degrees; - } + if (this.OptimizedApply(source, destination, this.Configuration)) + { + return; + } - private float Degrees { get; } + base.OnFrameApply(source, destination); + } /// protected override void AfterImageApply(Image destination) @@ -39,7 +50,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms return; } - if (MathF.Abs(WrapDegrees(this.Degrees)) < Constants.Epsilon) + if (MathF.Abs(WrapDegrees(this.degrees)) < Constants.Epsilon) { // No need to do anything so return. return; @@ -50,17 +61,6 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms base.AfterImageApply(destination); } - /// - protected override void OnFrameApply(ImageFrame source, ImageFrame destination) - { - if (this.OptimizedApply(source, destination, this.Configuration)) - { - return; - } - - base.OnFrameApply(source, destination); - } - /// /// Wraps a given angle in degrees so that it falls withing the 0-360 degree range /// @@ -93,7 +93,7 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms Configuration configuration) { // Wrap the degrees to keep within 0-360 so we can apply optimizations when possible. - float degrees = WrapDegrees(this.Degrees); + float degrees = WrapDegrees(this.degrees); if (MathF.Abs(degrees) < Constants.Epsilon) { @@ -131,25 +131,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// The configuration. private void Rotate180(ImageFrame source, ImageFrame destination, Configuration configuration) { - int width = source.Width; - int height = source.Height; - ParallelRowIterator.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]; - } - } - }); + new Rotate180RowIntervalAction(source.Width, source.Height, source, destination)); } /// @@ -160,31 +145,10 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// The configuration. private void Rotate270(ImageFrame source, ImageFrame destination, Configuration configuration) { - int width = source.Width; - int height = source.Height; - Rectangle destinationBounds = destination.Bounds(); - ParallelRowIterator.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]; - } - } - } - }); + new Rotate270RowIntervalAction(destination.Bounds(), source.Width, source.Height, source, destination)); } /// @@ -195,28 +159,131 @@ namespace SixLabors.ImageSharp.Processing.Processors.Transforms /// The configuration. private void Rotate90(ImageFrame source, ImageFrame destination, Configuration configuration) { - int width = source.Width; - int height = source.Height; - Rectangle destinationBounds = destination.Bounds(); - ParallelRowIterator.IterateRows( source.Bounds(), configuration, - rows => + new Rotate90RowIntervalAction(destination.Bounds(), source.Width, source.Height, source, destination)); + } + + private readonly struct Rotate180RowIntervalAction : IRowIntervalAction + { + private readonly int width; + private readonly int height; + private readonly ImageFrame source; + private readonly ImageFrame destination; + + [MethodImpl(InliningOptions.ShortMethod)] + public Rotate180RowIntervalAction( + int width, + int height, + ImageFrame source, + ImageFrame destination) + { + this.width = width; + this.height = height; + this.source = source; + this.destination = destination; + } + + [MethodImpl(InliningOptions.ShortMethod)] + public void Invoke(in RowInterval rows) + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span sourceRow = this.source.GetPixelRowSpan(y); + Span targetRow = this.destination.GetPixelRowSpan(this.height - y - 1); + + for (int x = 0; x < this.width; x++) + { + targetRow[this.width - x - 1] = sourceRow[x]; + } + } + } + } + + private readonly struct Rotate270RowIntervalAction : IRowIntervalAction + { + private readonly Rectangle bounds; + private readonly int width; + private readonly int height; + private readonly ImageFrame source; + private readonly ImageFrame destination; + + [MethodImpl(InliningOptions.ShortMethod)] + public Rotate270RowIntervalAction( + Rectangle bounds, + int width, + int height, + ImageFrame source, + ImageFrame destination) + { + this.bounds = bounds; + this.width = width; + this.height = height; + this.source = source; + this.destination = destination; + } + + [MethodImpl(InliningOptions.ShortMethod)] + public void Invoke(in RowInterval rows) + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span sourceRow = this.source.GetPixelRowSpan(y); + for (int x = 0; x < this.width; x++) + { + int newX = this.height - y - 1; + newX = this.height - newX - 1; + int newY = this.width - x - 1; + + if (this.bounds.Contains(newX, newY)) + { + this.destination[newX, newY] = sourceRow[x]; + } + } + } + } + } + + private readonly struct Rotate90RowIntervalAction : IRowIntervalAction + { + private readonly Rectangle bounds; + private readonly int width; + private readonly int height; + private readonly ImageFrame source; + private readonly ImageFrame destination; + + [MethodImpl(InliningOptions.ShortMethod)] + public Rotate90RowIntervalAction( + Rectangle bounds, + int width, + int height, + ImageFrame source, + ImageFrame destination) + { + this.bounds = bounds; + this.width = width; + this.height = height; + this.source = source; + this.destination = destination; + } + + [MethodImpl(InliningOptions.ShortMethod)] + public void Invoke(in RowInterval rows) + { + for (int y = rows.Min; y < rows.Max; y++) + { + Span sourceRow = this.source.GetPixelRowSpan(y); + int newX = this.height - y - 1; + for (int x = 0; x < this.width; x++) { - for (int y = rows.Min; y < rows.Max; y++) + if (this.bounds.Contains(newX, x)) { - 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]; - } - } + this.destination[newX, x] = sourceRow[x]; } - }); + } + } + } } } }