diff --git a/src/ImageSharp/Memory/BufferArea{T}.cs b/src/ImageSharp/Memory/BufferArea{T}.cs index 983d6b3a76..b9210e301f 100644 --- a/src/ImageSharp/Memory/BufferArea{T}.cs +++ b/src/ImageSharp/Memory/BufferArea{T}.cs @@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Memory /// The position inside a row /// The row index /// The reference to the value - public ref T this[int x, int y] => ref this.DestinationBuffer.GetSingleSpan()[this.GetIndexOf(x, y)]; + public ref T this[int x, int y] => ref this.DestinationBuffer[x + this.Rectangle.X, y + this.Rectangle.Y]; /// /// Gets a reference to the [0,0] element. @@ -80,7 +80,7 @@ namespace SixLabors.ImageSharp.Memory /// The reference to the [0,0] element [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref T GetReferenceToOrigin() => - ref this.DestinationBuffer.GetSingleSpan()[(this.Rectangle.Y * this.DestinationBuffer.Width) + this.Rectangle.X]; + ref this.GetRowSpan(0)[0]; /// /// Gets a span to row 'y' inside this area. @@ -94,16 +94,16 @@ namespace SixLabors.ImageSharp.Memory int xx = this.Rectangle.X; int width = this.Rectangle.Width; - return this.DestinationBuffer.GetSingleSpan().Slice(yy + xx, width); + return this.DestinationBuffer.MemoryGroup.GetBoundedSlice(yy + xx, width).Span; } /// /// Returns a sub-area as . (Similar to .) /// - /// The x index at the subarea origo - /// The y index at the subarea origo - /// The desired width of the subarea - /// The desired height of the subarea + /// The x index at the subarea origin. + /// The y index at the subarea origin. + /// The desired width of the subarea. + /// The desired height of the subarea. /// The subarea [MethodImpl(MethodImplOptions.AggressiveInlining)] public BufferArea GetSubArea(int x, int y, int width, int height) @@ -129,14 +129,6 @@ namespace SixLabors.ImageSharp.Memory return new BufferArea(this.DestinationBuffer, rectangle); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private int GetIndexOf(int x, int y) - { - int yy = this.GetRowIndex(y); - int xx = this.Rectangle.X + x; - return yy + xx; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal int GetRowIndex(int y) { @@ -148,7 +140,7 @@ namespace SixLabors.ImageSharp.Memory // Optimization for when the size of the area is the same as the buffer size. if (this.IsFullBufferArea) { - this.DestinationBuffer.GetSingleSpan().Clear(); + this.DestinationBuffer.MemoryGroup.Clear(); return; } diff --git a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs index ce719dc910..1b4c297f39 100644 --- a/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs +++ b/src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs @@ -7,6 +7,24 @@ namespace SixLabors.ImageSharp.Memory { internal static class MemoryGroupExtensions { + public static void Fill(this IMemoryGroup group, T value) + where T : struct + { + foreach (Memory memory in group) + { + memory.Span.Fill(value); + } + } + + public static void Clear(this IMemoryGroup group) + where T : struct + { + foreach (Memory memory in group) + { + memory.Span.Clear(); + } + } + /// /// Returns a slice that is expected to be within the bounds of a single buffer. /// Otherwise is thrown. diff --git a/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs b/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs index a0112ce901..77e899a4c2 100644 --- a/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs +++ b/tests/ImageSharp.Tests/Memory/BufferAreaTests.cs @@ -9,22 +9,22 @@ namespace SixLabors.ImageSharp.Tests.Memory { public class BufferAreaTests { + private readonly TestMemoryAllocator memoryAllocator = new TestMemoryAllocator(); + [Fact] public void Construct() { - using (var buffer = Configuration.Default.MemoryAllocator.Allocate2D(10, 20)) - { - var rectangle = new Rectangle(3, 2, 5, 6); - var area = new BufferArea(buffer, rectangle); + using Buffer2D buffer = this.memoryAllocator.Allocate2D(10, 20); + var rectangle = new Rectangle(3, 2, 5, 6); + var area = new BufferArea(buffer, rectangle); - Assert.Equal(buffer, area.DestinationBuffer); - Assert.Equal(rectangle, area.Rectangle); - } + Assert.Equal(buffer, area.DestinationBuffer); + Assert.Equal(rectangle, area.Rectangle); } - private static Buffer2D CreateTestBuffer(int w, int h) + private Buffer2D CreateTestBuffer(int w, int h) { - var buffer = Configuration.Default.MemoryAllocator.Allocate2D(w, h); + Buffer2D buffer = this.memoryAllocator.Allocate2D(w, h); for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) @@ -37,110 +37,122 @@ namespace SixLabors.ImageSharp.Tests.Memory } [Theory] - [InlineData(2, 3, 2, 2)] - [InlineData(5, 4, 3, 2)] - public void Indexer(int rx, int ry, int x, int y) + [InlineData(1000, 2, 3, 2, 2)] + [InlineData(1000, 5, 4, 3, 2)] + [InlineData(200, 2, 3, 2, 2)] + [InlineData(200, 5, 4, 3, 2)] + public void Indexer(int bufferCapacity, int rx, int ry, int x, int y) { - using (Buffer2D buffer = CreateTestBuffer(20, 30)) - { - var r = new Rectangle(rx, ry, 5, 6); + this.memoryAllocator.BufferCapacityInBytes = sizeof(int) * bufferCapacity; + using Buffer2D buffer = this.CreateTestBuffer(20, 30); + var r = new Rectangle(rx, ry, 5, 6); - BufferArea area = buffer.GetArea(r); + BufferArea area = buffer.GetArea(r); - int value = area[x, y]; - int expected = ((ry + y) * 100) + rx + x; - Assert.Equal(expected, value); - } + int value = area[x, y]; + int expected = ((ry + y) * 100) + rx + x; + Assert.Equal(expected, value); } [Theory] - [InlineData(2, 3, 2, 5, 6)] - [InlineData(5, 4, 3, 6, 5)] - public void GetRowSpan(int rx, int ry, int y, int w, int h) + [InlineData(1000, 2, 3, 2, 5, 6)] + [InlineData(1000, 5, 4, 3, 6, 5)] + [InlineData(200, 2, 3, 2, 5, 6)] + [InlineData(200, 5, 4, 3, 6, 5)] + public void GetRowSpan(int bufferCapacity, int rx, int ry, int y, int w, int h) { - using (Buffer2D buffer = CreateTestBuffer(20, 30)) - { - var r = new Rectangle(rx, ry, w, h); + this.memoryAllocator.BufferCapacityInBytes = sizeof(int) * bufferCapacity; - BufferArea area = buffer.GetArea(r); + using Buffer2D buffer = this.CreateTestBuffer(20, 30); + var r = new Rectangle(rx, ry, w, h); - Span span = area.GetRowSpan(y); + BufferArea area = buffer.GetArea(r); - Assert.Equal(w, span.Length); + Span span = area.GetRowSpan(y); - for (int i = 0; i < w; i++) - { - int expected = ((ry + y) * 100) + rx + i; - int value = span[i]; + Assert.Equal(w, span.Length); - Assert.Equal(expected, value); - } + for (int i = 0; i < w; i++) + { + int expected = ((ry + y) * 100) + rx + i; + int value = span[i]; + + Assert.Equal(expected, value); } } [Fact] public void GetSubArea() { - using (Buffer2D buffer = CreateTestBuffer(20, 30)) - { - BufferArea area0 = buffer.GetArea(6, 8, 10, 10); + using Buffer2D buffer = this.CreateTestBuffer(20, 30); + BufferArea area0 = buffer.GetArea(6, 8, 10, 10); - BufferArea area1 = area0.GetSubArea(4, 4, 5, 5); + BufferArea area1 = area0.GetSubArea(4, 4, 5, 5); - var expectedRect = new Rectangle(10, 12, 5, 5); + var expectedRect = new Rectangle(10, 12, 5, 5); - Assert.Equal(buffer, area1.DestinationBuffer); - Assert.Equal(expectedRect, area1.Rectangle); + Assert.Equal(buffer, area1.DestinationBuffer); + Assert.Equal(expectedRect, area1.Rectangle); - int value00 = (12 * 100) + 10; - Assert.Equal(value00, area1[0, 0]); - } + int value00 = (12 * 100) + 10; + Assert.Equal(value00, area1[0, 0]); } - [Fact] - public void DangerousGetPinnableReference() + [Theory] + [InlineData(1000)] + [InlineData(40)] + public void GetReferenceToOrigin(int bufferCapacity) { - using (Buffer2D buffer = CreateTestBuffer(20, 30)) - { - BufferArea area0 = buffer.GetArea(6, 8, 10, 10); + this.memoryAllocator.BufferCapacityInBytes = sizeof(int) * bufferCapacity; - ref int r = ref area0.GetReferenceToOrigin(); + using Buffer2D buffer = this.CreateTestBuffer(20, 30); + BufferArea area0 = buffer.GetArea(6, 8, 10, 10); - int expected = buffer[6, 8]; - Assert.Equal(expected, r); - } + ref int r = ref area0.GetReferenceToOrigin(); + + int expected = buffer[6, 8]; + Assert.Equal(expected, r); } - [Fact] - public void Clear_FullArea() + [Theory] + [InlineData(1000)] + [InlineData(70)] + public void Clear_FullArea(int bufferCapacity) { - using (Buffer2D buffer = CreateTestBuffer(22, 13)) + this.memoryAllocator.BufferCapacityInBytes = sizeof(int) * bufferCapacity; + + using Buffer2D buffer = this.CreateTestBuffer(22, 13); + var emptyRow = new int[22]; + buffer.GetArea().Clear(); + + for (int y = 0; y < 13; y++) { - buffer.GetArea().Clear(); - Span fullSpan = buffer.GetSingleSpan(); - Assert.True(fullSpan.SequenceEqual(new int[fullSpan.Length])); + Span row = buffer.GetRowSpan(y); + Assert.True(row.SequenceEqual(emptyRow)); } } - [Fact] - public void Clear_SubArea() + [Theory] + [InlineData(1000)] + [InlineData(40)] + public void Clear_SubArea(int bufferCapacity) { - using (Buffer2D buffer = CreateTestBuffer(20, 30)) - { - BufferArea area = buffer.GetArea(5, 5, 10, 10); - area.Clear(); + this.memoryAllocator.BufferCapacityInBytes = sizeof(int) * bufferCapacity; - Assert.NotEqual(0, buffer[4, 4]); - Assert.NotEqual(0, buffer[15, 15]); + using Buffer2D buffer = this.CreateTestBuffer(20, 30); + BufferArea area = buffer.GetArea(5, 5, 10, 10); + area.Clear(); - Assert.Equal(0, buffer[5, 5]); - Assert.Equal(0, buffer[14, 14]); + Assert.NotEqual(0, buffer[4, 4]); + Assert.NotEqual(0, buffer[15, 15]); - for (int y = area.Rectangle.Y; y < area.Rectangle.Bottom; y++) - { - Span span = buffer.GetRowSpan(y).Slice(area.Rectangle.X, area.Width); - Assert.True(span.SequenceEqual(new int[area.Width])); - } + Assert.Equal(0, buffer[5, 5]); + Assert.Equal(0, buffer[14, 14]); + + for (int y = area.Rectangle.Y; y < area.Rectangle.Bottom; y++) + { + Span span = buffer.GetRowSpan(y).Slice(area.Rectangle.X, area.Width); + Assert.True(span.SequenceEqual(new int[area.Width])); } } } diff --git a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.cs b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.cs index bf081cb557..694c4d32f6 100644 --- a/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.cs +++ b/tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.cs @@ -3,6 +3,7 @@ using System; using System.Buffers; +using System.Linq; using System.Runtime.InteropServices; using SixLabors.ImageSharp.Memory; using Xunit; @@ -163,6 +164,32 @@ namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers Assert.ThrowsAny(() => group.GetBoundedSlice(start, length)); } + [Fact] + public void Fill() + { + using MemoryGroup group = this.CreateTestGroup(100, 10, true); + group.Fill(42); + + int[] expectedRow = Enumerable.Repeat(42, 10).ToArray(); + foreach (Memory memory in group) + { + Assert.True(memory.Span.SequenceEqual(expectedRow)); + } + } + + [Fact] + public void Clear() + { + using MemoryGroup group = this.CreateTestGroup(100, 10, true); + group.Clear(); + + var expectedRow = new int[10]; + foreach (Memory memory in group) + { + Assert.True(memory.Span.SequenceEqual(expectedRow)); + } + } + private static void MultiplyAllBy2(ReadOnlySpan source, Span target) { Assert.Equal(source.Length, target.Length);