From ae22e017c55a27b64c2380660a7a456a6da9bac1 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 19 Apr 2019 17:27:01 +0200 Subject: [PATCH] buffer.CopyColumns(...) --- src/ImageSharp/Memory/Buffer2DExtensions.cs | 144 +++++++++++------- .../ImageSharp.Tests/Memory/Buffer2DTests.cs | 28 ++++ .../TestUtilities/TestDataGenerator.cs | 13 +- 3 files changed, 130 insertions(+), 55 deletions(-) diff --git a/src/ImageSharp/Memory/Buffer2DExtensions.cs b/src/ImageSharp/Memory/Buffer2DExtensions.cs index 17ab6e2522..c8bbf01c7f 100644 --- a/src/ImageSharp/Memory/Buffer2DExtensions.cs +++ b/src/ImageSharp/Memory/Buffer2DExtensions.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. using System; +using System.Buffers; using System.Runtime.CompilerServices; using SixLabors.Primitives; @@ -14,41 +15,90 @@ namespace SixLabors.ImageSharp.Memory internal static class Buffer2DExtensions { /// - /// Gets a to the backing buffer of . + /// Copy columns of inplace, + /// from positions starting at to positions at . /// - internal static Span GetSpan(this Buffer2D buffer) + public static unsafe void CopyColumns( + this Buffer2D buffer, + int sourceIndex, + int destIndex, + int columnCount) where T : struct { - return buffer.MemorySource.GetSpan(); + DebugGuard.NotNull(buffer, nameof(buffer)); + DebugGuard.MustBeGreaterThanOrEqualTo(sourceIndex, 0, nameof(sourceIndex)); + DebugGuard.MustBeGreaterThanOrEqualTo(destIndex, sourceIndex + columnCount, nameof(destIndex)); + DebugGuard.MustBeLessThanOrEqualTo(destIndex, buffer.Width - columnCount, nameof(destIndex)); + + int elementSize = Unsafe.SizeOf(); + int width = buffer.Width * elementSize; + int sOffset = sourceIndex * elementSize; + int dOffset = destIndex * elementSize; + long count = columnCount * elementSize; + + using (MemoryHandle handle = buffer.Memory.Pin()) + { + byte* basePtr = (byte*)handle.Pointer; + for (int y = 0; y < buffer.Height; y++) + { + byte* sPtr = basePtr + sOffset; + byte* dPtr = basePtr + dOffset; + + Buffer.MemoryCopy(sPtr, dPtr, count, count); + + basePtr += width; + } + } } /// - /// Gets a to the row 'y' beginning from the pixel at 'x'. + /// Returns a representing the full area of the buffer. /// - /// The buffer - /// The x coordinate (position in the row) - /// The y (row) coordinate /// The element type - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span GetRowSpan(this Buffer2D buffer, int x, int y) + /// The + /// The + public static Rectangle FullRectangle(this Buffer2D buffer) where T : struct { - return buffer.GetSpan().Slice((y * buffer.Width) + x, buffer.Width - x); + return new Rectangle(0, 0, buffer.Width, buffer.Height); } /// - /// Gets a to the row 'y' beginning from the pixel at the first pixel on that row. + /// Return a to the subarea represented by 'rectangle' /// - /// The buffer - /// The y (row) coordinate /// The element type - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static Span GetRowSpan(this Buffer2D buffer, int y) + /// The + /// The rectangle subarea + /// The + public static BufferArea GetArea(this Buffer2D buffer, in Rectangle rectangle) + where T : struct => + new BufferArea(buffer, rectangle); + + public static BufferArea GetArea(this Buffer2D buffer, int x, int y, int width, int height) + where T : struct => + new BufferArea(buffer, new Rectangle(x, y, width, height)); + + /// + /// Return a to the whole area of 'buffer' + /// + /// The element type + /// The + /// The + public static BufferArea GetArea(this Buffer2D buffer) + where T : struct => + new BufferArea(buffer); + + public static BufferArea GetAreaBetweenRows(this Buffer2D buffer, int minY, int maxY) + where T : struct => + new BufferArea(buffer, new Rectangle(0, minY, buffer.Width, maxY - minY)); + + /// + /// Gets a span for all the pixels in defined by + /// + public static Span GetMultiRowSpan(this Buffer2D buffer, in RowInterval rows) where T : struct { - return buffer.GetSpan().Slice(y * buffer.Width, buffer.Width); + return buffer.Span.Slice(rows.Min * buffer.Width, rows.Height * buffer.Width); } /// @@ -66,61 +116,53 @@ namespace SixLabors.ImageSharp.Memory } /// - /// Returns the size of the buffer. + /// Gets a to the row 'y' beginning from the pixel at 'x'. /// + /// The buffer + /// The x coordinate (position in the row) + /// The y (row) coordinate /// The element type - /// The - /// The of the buffer - public static Size Size(this Buffer2D buffer) + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span GetRowSpan(this Buffer2D buffer, int x, int y) where T : struct { - return new Size(buffer.Width, buffer.Height); + return buffer.GetSpan().Slice((y * buffer.Width) + x, buffer.Width - x); } /// - /// Returns a representing the full area of the buffer. + /// Gets a to the row 'y' beginning from the pixel at the first pixel on that row. /// + /// The buffer + /// The y (row) coordinate /// The element type - /// The - /// The - public static Rectangle FullRectangle(this Buffer2D buffer) + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Span GetRowSpan(this Buffer2D buffer, int y) where T : struct { - return new Rectangle(0, 0, buffer.Width, buffer.Height); + return buffer.GetSpan().Slice(y * buffer.Width, buffer.Width); } /// - /// Return a to the subarea represented by 'rectangle' - /// - /// The element type - /// The - /// The rectangle subarea - /// The - public static BufferArea GetArea(this Buffer2D buffer, in Rectangle rectangle) - where T : struct => new BufferArea(buffer, rectangle); - - public static BufferArea GetArea(this Buffer2D buffer, int x, int y, int width, int height) - where T : struct => new BufferArea(buffer, new Rectangle(x, y, width, height)); - - public static BufferArea GetAreaBetweenRows(this Buffer2D buffer, int minY, int maxY) - where T : struct => new BufferArea(buffer, new Rectangle(0, minY, buffer.Width, maxY - minY)); - - /// - /// Return a to the whole area of 'buffer' + /// Returns the size of the buffer. /// /// The element type /// The - /// The - public static BufferArea GetArea(this Buffer2D buffer) - where T : struct => new BufferArea(buffer); + /// The of the buffer + public static Size Size(this Buffer2D buffer) + where T : struct + { + return new Size(buffer.Width, buffer.Height); + } /// - /// Gets a span for all the pixels in defined by + /// Gets a to the backing buffer of . /// - public static Span GetMultiRowSpan(this Buffer2D buffer, in RowInterval rows) + internal static Span GetSpan(this Buffer2D buffer) where T : struct { - return buffer.Span.Slice(rows.Min * buffer.Width, rows.Height * buffer.Width); + return buffer.MemorySource.GetSpan(); } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs index 19ec725f27..3d40875461 100644 --- a/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs +++ b/tests/ImageSharp.Tests/Memory/Buffer2DTests.cs @@ -127,5 +127,33 @@ namespace SixLabors.ImageSharp.Tests.Memory Assert.Equal(new Size(10, 5), b.Size()); } } + + [Theory] + [InlineData(100, 20, 0, 90, 10)] + [InlineData(100, 3, 0, 50, 50)] + [InlineData(123, 23, 10, 80, 13)] + [InlineData(10, 1, 3, 6, 3)] + [InlineData(2, 2, 0, 1, 1)] + [InlineData(5, 1, 1, 3, 2)] + public void CopyColumns(int width, int height, int startIndex, int destIndex, int columnCount) + { + Random rnd = new Random(123); + using (Buffer2D b = this.MemoryAllocator.Allocate2D(width, height)) + { + rnd.RandomFill(b.Span, 0, 1); + + b.CopyColumns(startIndex, destIndex, columnCount); + + for (int y = 0; y < b.Height; y++) + { + Span row = b.GetRowSpan(y); + + Span s = row.Slice(startIndex, columnCount); + Span d = row.Slice(destIndex, columnCount); + + Xunit.Assert.True(s.SequenceEqual(d)); + } + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestUtilities/TestDataGenerator.cs b/tests/ImageSharp.Tests/TestUtilities/TestDataGenerator.cs index e3d8bf3806..4ccb387451 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestDataGenerator.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestDataGenerator.cs @@ -23,14 +23,19 @@ namespace SixLabors.ImageSharp.Tests { float[] values = new float[length]; - for (int i = 0; i < length; i++) - { - values[i] = GetRandomFloat(rnd, minVal, maxVal); - } + RandomFill(rnd, values, minVal, maxVal); return values; } + public static void RandomFill(this Random rnd, Span destination, float minVal, float maxVal) + { + for (int i = 0; i < destination.Length; i++) + { + destination[i] = GetRandomFloat(rnd, minVal, maxVal); + } + } + /// /// Creates an of the given length consisting of random values between the two ranges. ///