From e0e0cf189d1053a4b2e887cfa11659d5d20b283f Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 4 Dec 2017 16:06:13 +1100 Subject: [PATCH] Span!! --- src/ImageSharp/Memory/Buffer{T}.cs | 2 +- .../Processors/Transforms/AffineProcessor.cs | 157 +++++++++--------- 2 files changed, 81 insertions(+), 78 deletions(-) diff --git a/src/ImageSharp/Memory/Buffer{T}.cs b/src/ImageSharp/Memory/Buffer{T}.cs index f5c9ed00a..67af23426 100644 --- a/src/ImageSharp/Memory/Buffer{T}.cs +++ b/src/ImageSharp/Memory/Buffer{T}.cs @@ -142,7 +142,7 @@ namespace SixLabors.ImageSharp.Memory [MethodImpl(MethodImplOptions.AggressiveInlining)] public static Buffer CreateClean(int count) { - Buffer buffer = new Buffer(count); + var buffer = new Buffer(count); buffer.Clear(); return buffer; } diff --git a/src/ImageSharp/Processing/Processors/Transforms/AffineProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/AffineProcessor.cs index 767c6feed..f2e74abd1 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/AffineProcessor.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/AffineProcessor.cs @@ -103,92 +103,95 @@ namespace SixLabors.ImageSharp.Processing.Processors int xLength = (int)MathF.Ceiling((radius.X * 2) + 2); int yLength = (int)MathF.Ceiling((radius.Y * 2) + 2); - Parallel.For( - 0, - height, - configuration.ParallelOptions, - y => - { - Span destRow = destination.GetPixelRowSpan(y); - using (var yBuffer = new Buffer(yLength)) - using (var xBuffer = new Buffer(xLength)) - { - for (int x = 0; x < width; x++) + using (var yBuffer = new Buffer2D(yLength, height)) + using (var xBuffer = new Buffer2D(xLength, height)) + { + Parallel.For( + 0, + height, + configuration.ParallelOptions, + y => { - // 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); + Span destRow = destination.GetPixelRowSpan(y); + Span ySpan = yBuffer.GetRowSpan(y); + Span xSpan = xBuffer.GetRowSpan(y); - // Clamp sampling pixel radial extents to the source image edges - Vector2 maxXY = point + radius; - Vector2 minXY = point - radius; + 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); - var extents = new Vector4( - MathF.Ceiling(maxXY.X), - MathF.Ceiling(maxXY.Y), - MathF.Floor(minXY.X), - MathF.Floor(minXY.Y)); + // Clamp sampling pixel radial extents to the source image edges + Vector2 maxXY = point + radius; + Vector2 minXY = point - radius; - int right = (int)extents.X; - int bottom = (int)extents.Y; - int left = (int)extents.Z; - int top = (int)extents.W; + var extents = new Vector4( + MathF.Ceiling(maxXY.X), + MathF.Ceiling(maxXY.Y), + MathF.Floor(minXY.X), + MathF.Floor(minXY.Y)); - extents = Vector4.Clamp(extents, Vector4.Zero, maxSource); + int right = (int)extents.X; + int bottom = (int)extents.Y; + int left = (int)extents.Z; + int top = (int)extents.W; - int maxX = (int)extents.X; - int maxY = (int)extents.Y; - int minX = (int)extents.Z; - int minY = (int)extents.W; + extents = Vector4.Clamp(extents, Vector4.Zero, maxSource); - if (minX == maxX || minY == maxY) - { - continue; - } + int maxX = (int)extents.X; + int maxY = (int)extents.Y; + int minX = (int)extents.Z; + int minY = (int)extents.W; - // It appears these have to be calculated on-the-fly. - // Precalulating transformed weights would require prior knowledge of every transformed pixel location - // since they can be at sub-pixel positions. - // I've optimized where I can but am always open to suggestions. - // - // Create and normalize the y-weights - if (yScale > 1) - { - CalculateWeightsDown(top, bottom, minY, maxY, point.Y, sampler, yScale, yBuffer); - } - else - { - CalculateWeightsScaleUp(minY, maxY, point.Y, sampler, yBuffer); - } + if (minX == maxX || minY == maxY) + { + continue; + } - // Create and normalize the x-weights - if (xScale > 1) - { - CalculateWeightsDown(left, right, minX, maxX, point.X, sampler, xScale, xBuffer); - } - else - { - CalculateWeightsScaleUp(minX, maxX, point.X, sampler, xBuffer); - } + // It appears these have to be calculated on-the-fly. + // Precalulating transformed weights would require prior knowledge of every transformed pixel location + // since they can be at sub-pixel positions on both axis. + // I've optimized where I can but am always open to suggestions. + // + // Create and normalize the y-weights + if (yScale > 1) + { + CalculateWeightsDown(top, bottom, minY, maxY, point.Y, sampler, yScale, ySpan); + } + else + { + CalculateWeightsScaleUp(minY, maxY, point.Y, sampler, ySpan); + } - // Now multiply the results against the offsets - Vector4 sum = Vector4.Zero; - for (int yy = 0, j = minY; j <= maxY; j++, yy++) - { - float yWeight = yBuffer[yy]; + // Create and normalize the x-weights + if (xScale > 1) + { + CalculateWeightsDown(left, right, minX, maxX, point.X, sampler, xScale, xSpan); + } + else + { + CalculateWeightsScaleUp(minX, maxX, point.X, sampler, xSpan); + } - for (int xx = 0, i = minX; i <= maxX; i++, xx++) + // Now multiply the results against the offsets + Vector4 sum = Vector4.Zero; + for (int yy = 0, j = minY; j <= maxY; j++, yy++) { - float xWeight = xBuffer[xx]; - sum += source[i, j].ToVector4() * xWeight * yWeight; + float yWeight = ySpan[yy]; + + for (int xx = 0, i = minX; i <= maxX; i++, xx++) + { + float xWeight = xSpan[xx]; + sum += source[i, j].ToVector4() * xWeight * yWeight; + } } - } - ref TPixel dest = ref destRow[x]; - dest.PackFromVector4(sum); - } - } - }); + ref TPixel dest = ref destRow[x]; + dest.PackFromVector4(sum); + } + }); + } } /// @@ -206,7 +209,8 @@ namespace SixLabors.ImageSharp.Processing.Processors } /// - /// Calculated the weights for the given point. This method uses more samples than the upscaled version to ensure edge pixels are correctly rendered. + /// Calculated the weights for the given point. + /// This method uses more samples than the upscaled version to ensure edge pixels are correctly rendered. /// Additionally the weights are nomalized. /// /// The minimum sampling offset @@ -218,7 +222,7 @@ namespace SixLabors.ImageSharp.Processing.Processors /// The transformed image scale relative to the source /// The collection of weights [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void CalculateWeightsDown(int min, int max, int sourceMin, int sourceMax, float point, IResampler sampler, float scale, Buffer weights) + private static void CalculateWeightsDown(int min, int max, int sourceMin, int sourceMax, float point, IResampler sampler, float scale, Span weights) { float sum = 0; ref float weightsBaseRef = ref weights[0]; @@ -253,8 +257,7 @@ namespace SixLabors.ImageSharp.Processing.Processors } /// - /// Calculated the weights for the given point. This method uses more samples than the upscaled version to ensure edge pixels are correctly rendered. - /// Additionally the weights are nomalized. + /// Calculated the weights for the given point. /// /// The minimum source bounds /// The maximum source bounds @@ -262,7 +265,7 @@ namespace SixLabors.ImageSharp.Processing.Processors /// The sampler /// The collection of weights [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void CalculateWeightsScaleUp(int sourceMin, int sourceMax, float point, IResampler sampler, Buffer weights) + private static void CalculateWeightsScaleUp(int sourceMin, int sourceMax, float point, IResampler sampler, Span weights) { ref float weightsBaseRef = ref weights[0]; for (int x = 0, i = sourceMin; i <= sourceMax; i++, x++)