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="x">The position inside a row</param>
/// <param name="y">The row index</param> /// <param name="y">The row index</param>
/// <returns>The reference to the value</returns> /// <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> /// <summary>
/// Gets a reference to the [0,0] element. /// 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> /// <returns>The reference to the [0,0] element</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T GetReferenceToOrigin() => public ref T GetReferenceToOrigin() =>
ref this.DestinationBuffer.GetSingleSpan()[(this.Rectangle.Y * this.DestinationBuffer.Width) + this.Rectangle.X]; ref this.GetRowSpan(0)[0];
/// <summary> /// <summary>
/// Gets a span to row 'y' inside this area. /// Gets a span to row 'y' inside this area.
@ -94,16 +94,16 @@ namespace SixLabors.ImageSharp.Memory
int xx = this.Rectangle.X; int xx = this.Rectangle.X;
int width = this.Rectangle.Width; int width = this.Rectangle.Width;
return this.DestinationBuffer.GetSingleSpan().Slice(yy + xx, width); return this.DestinationBuffer.MemoryGroup.GetBoundedSlice(yy + xx, width).Span;
} }
/// <summary> /// <summary>
/// Returns a sub-area as <see cref="BufferArea{T}"/>. (Similar to <see cref="Span{T}.Slice(int, int)"/>.) /// Returns a sub-area as <see cref="BufferArea{T}"/>. (Similar to <see cref="Span{T}.Slice(int, int)"/>.)
/// </summary> /// </summary>
/// <param name="x">The x index at the subarea origo</param> /// <param name="x">The x index at the subarea origin.</param>
/// <param name="y">The y index at the subarea origo</param> /// <param name="y">The y index at the subarea origin.</param>
/// <param name="width">The desired width of the subarea</param> /// <param name="width">The desired width of the subarea.</param>
/// <param name="height">The desired height of the subarea</param> /// <param name="height">The desired height of the subarea.</param>
/// <returns>The subarea</returns> /// <returns>The subarea</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public BufferArea<T> GetSubArea(int x, int y, int width, int height) 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); 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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
internal int GetRowIndex(int y) 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. // Optimization for when the size of the area is the same as the buffer size.
if (this.IsFullBufferArea) if (this.IsFullBufferArea)
{ {
this.DestinationBuffer.GetSingleSpan().Clear(); this.DestinationBuffer.MemoryGroup.Clear();
return; return;
} }

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

@ -7,6 +7,24 @@ namespace SixLabors.ImageSharp.Memory
{ {
internal static class MemoryGroupExtensions 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> /// <summary>
/// Returns a slice that is expected to be within the bounds of a single buffer. /// Returns a slice that is expected to be within the bounds of a single buffer.
/// Otherwise <see cref="ArgumentOutOfRangeException"/> is thrown. /// 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 public class BufferAreaTests
{ {
private readonly TestMemoryAllocator memoryAllocator = new TestMemoryAllocator();
[Fact] [Fact]
public void Construct() public void Construct()
{ {
using (var buffer = Configuration.Default.MemoryAllocator.Allocate2D<int>(10, 20)) using Buffer2D<int> buffer = this.memoryAllocator.Allocate2D<int>(10, 20);
{ var rectangle = new Rectangle(3, 2, 5, 6);
var rectangle = new Rectangle(3, 2, 5, 6); var area = new BufferArea<int>(buffer, rectangle);
var area = new BufferArea<int>(buffer, rectangle);
Assert.Equal(buffer, area.DestinationBuffer); Assert.Equal(buffer, area.DestinationBuffer);
Assert.Equal(rectangle, area.Rectangle); 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 y = 0; y < h; y++)
{ {
for (int x = 0; x < w; x++) for (int x = 0; x < w; x++)
@ -37,110 +37,122 @@ namespace SixLabors.ImageSharp.Tests.Memory
} }
[Theory] [Theory]
[InlineData(2, 3, 2, 2)] [InlineData(1000, 2, 3, 2, 2)]
[InlineData(5, 4, 3, 2)] [InlineData(1000, 5, 4, 3, 2)]
public void Indexer(int rx, int ry, int x, int y) [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)) this.memoryAllocator.BufferCapacityInBytes = sizeof(int) * bufferCapacity;
{ using Buffer2D<int> buffer = this.CreateTestBuffer(20, 30);
var r = new Rectangle(rx, ry, 5, 6); 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 value = area[x, y];
int expected = ((ry + y) * 100) + rx + x; int expected = ((ry + y) * 100) + rx + x;
Assert.Equal(expected, value); Assert.Equal(expected, value);
}
} }
[Theory] [Theory]
[InlineData(2, 3, 2, 5, 6)] [InlineData(1000, 2, 3, 2, 5, 6)]
[InlineData(5, 4, 3, 6, 5)] [InlineData(1000, 5, 4, 3, 6, 5)]
public void GetRowSpan(int rx, int ry, int y, int w, int h) [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)) this.memoryAllocator.BufferCapacityInBytes = sizeof(int) * bufferCapacity;
{
var r = new Rectangle(rx, ry, w, h);
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++) Assert.Equal(w, span.Length);
{
int expected = ((ry + y) * 100) + rx + i;
int value = span[i];
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] [Fact]
public void GetSubArea() public void GetSubArea()
{ {
using (Buffer2D<int> buffer = CreateTestBuffer(20, 30)) using Buffer2D<int> buffer = this.CreateTestBuffer(20, 30);
{ BufferArea<int> area0 = buffer.GetArea(6, 8, 10, 10);
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(buffer, area1.DestinationBuffer);
Assert.Equal(expectedRect, area1.Rectangle); Assert.Equal(expectedRect, area1.Rectangle);
int value00 = (12 * 100) + 10; int value00 = (12 * 100) + 10;
Assert.Equal(value00, area1[0, 0]); Assert.Equal(value00, area1[0, 0]);
}
} }
[Fact] [Theory]
public void DangerousGetPinnableReference() [InlineData(1000)]
[InlineData(40)]
public void GetReferenceToOrigin(int bufferCapacity)
{ {
using (Buffer2D<int> buffer = CreateTestBuffer(20, 30)) this.memoryAllocator.BufferCapacityInBytes = sizeof(int) * bufferCapacity;
{
BufferArea<int> area0 = buffer.GetArea(6, 8, 10, 10);
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]; ref int r = ref area0.GetReferenceToOrigin();
Assert.Equal(expected, r);
} int expected = buffer[6, 8];
Assert.Equal(expected, r);
} }
[Fact] [Theory]
public void Clear_FullArea() [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> row = buffer.GetRowSpan(y);
Span<int> fullSpan = buffer.GetSingleSpan(); Assert.True(row.SequenceEqual(emptyRow));
Assert.True(fullSpan.SequenceEqual(new int[fullSpan.Length]));
} }
} }
[Fact] [Theory]
public void Clear_SubArea() [InlineData(1000)]
[InlineData(40)]
public void Clear_SubArea(int bufferCapacity)
{ {
using (Buffer2D<int> buffer = CreateTestBuffer(20, 30)) this.memoryAllocator.BufferCapacityInBytes = sizeof(int) * bufferCapacity;
{
BufferArea<int> area = buffer.GetArea(5, 5, 10, 10);
area.Clear();
Assert.NotEqual(0, buffer[4, 4]); using Buffer2D<int> buffer = this.CreateTestBuffer(20, 30);
Assert.NotEqual(0, buffer[15, 15]); BufferArea<int> area = buffer.GetArea(5, 5, 10, 10);
area.Clear();
Assert.Equal(0, buffer[5, 5]); Assert.NotEqual(0, buffer[4, 4]);
Assert.Equal(0, buffer[14, 14]); Assert.NotEqual(0, buffer[15, 15]);
for (int y = area.Rectangle.Y; y < area.Rectangle.Bottom; y++) Assert.Equal(0, buffer[5, 5]);
{ Assert.Equal(0, buffer[14, 14]);
Span<int> span = buffer.GetRowSpan(y).Slice(area.Rectangle.X, area.Width);
Assert.True(span.SequenceEqual(new int[area.Width])); 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;
using System.Buffers; using System.Buffers;
using System.Linq;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Memory;
using Xunit; using Xunit;
@ -163,6 +164,32 @@ namespace SixLabors.ImageSharp.Tests.Memory.DiscontiguousBuffers
Assert.ThrowsAny<ArgumentOutOfRangeException>(() => group.GetBoundedSlice(start, length)); 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) private static void MultiplyAllBy2(ReadOnlySpan<int> source, Span<int> target)
{ {
Assert.Equal(source.Length, target.Length); Assert.Equal(source.Length, target.Length);

Loading…
Cancel
Save