mirror of https://github.com/SixLabors/ImageSharp
5 changed files with 185 additions and 31 deletions
@ -0,0 +1,63 @@ |
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
using SixLabors.Primitives; |
|||
|
|||
namespace SixLabors.ImageSharp.Memory |
|||
{ |
|||
/// <summary>
|
|||
/// Represents a rectangular area inside a 2D memory buffer (<see cref="Buffer2D{T}"/>).
|
|||
/// This type is kind-of 2D Span, but it can live on heap.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The element type</typeparam>
|
|||
internal struct BufferArea<T> |
|||
where T : struct |
|||
{ |
|||
public readonly Rectangle Rectangle; |
|||
|
|||
public BufferArea(IBuffer2D<T> destinationBuffer, Rectangle rectangle) |
|||
{ |
|||
Guard.MustBeGreaterThanOrEqualTo(rectangle.X, 0, nameof(rectangle)); |
|||
Guard.MustBeGreaterThanOrEqualTo(rectangle.Y, 0, nameof(rectangle)); |
|||
Guard.MustBeLessThan(rectangle.Width, destinationBuffer.Width, nameof(rectangle)); |
|||
Guard.MustBeLessThan(rectangle.Height, destinationBuffer.Height, nameof(rectangle)); |
|||
|
|||
this.DestinationBuffer = destinationBuffer; |
|||
this.Rectangle = rectangle; |
|||
} |
|||
|
|||
public BufferArea(IBuffer2D<T> destinationBuffer) |
|||
: this(destinationBuffer, destinationBuffer.FullRectangle()) |
|||
{ |
|||
} |
|||
|
|||
public IBuffer2D<T> DestinationBuffer { get; } |
|||
|
|||
public Size Size => this.Rectangle.Size; |
|||
|
|||
public ref T this[int x, int y] => ref this.DestinationBuffer.Span[this.GetIndexOf(x, y)]; |
|||
|
|||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|||
public Span<T> GetRowSpan(int y) |
|||
{ |
|||
int yy = this.GetRowIndex(y); |
|||
int xx = this.Rectangle.X; |
|||
int width = this.Rectangle.Width; |
|||
|
|||
return this.DestinationBuffer.Span.Slice(yy + xx, width); |
|||
} |
|||
|
|||
[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)] |
|||
private int GetRowIndex(int y) |
|||
{ |
|||
return (y + this.Rectangle.Y) * this.DestinationBuffer.Width; |
|||
} |
|||
} |
|||
} |
|||
@ -1,30 +0,0 @@ |
|||
using SixLabors.Primitives; |
|||
|
|||
namespace SixLabors.ImageSharp.Memory |
|||
{ |
|||
/// <summary>
|
|||
/// Represents a rectangular area inside a 2D memory buffer. (Most commonly <see cref="Buffer2D{T}"/>)
|
|||
/// This type is kind-of 2D Span.
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The element type</typeparam>
|
|||
internal struct BufferArea2D<T> |
|||
where T : struct |
|||
{ |
|||
public IBuffer2D<T> DestinationBuffer { get; } |
|||
|
|||
public readonly Rectangle Rectangle; |
|||
|
|||
public BufferArea2D(IBuffer2D<T> destinationBuffer, Rectangle rectangle) |
|||
{ |
|||
this.DestinationBuffer = destinationBuffer; |
|||
this.Rectangle = rectangle; |
|||
} |
|||
|
|||
public BufferArea2D(Buffer2D<T> destinationBuffer) |
|||
: this(destinationBuffer, destinationBuffer.FullRectangle()) |
|||
{ |
|||
} |
|||
|
|||
public Size Size => this.Rectangle.Size; |
|||
} |
|||
} |
|||
@ -0,0 +1,102 @@ |
|||
// ReSharper disable InconsistentNaming
|
|||
namespace SixLabors.ImageSharp.Tests.Memory |
|||
{ |
|||
using System; |
|||
|
|||
using SixLabors.ImageSharp.Memory; |
|||
using SixLabors.Primitives; |
|||
|
|||
using Xunit; |
|||
|
|||
public class BufferAreaTests |
|||
{ |
|||
[Fact] |
|||
public void Construct() |
|||
{ |
|||
using (var buffer = new Buffer2D<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); |
|||
} |
|||
} |
|||
|
|||
private static Buffer2D<int> CreateTestBuffer(int w, int h) |
|||
{ |
|||
var buffer = new Buffer2D<int>(w, h); |
|||
for (int y = 0; y < h; y++) |
|||
{ |
|||
for (int x = 0; x < w; x++) |
|||
{ |
|||
buffer[x, y] = y * 100 + x; |
|||
} |
|||
} |
|||
return buffer; |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(-1, 1, 0, 0)] |
|||
[InlineData(1, -1, 0, 0)] |
|||
[InlineData(0, 0, 1, 0)] |
|||
[InlineData(0, 0, 0, 42)] |
|||
public void Construct_WhenRectangleIsOutsideOfBufferBoundaries_Throws(int dx, int dy, int dWidth, int dHeight) |
|||
{ |
|||
using (var buffer = new Buffer2D<int>(10, 20)) |
|||
{ |
|||
Rectangle r = buffer.FullRectangle(); |
|||
|
|||
r = new Rectangle(r.X+dx, r.Y+dy, r.Width + dWidth, r.Height + dHeight ); |
|||
|
|||
Assert.ThrowsAny<ArgumentException>( |
|||
() => |
|||
{ |
|||
var area = new BufferArea<int>(buffer, r); |
|||
}); |
|||
} |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(2, 3, 2, 2)] |
|||
[InlineData(5, 4, 3, 2)] |
|||
public void Indexer(int rx, int ry, int x, int y) |
|||
{ |
|||
using (Buffer2D<int> buffer = CreateTestBuffer(20, 30)) |
|||
{ |
|||
Rectangle r = new Rectangle(rx, ry, 5, 6); |
|||
|
|||
BufferArea<int> area = buffer.GetArea(r); |
|||
|
|||
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) |
|||
{ |
|||
using (Buffer2D<int> buffer = CreateTestBuffer(20, 30)) |
|||
{ |
|||
Rectangle r = new Rectangle(rx, ry, w, h); |
|||
|
|||
BufferArea<int> area = buffer.GetArea(r); |
|||
|
|||
Span<int> span = area.GetRowSpan(y); |
|||
|
|||
Assert.Equal(w, span.Length); |
|||
|
|||
for (int i = 0; i < w; i++) |
|||
{ |
|||
int expected = (ry + y) * 100 + rx + i; |
|||
int value = span[i]; |
|||
|
|||
Assert.Equal(expected, value); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue