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.
///