Browse Source

robust BufferArea

af/octree-no-pixelmap
Anton Firszov 6 years ago
parent
commit
a3a0a3d1d8
  1. 24
      src/ImageSharp/Memory/BufferArea{T}.cs
  2. 18
      src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs
  3. 162
      tests/ImageSharp.Tests/Memory/BufferAreaTests.cs
  4. 27
      tests/ImageSharp.Tests/Memory/DiscontiguousBuffers/MemoryGroupTests.cs

24
src/ImageSharp/Memory/BufferArea{T}.cs

@ -72,7 +72,7 @@ namespace SixLabors.ImageSharp.Memory
/// <param name="x">The position inside a row</param>
/// <param name="y">The row index</param>
/// <returns>The reference to the value</returns>
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];
/// <summary>
/// Gets a reference to the [0,0] element.
@ -80,7 +80,7 @@ namespace SixLabors.ImageSharp.Memory
/// <returns>The reference to the [0,0] element</returns>
[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];
/// <summary>
/// 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;
}
/// <summary>
/// Returns a sub-area as <see cref="BufferArea{T}"/>. (Similar to <see cref="Span{T}.Slice(int, int)"/>.)
/// </summary>
/// <param name="x">The x index at the subarea origo</param>
/// <param name="y">The y index at the subarea origo</param>
/// <param name="width">The desired width of the subarea</param>
/// <param name="height">The desired height of the subarea</param>
/// <param name="x">The x index at the subarea origin.</param>
/// <param name="y">The y index at the subarea origin.</param>
/// <param name="width">The desired width of the subarea.</param>
/// <param name="height">The desired height of the subarea.</param>
/// <returns>The subarea</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public BufferArea<T> GetSubArea(int x, int y, int width, int height)
@ -129,14 +129,6 @@ namespace SixLabors.ImageSharp.Memory
return new BufferArea<T>(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;
}

18
src/ImageSharp/Memory/DiscontiguousBuffers/MemoryGroupExtensions.cs

@ -7,6 +7,24 @@ namespace SixLabors.ImageSharp.Memory
{
internal static class MemoryGroupExtensions
{
public static void Fill<T>(this IMemoryGroup<T> group, T value)
where T : struct
{
foreach (Memory<T> memory in group)
{
memory.Span.Fill(value);
}
}
public static void Clear<T>(this IMemoryGroup<T> group)
where T : struct
{
foreach (Memory<T> memory in group)
{
memory.Span.Clear();
}
}
/// <summary>
/// Returns a slice that is expected to be within the bounds of a single buffer.
/// Otherwise <see cref="ArgumentOutOfRangeException"/> is thrown.

162
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<int>(10, 20))
{
var rectangle = new Rectangle(3, 2, 5, 6);
var area = new BufferArea<int>(buffer, rectangle);
using Buffer2D<int> buffer = this.memoryAllocator.Allocate2D<int>(10, 20);
var rectangle = new Rectangle(3, 2, 5, 6);
var area = new BufferArea<int>(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<int> CreateTestBuffer(int w, int h)
private Buffer2D<int> CreateTestBuffer(int w, int h)
{
var buffer = Configuration.Default.MemoryAllocator.Allocate2D<int>(w, h);
Buffer2D<int> buffer = this.memoryAllocator.Allocate2D<int>(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<int> buffer = CreateTestBuffer(20, 30))
{
var r = new Rectangle(rx, ry, 5, 6);
this.memoryAllocator.BufferCapacityInBytes = sizeof(int) * bufferCapacity;
using Buffer2D<int> buffer = this.CreateTestBuffer(20, 30);
var r = new Rectangle(rx, ry, 5, 6);
BufferArea<int> area = buffer.GetArea(r);
BufferArea<int> 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<int> buffer = CreateTestBuffer(20, 30))
{
var r = new Rectangle(rx, ry, w, h);
this.memoryAllocator.BufferCapacityInBytes = sizeof(int) * bufferCapacity;
BufferArea<int> area = buffer.GetArea(r);
using Buffer2D<int> buffer = this.CreateTestBuffer(20, 30);
var r = new Rectangle(rx, ry, w, h);
Span<int> span = area.GetRowSpan(y);
BufferArea<int> area = buffer.GetArea(r);
Assert.Equal(w, span.Length);
Span<int> 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<int> buffer = CreateTestBuffer(20, 30))
{
BufferArea<int> area0 = buffer.GetArea(6, 8, 10, 10);
using Buffer2D<int> buffer = this.CreateTestBuffer(20, 30);
BufferArea<int> area0 = buffer.GetArea(6, 8, 10, 10);
BufferArea<int> area1 = area0.GetSubArea(4, 4, 5, 5);
BufferArea<int> 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<int> buffer = CreateTestBuffer(20, 30))
{
BufferArea<int> area0 = buffer.GetArea(6, 8, 10, 10);
this.memoryAllocator.BufferCapacityInBytes = sizeof(int) * bufferCapacity;
ref int r = ref area0.GetReferenceToOrigin();
using Buffer2D<int> buffer = this.CreateTestBuffer(20, 30);
BufferArea<int> 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<int> buffer = CreateTestBuffer(22, 13))
this.memoryAllocator.BufferCapacityInBytes = sizeof(int) * bufferCapacity;
using Buffer2D<int> buffer = this.CreateTestBuffer(22, 13);
var emptyRow = new int[22];
buffer.GetArea().Clear();
for (int y = 0; y < 13; y++)
{
buffer.GetArea().Clear();
Span<int> fullSpan = buffer.GetSingleSpan();
Assert.True(fullSpan.SequenceEqual(new int[fullSpan.Length]));
Span<int> 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<int> buffer = CreateTestBuffer(20, 30))
{
BufferArea<int> 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<int> buffer = this.CreateTestBuffer(20, 30);
BufferArea<int> 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<int> 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<int> span = buffer.GetRowSpan(y).Slice(area.Rectangle.X, area.Width);
Assert.True(span.SequenceEqual(new int[area.Width]));
}
}
}

27
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<ArgumentOutOfRangeException>(() => group.GetBoundedSlice(start, length));
}
[Fact]
public void Fill()
{
using MemoryGroup<int> group = this.CreateTestGroup(100, 10, true);
group.Fill(42);
int[] expectedRow = Enumerable.Repeat(42, 10).ToArray();
foreach (Memory<int> memory in group)
{
Assert.True(memory.Span.SequenceEqual(expectedRow));
}
}
[Fact]
public void Clear()
{
using MemoryGroup<int> group = this.CreateTestGroup(100, 10, true);
group.Clear();
var expectedRow = new int[10];
foreach (Memory<int> memory in group)
{
Assert.True(memory.Span.SequenceEqual(expectedRow));
}
}
private static void MultiplyAllBy2(ReadOnlySpan<int> source, Span<int> target)
{
Assert.Equal(source.Length, target.Length);

Loading…
Cancel
Save