Browse Source

PinnedBuffer<T> ==> Buffer<T> with explicit pinning capability

pull/174/head
Anton Firszov 9 years ago
parent
commit
615163d360
  1. 2
      src/ImageSharp.Drawing/Brushes/ImageBrush{TColor}.cs
  2. 2
      src/ImageSharp.Drawing/Brushes/PatternBrush{TColor}.cs
  3. 2
      src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs
  4. 2
      src/ImageSharp.Drawing/Brushes/RecolorBrush{TColor}.cs
  5. 2
      src/ImageSharp.Drawing/Brushes/SolidBrush{TColor}.cs
  6. 2
      src/ImageSharp/Colors/Color.BulkOperations.cs
  7. 89
      src/ImageSharp/Common/Memory/Buffer.cs
  8. 22
      src/ImageSharp/Common/Memory/Buffer2D.cs
  9. 2
      src/ImageSharp/Common/Memory/BufferSpan{T}.cs
  10. 4
      src/ImageSharp/Common/Memory/IBuffer2D.cs
  11. 6
      src/ImageSharp/Common/Memory/PinnedImageBufferExtensions.cs
  12. 18
      src/ImageSharp/Image/PixelAccessor{TColor}.cs
  13. 6
      src/ImageSharp/Image/PixelArea{TColor}.cs
  14. 6
      src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs
  15. 4
      src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs
  16. 14
      tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs
  17. 8
      tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs
  18. 8
      tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs
  19. 8
      tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs
  20. 8
      tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs
  21. 6
      tests/ImageSharp.Benchmarks/General/ClearBuffer.cs
  22. 7
      tests/ImageSharp.Benchmarks/General/IterateArray.cs
  23. 10
      tests/ImageSharp.Benchmarks/General/PixelIndexing.cs
  24. 16
      tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs
  25. 27
      tests/ImageSharp.Tests/Common/Buffer2DTests.cs
  26. 6
      tests/ImageSharp.Tests/Common/BufferSpanTests.cs
  27. 93
      tests/ImageSharp.Tests/Common/BufferTests.cs

2
src/ImageSharp.Drawing/Brushes/ImageBrush{TColor}.cs

@ -117,7 +117,7 @@ namespace ImageSharp.Drawing.Brushes
{ {
Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth)); Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth));
using (PinnedBuffer<float> buffer = new PinnedBuffer<float>(scanlineBuffer)) using (Buffer<float> buffer = new Buffer<float>(scanlineBuffer))
{ {
BufferSpan<float> slice = buffer.Slice(offset); BufferSpan<float> slice = buffer.Slice(offset);

2
src/ImageSharp.Drawing/Brushes/PatternBrush{TColor}.cs

@ -150,7 +150,7 @@ namespace ImageSharp.Drawing.Brushes
{ {
Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth)); Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth));
using (PinnedBuffer<float> buffer = new PinnedBuffer<float>(scanlineBuffer)) using (Buffer<float> buffer = new Buffer<float>(scanlineBuffer))
{ {
BufferSpan<float> slice = buffer.Slice(offset); BufferSpan<float> slice = buffer.Slice(offset);

2
src/ImageSharp.Drawing/Brushes/Processors/BrushApplicator.cs

@ -55,7 +55,7 @@ namespace ImageSharp.Drawing.Processors
{ {
DebugGuard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth)); DebugGuard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth));
using (PinnedBuffer<float> buffer = new PinnedBuffer<float>(scanlineBuffer)) using (Buffer<float> buffer = new Buffer<float>(scanlineBuffer))
{ {
BufferSpan<float> slice = buffer.Slice(offset); BufferSpan<float> slice = buffer.Slice(offset);

2
src/ImageSharp.Drawing/Brushes/RecolorBrush{TColor}.cs

@ -141,7 +141,7 @@ namespace ImageSharp.Drawing.Brushes
{ {
Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth)); Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth));
using (PinnedBuffer<float> buffer = new PinnedBuffer<float>(scanlineBuffer)) using (Buffer<float> buffer = new Buffer<float>(scanlineBuffer))
{ {
BufferSpan<float> slice = buffer.Slice(offset); BufferSpan<float> slice = buffer.Slice(offset);

2
src/ImageSharp.Drawing/Brushes/SolidBrush{TColor}.cs

@ -89,7 +89,7 @@ namespace ImageSharp.Drawing.Brushes
{ {
Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth)); Guard.MustBeGreaterThanOrEqualTo(scanlineBuffer.Length, offset + scanlineWidth, nameof(scanlineWidth));
using (PinnedBuffer<float> buffer = new PinnedBuffer<float>(scanlineBuffer)) using (Buffer<float> buffer = new Buffer<float>(scanlineBuffer))
{ {
BufferSpan<float> slice = buffer.Slice(offset); BufferSpan<float> slice = buffer.Slice(offset);

2
src/ImageSharp/Colors/Color.BulkOperations.cs

@ -64,7 +64,7 @@ namespace ImageSharp
ref uint src = ref Unsafe.As<Color, uint>(ref sourceColors.DangerousGetPinnableReference()); ref uint src = ref Unsafe.As<Color, uint>(ref sourceColors.DangerousGetPinnableReference());
using (PinnedBuffer<uint> tempBuf = new PinnedBuffer<uint>( using (Buffer<uint> tempBuf = new Buffer<uint>(
unpackedRawCount + Vector<uint>.Count)) unpackedRawCount + Vector<uint>.Count))
{ {
uint[] temp = tempBuf.Array; uint[] temp = tempBuf.Array;

89
src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs → src/ImageSharp/Common/Memory/Buffer.cs

@ -1,4 +1,4 @@
// <copyright file="PinnedBuffer{T}.cs" company="James Jackson-South"> // <copyright file="Buffer{T}.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
@ -11,13 +11,18 @@ namespace ImageSharp
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
/// <summary> /// <summary>
/// Manages a pinned buffer of value type objects as a Disposable resource. /// Manages a buffer of value type objects as a Disposable resource.
/// The backing array is either pooled or comes from the outside. /// The backing array is either pooled or comes from the outside.
/// </summary> /// </summary>
/// <typeparam name="T">The value type.</typeparam> /// <typeparam name="T">The value type.</typeparam>
internal class PinnedBuffer<T> : IDisposable internal class Buffer<T> : IDisposable
where T : struct where T : struct
{ {
/// <summary>
/// A pointer to the first element of <see cref="Array"/> when pinned.
/// </summary>
private IntPtr pointer;
/// <summary> /// <summary>
/// A handle that allows to access the managed <see cref="Array"/> as an unmanaged memory by pinning. /// A handle that allows to access the managed <see cref="Array"/> as an unmanaged memory by pinning.
/// </summary> /// </summary>
@ -25,40 +30,38 @@ namespace ImageSharp
/// <summary> /// <summary>
/// A value indicating wheter <see cref="Array"/> should be returned to <see cref="PixelDataPool{T}"/> /// A value indicating wheter <see cref="Array"/> should be returned to <see cref="PixelDataPool{T}"/>
/// when disposing this <see cref="PinnedBuffer{T}"/> instance. /// when disposing this <see cref="Buffer{T}"/> instance.
/// </summary> /// </summary>
private bool isPoolingOwner; private bool isPoolingOwner;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PinnedBuffer{T}"/> class. /// Initializes a new instance of the <see cref="Buffer{T}"/> class.
/// </summary> /// </summary>
/// <param name="length">The desired count of elements. (Minimum size for <see cref="Array"/>)</param> /// <param name="length">The desired count of elements. (Minimum size for <see cref="Array"/>)</param>
public PinnedBuffer(int length) public Buffer(int length)
{ {
this.Length = length; this.Length = length;
this.Array = PixelDataPool<T>.Rent(length); this.Array = PixelDataPool<T>.Rent(length);
this.isPoolingOwner = true; this.isPoolingOwner = true;
this.Pin();
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PinnedBuffer{T}"/> class. /// Initializes a new instance of the <see cref="Buffer{T}"/> class.
/// </summary> /// </summary>
/// <param name="array">The array to pin.</param> /// <param name="array">The array to pin.</param>
public PinnedBuffer(T[] array) public Buffer(T[] array)
{ {
this.Length = array.Length; this.Length = array.Length;
this.Array = array; this.Array = array;
this.isPoolingOwner = false; this.isPoolingOwner = false;
this.Pin();
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PinnedBuffer{T}"/> class. /// Initializes a new instance of the <see cref="Buffer{T}"/> class.
/// </summary> /// </summary>
/// <param name="array">The array to pin.</param> /// <param name="array">The array to pin.</param>
/// <param name="length">The count of "relevant" elements in 'array'.</param> /// <param name="length">The count of "relevant" elements in 'array'.</param>
public PinnedBuffer(T[] array, int length) public Buffer(T[] array, int length)
{ {
if (array.Length < length) if (array.Length < length)
{ {
@ -68,19 +71,18 @@ namespace ImageSharp
this.Length = length; this.Length = length;
this.Array = array; this.Array = array;
this.isPoolingOwner = false; this.isPoolingOwner = false;
this.Pin();
} }
/// <summary> /// <summary>
/// Finalizes an instance of the <see cref="PinnedBuffer{T}"/> class. /// Finalizes an instance of the <see cref="Buffer{T}"/> class.
/// </summary> /// </summary>
~PinnedBuffer() ~Buffer()
{ {
this.UnPin(); this.UnPin();
} }
/// <summary> /// <summary>
/// Gets a value indicating whether this <see cref="PinnedBuffer{T}"/> instance is disposed, or has lost ownership of <see cref="Array"/>. /// Gets a value indicating whether this <see cref="Buffer{T}"/> instance is disposed, or has lost ownership of <see cref="Array"/>.
/// </summary> /// </summary>
public bool IsDisposedOrLostArrayOwnership { get; private set; } public bool IsDisposedOrLostArrayOwnership { get; private set; }
@ -94,11 +96,6 @@ namespace ImageSharp
/// </summary> /// </summary>
public T[] Array { get; private set; } public T[] Array { get; private set; }
/// <summary>
/// Gets a pointer to the pinned <see cref="Array"/>.
/// </summary>
public IntPtr Pointer { get; private set; }
/// <summary> /// <summary>
/// Gets a <see cref="BufferSpan{T}"/> to the backing buffer. /// Gets a <see cref="BufferSpan{T}"/> to the backing buffer.
/// </summary> /// </summary>
@ -109,37 +106,35 @@ namespace ImageSharp
/// </summary> /// </summary>
/// <param name="index">The index</param> /// <param name="index">The index</param>
/// <returns>The reference to the specified element</returns> /// <returns>The reference to the specified element</returns>
public unsafe ref T this[int index] public ref T this[int index]
{ {
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
get get
{ {
DebugGuard.MustBeLessThan(index, this.Length, nameof(index)); DebugGuard.MustBeLessThan(index, this.Length, nameof(index));
return ref this.Array[index];
byte* ptr = (byte*)this.Pointer + BufferSpan.SizeOf<T>(index);
return ref Unsafe.AsRef<T>(ptr);
} }
} }
/// <summary> /// <summary>
/// Converts <see cref="PinnedBuffer{T}"/> to an <see cref="BufferSpan{T}"/>. /// Converts <see cref="Buffer{T}"/> to an <see cref="BufferSpan{T}"/>.
/// </summary> /// </summary>
/// <param name="buffer">The <see cref="PinnedBuffer{T}"/> to convert.</param> /// <param name="buffer">The <see cref="Buffer{T}"/> to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator BufferSpan<T>(PinnedBuffer<T> buffer) public static implicit operator BufferSpan<T>(Buffer<T> buffer)
{ {
return new BufferSpan<T>(buffer.Array, 0, buffer.Length); return new BufferSpan<T>(buffer.Array, 0, buffer.Length);
} }
/// <summary> /// <summary>
/// Creates a clean instance of <see cref="PinnedBuffer{T}"/> initializing it's elements with 'default(T)'. /// Creates a clean instance of <see cref="Buffer{T}"/> initializing it's elements with 'default(T)'.
/// </summary> /// </summary>
/// <param name="count">The desired count of elements. (Minimum size for <see cref="Array"/>)</param> /// <param name="count">The desired count of elements. (Minimum size for <see cref="Array"/>)</param>
/// <returns>The <see cref="PinnedBuffer{T}"/> instance</returns> /// <returns>The <see cref="Buffer{T}"/> instance</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static PinnedBuffer<T> CreateClean(int count) public static Buffer<T> CreateClean(int count)
{ {
PinnedBuffer<T> buffer = new PinnedBuffer<T>(count); Buffer<T> buffer = new Buffer<T>(count);
buffer.Clear(); buffer.Clear();
return buffer; return buffer;
} }
@ -168,7 +163,7 @@ namespace ImageSharp
} }
/// <summary> /// <summary>
/// Disposes the <see cref="PinnedBuffer{T}"/> instance by unpinning the array, and returning the pooled buffer when necessary. /// Disposes the <see cref="Buffer{T}"/> instance by unpinning the array, and returning the pooled buffer when necessary.
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose() public void Dispose()
@ -199,11 +194,11 @@ namespace ImageSharp
/// </summary> /// </summary>
/// <returns>The unpinned <see cref="Array"/></returns> /// <returns>The unpinned <see cref="Array"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public T[] UnPinAndTakeArrayOwnership() public T[] TakeArrayOwnership()
{ {
if (this.IsDisposedOrLostArrayOwnership) if (this.IsDisposedOrLostArrayOwnership)
{ {
throw new InvalidOperationException("UnPinAndTakeArrayOwnership() is invalid: either PinnedBuffer<T> is disposed or UnPinAndTakeArrayOwnership() has been called multiple times!"); throw new InvalidOperationException("TakeArrayOwnership() is invalid: either Buffer<T> is disposed or TakeArrayOwnership() has been called multiple times!");
} }
this.IsDisposedOrLostArrayOwnership = true; this.IsDisposedOrLostArrayOwnership = true;
@ -220,17 +215,29 @@ namespace ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear() public void Clear()
{ {
((BufferSpan<T>)this).Clear(); this.Span.Clear();
} }
/// <summary> /// <summary>
/// Pins <see cref="Array"/>. /// Pins <see cref="Array"/>.
/// </summary> /// </summary>
/// <returns>The pinned pointer</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Pin() public IntPtr Pin()
{ {
this.handle = GCHandle.Alloc(this.Array, GCHandleType.Pinned); if (this.IsDisposedOrLostArrayOwnership)
this.Pointer = this.handle.AddrOfPinnedObject(); {
throw new InvalidOperationException(
"Pin() is invalid on a buffer with IsDisposedOrLostArrayOwnership == true!");
}
if (this.pointer == IntPtr.Zero)
{
this.handle = GCHandle.Alloc(this.Array, GCHandleType.Pinned);
this.pointer = this.handle.AddrOfPinnedObject();
}
return this.pointer;
} }
/// <summary> /// <summary>
@ -239,13 +246,13 @@ namespace ImageSharp
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void UnPin() private void UnPin()
{ {
if (this.Pointer == IntPtr.Zero || !this.handle.IsAllocated) if (this.pointer == IntPtr.Zero || !this.handle.IsAllocated)
{ {
return; return;
} }
this.handle.Free(); this.handle.Free();
this.Pointer = IntPtr.Zero; this.pointer = IntPtr.Zero;
} }
} }
} }

22
src/ImageSharp/Common/Memory/PinnedImageBuffer{T}.cs → src/ImageSharp/Common/Memory/Buffer2D.cs

@ -1,4 +1,4 @@
// <copyright file="PinnedImageBuffer{T}.cs" company="James Jackson-South"> // <copyright file="Buffer2D{T}.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
@ -9,19 +9,19 @@ namespace ImageSharp
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
/// <summary> /// <summary>
/// Represents a pinned buffer of value type objects /// Represents a buffer of value type objects
/// interpreted as a 2D region of <see cref="Width"/> x <see cref="Height"/> elements. /// interpreted as a 2D region of <see cref="Width"/> x <see cref="Height"/> elements.
/// </summary> /// </summary>
/// <typeparam name="T">The value type.</typeparam> /// <typeparam name="T">The value type.</typeparam>
internal class PinnedImageBuffer<T> : PinnedBuffer<T>, IPinnedImageBuffer<T> internal class Buffer2D<T> : Buffer<T>, IBuffer2D<T>
where T : struct where T : struct
{ {
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PinnedImageBuffer{T}"/> class. /// Initializes a new instance of the <see cref="Buffer2D{T}"/> class.
/// </summary> /// </summary>
/// <param name="width">The number of elements in a row</param> /// <param name="width">The number of elements in a row</param>
/// <param name="height">The number of rows</param> /// <param name="height">The number of rows</param>
public PinnedImageBuffer(int width, int height) public Buffer2D(int width, int height)
: base(width * height) : base(width * height)
{ {
this.Width = width; this.Width = width;
@ -29,12 +29,12 @@ namespace ImageSharp
} }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PinnedImageBuffer{T}"/> class. /// Initializes a new instance of the <see cref="Buffer2D{T}"/> class.
/// </summary> /// </summary>
/// <param name="array">The array to pin</param> /// <param name="array">The array to pin</param>
/// <param name="width">The number of elements in a row</param> /// <param name="width">The number of elements in a row</param>
/// <param name="height">The number of rows</param> /// <param name="height">The number of rows</param>
public PinnedImageBuffer(T[] array, int width, int height) public Buffer2D(T[] array, int width, int height)
: base(array, width * height) : base(array, width * height)
{ {
this.Width = width; this.Width = width;
@ -63,14 +63,14 @@ namespace ImageSharp
} }
/// <summary> /// <summary>
/// Creates a clean instance of <see cref="PinnedImageBuffer{T}"/> initializing it's elements with 'default(T)'. /// Creates a clean instance of <see cref="Buffer2D{T}"/> initializing it's elements with 'default(T)'.
/// </summary> /// </summary>
/// <param name="width">The number of elements in a row</param> /// <param name="width">The number of elements in a row</param>
/// <param name="height">The number of rows</param> /// <param name="height">The number of rows</param>
/// <returns>The <see cref="PinnedBuffer{T}"/> instance</returns> /// <returns>The <see cref="Buffer{T}"/> instance</returns>
public static PinnedImageBuffer<T> CreateClean(int width, int height) public static Buffer2D<T> CreateClean(int width, int height)
{ {
PinnedImageBuffer<T> buffer = new PinnedImageBuffer<T>(width, height); Buffer2D<T> buffer = new Buffer2D<T>(width, height);
buffer.Clear(); buffer.Clear();
return buffer; return buffer;
} }

2
src/ImageSharp/Common/Memory/BufferSpan{T}.cs

@ -12,7 +12,7 @@ namespace ImageSharp
/// <summary> /// <summary>
/// Represents a contiguous region of a pinned managed array. /// Represents a contiguous region of a pinned managed array.
/// The array is usually owned by a <see cref="PinnedBuffer{T}"/> instance. /// The array is usually owned by a <see cref="Buffer{T}"/> instance.
/// </summary> /// </summary>
/// <remarks> /// <remarks>
/// <see cref="BufferSpan{T}"/> is very similar to corefx System.Span&lt;T&gt;, and we try to maintain a compatible API. /// <see cref="BufferSpan{T}"/> is very similar to corefx System.Span&lt;T&gt;, and we try to maintain a compatible API.

4
src/ImageSharp/Common/Memory/IPinnedImageBuffer{T}.cs → src/ImageSharp/Common/Memory/IBuffer2D.cs

@ -1,4 +1,4 @@
// <copyright file="IPinnedImageBuffer{T}.cs" company="James Jackson-South"> // <copyright file="IBuffer2D{T}.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
@ -10,7 +10,7 @@ namespace ImageSharp
/// interpreted as a 2D region of <see cref="Width"/> x <see cref="Height"/> elements. /// interpreted as a 2D region of <see cref="Width"/> x <see cref="Height"/> elements.
/// </summary> /// </summary>
/// <typeparam name="T">The value type.</typeparam> /// <typeparam name="T">The value type.</typeparam>
internal interface IPinnedImageBuffer<T> internal interface IBuffer2D<T>
where T : struct where T : struct
{ {
/// <summary> /// <summary>

6
src/ImageSharp/Common/Memory/PinnedImageBufferExtensions.cs

@ -9,7 +9,7 @@ namespace ImageSharp
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
/// <summary> /// <summary>
/// Defines extension methods for <see cref="IPinnedImageBuffer{T}"/>. /// Defines extension methods for <see cref="IBuffer2D{T}"/>.
/// </summary> /// </summary>
internal static class PinnedImageBufferExtensions internal static class PinnedImageBufferExtensions
{ {
@ -22,7 +22,7 @@ namespace ImageSharp
/// <typeparam name="T">The element type</typeparam> /// <typeparam name="T">The element type</typeparam>
/// <returns>The <see cref="BufferSpan{T}"/></returns> /// <returns>The <see cref="BufferSpan{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BufferSpan<T> GetRowSpan<T>(this IPinnedImageBuffer<T> buffer, int x, int y) public static BufferSpan<T> GetRowSpan<T>(this IBuffer2D<T> buffer, int x, int y)
where T : struct where T : struct
{ {
return buffer.Span.Slice((y * buffer.Width) + x, buffer.Width - x); return buffer.Span.Slice((y * buffer.Width) + x, buffer.Width - x);
@ -36,7 +36,7 @@ namespace ImageSharp
/// <typeparam name="T">The element type</typeparam> /// <typeparam name="T">The element type</typeparam>
/// <returns>The <see cref="BufferSpan{T}"/></returns> /// <returns>The <see cref="BufferSpan{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BufferSpan<T> GetRowSpan<T>(this IPinnedImageBuffer<T> buffer, int y) public static BufferSpan<T> GetRowSpan<T>(this IBuffer2D<T> buffer, int y)
where T : struct where T : struct
{ {
return buffer.Span.Slice(y * buffer.Width, buffer.Width); return buffer.Span.Slice(y * buffer.Width, buffer.Width);

18
src/ImageSharp/Image/PixelAccessor{TColor}.cs

@ -15,7 +15,7 @@ namespace ImageSharp
/// Provides per-pixel access to generic <see cref="Image{TColor}"/> pixels. /// Provides per-pixel access to generic <see cref="Image{TColor}"/> pixels.
/// </summary> /// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam> /// <typeparam name="TColor">The pixel format.</typeparam>
public sealed class PixelAccessor<TColor> : IDisposable, IPinnedImageBuffer<TColor> public sealed class PixelAccessor<TColor> : IDisposable, IBuffer2D<TColor>
where TColor : struct, IPixel<TColor> where TColor : struct, IPixel<TColor>
{ {
/// <summary> /// <summary>
@ -30,9 +30,9 @@ namespace ImageSharp
private bool isDisposed; private bool isDisposed;
/// <summary> /// <summary>
/// The <see cref="PinnedBuffer{T}"/> containing the pixel data. /// The <see cref="Buffer{T}"/> containing the pixel data.
/// </summary> /// </summary>
private PinnedImageBuffer<TColor> pixelBuffer; private Buffer2D<TColor> pixelBuffer;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PixelAccessor{TColor}"/> class. /// Initializes a new instance of the <see cref="PixelAccessor{TColor}"/> class.
@ -54,7 +54,7 @@ namespace ImageSharp
/// <param name="width">The width of the image represented by the pixel buffer.</param> /// <param name="width">The width of the image represented by the pixel buffer.</param>
/// <param name="height">The height of the image represented by the pixel buffer.</param> /// <param name="height">The height of the image represented by the pixel buffer.</param>
public PixelAccessor(int width, int height) public PixelAccessor(int width, int height)
: this(width, height, PinnedImageBuffer<TColor>.CreateClean(width, height)) : this(width, height, Buffer2D<TColor>.CreateClean(width, height))
{ {
} }
@ -64,7 +64,7 @@ namespace ImageSharp
/// <param name="width">The width of the image represented by the pixel buffer.</param> /// <param name="width">The width of the image represented by the pixel buffer.</param>
/// <param name="height">The height of the image represented by the pixel buffer.</param> /// <param name="height">The height of the image represented by the pixel buffer.</param>
/// <param name="pixels">The pixel buffer.</param> /// <param name="pixels">The pixel buffer.</param>
private PixelAccessor(int width, int height, PinnedImageBuffer<TColor> pixels) private PixelAccessor(int width, int height, Buffer2D<TColor> pixels)
{ {
Guard.NotNull(pixels, nameof(pixels)); Guard.NotNull(pixels, nameof(pixels));
Guard.MustBeGreaterThan(width, 0, nameof(width)); Guard.MustBeGreaterThan(width, 0, nameof(width));
@ -114,7 +114,7 @@ namespace ImageSharp
public ParallelOptions ParallelOptions { get; } public ParallelOptions ParallelOptions { get; }
/// <inheritdoc /> /// <inheritdoc />
BufferSpan<TColor> IPinnedImageBuffer<TColor>.Span => this.pixelBuffer; BufferSpan<TColor> IBuffer2D<TColor>.Span => this.pixelBuffer;
private static BulkPixelOperations<TColor> Operations => BulkPixelOperations<TColor>.Instance; private static BulkPixelOperations<TColor> Operations => BulkPixelOperations<TColor>.Instance;
@ -239,7 +239,7 @@ namespace ImageSharp
/// <remarks>If <see cref="M:PixelAccessor.PooledMemory"/> is true then caller is responsible for ensuring <see cref="M:PixelDataPool.Return()"/> is called.</remarks> /// <remarks>If <see cref="M:PixelAccessor.PooledMemory"/> is true then caller is responsible for ensuring <see cref="M:PixelDataPool.Return()"/> is called.</remarks>
internal TColor[] ReturnCurrentPixelsAndReplaceThemInternally(int width, int height, TColor[] pixels) internal TColor[] ReturnCurrentPixelsAndReplaceThemInternally(int width, int height, TColor[] pixels)
{ {
TColor[] oldPixels = this.pixelBuffer.UnPinAndTakeArrayOwnership(); TColor[] oldPixels = this.pixelBuffer.TakeArrayOwnership();
this.SetPixelBufferUnsafe(width, height, pixels); this.SetPixelBufferUnsafe(width, height, pixels);
return oldPixels; return oldPixels;
} }
@ -410,7 +410,7 @@ namespace ImageSharp
private void SetPixelBufferUnsafe(int width, int height, TColor[] pixels) private void SetPixelBufferUnsafe(int width, int height, TColor[] pixels)
{ {
this.SetPixelBufferUnsafe(width, height, new PinnedImageBuffer<TColor>(pixels, width, height)); this.SetPixelBufferUnsafe(width, height, new Buffer2D<TColor>(pixels, width, height));
} }
/// <summary> /// <summary>
@ -419,7 +419,7 @@ namespace ImageSharp
/// <param name="width">The width.</param> /// <param name="width">The width.</param>
/// <param name="height">The height.</param> /// <param name="height">The height.</param>
/// <param name="pixels">The pixel buffer</param> /// <param name="pixels">The pixel buffer</param>
private void SetPixelBufferUnsafe(int width, int height, PinnedImageBuffer<TColor> pixels) private void SetPixelBufferUnsafe(int width, int height, Buffer2D<TColor> pixels)
{ {
this.pixelBuffer = pixels; this.pixelBuffer = pixels;

6
src/ImageSharp/Image/PixelArea{TColor}.cs

@ -30,7 +30,7 @@ namespace ImageSharp
/// <summary> /// <summary>
/// The underlying buffer containing the raw pixel data. /// The underlying buffer containing the raw pixel data.
/// </summary> /// </summary>
private PinnedBuffer<byte> byteBuffer; private Buffer<byte> byteBuffer;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PixelArea{TColor}"/> class. /// Initializes a new instance of the <see cref="PixelArea{TColor}"/> class.
@ -66,7 +66,7 @@ namespace ImageSharp
this.RowStride = width * GetComponentCount(componentOrder); this.RowStride = width * GetComponentCount(componentOrder);
this.Length = bytes.Length; // TODO: Is this the right value for Length? this.Length = bytes.Length; // TODO: Is this the right value for Length?
this.byteBuffer = new PinnedBuffer<byte>(bytes); this.byteBuffer = new Buffer<byte>(bytes);
} }
/// <summary> /// <summary>
@ -116,7 +116,7 @@ namespace ImageSharp
this.RowStride = (width * GetComponentCount(componentOrder)) + padding; this.RowStride = (width * GetComponentCount(componentOrder)) + padding;
this.Length = this.RowStride * height; this.Length = this.RowStride * height;
this.byteBuffer = new PinnedBuffer<byte>(this.Length); this.byteBuffer = new Buffer<byte>(this.Length);
} }
/// <summary> /// <summary>

6
src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs

@ -107,7 +107,7 @@ namespace ImageSharp.Processing.Processors
/// <param name="x">The column position</param> /// <param name="x">The column position</param>
/// <returns>The weighted sum</returns> /// <returns>The weighted sum</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ComputeWeightedColumnSum(PinnedImageBuffer<Vector4> firstPassPixels, int x) public Vector4 ComputeWeightedColumnSum(Buffer2D<Vector4> firstPassPixels, int x)
{ {
ref float verticalValues = ref this.Ptr; ref float verticalValues = ref this.Ptr;
int left = this.Left; int left = this.Left;
@ -131,7 +131,7 @@ namespace ImageSharp.Processing.Processors
/// </summary> /// </summary>
internal class WeightsBuffer : IDisposable internal class WeightsBuffer : IDisposable
{ {
private PinnedImageBuffer<float> dataBuffer; private Buffer2D<float> dataBuffer;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="WeightsBuffer"/> class. /// Initializes a new instance of the <see cref="WeightsBuffer"/> class.
@ -140,7 +140,7 @@ namespace ImageSharp.Processing.Processors
/// <param name="destinationSize">The size of the destination window</param> /// <param name="destinationSize">The size of the destination window</param>
public WeightsBuffer(int sourceSize, int destinationSize) public WeightsBuffer(int sourceSize, int destinationSize)
{ {
this.dataBuffer = PinnedImageBuffer<float>.CreateClean(sourceSize, destinationSize); this.dataBuffer = Buffer2D<float>.CreateClean(sourceSize, destinationSize);
this.Weights = new WeightsWindow[destinationSize]; this.Weights = new WeightsWindow[destinationSize];
} }

4
src/ImageSharp/Processing/Processors/Transforms/ResizeProcessor.cs

@ -106,7 +106,7 @@ namespace ImageSharp.Processing.Processors
using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(width, height)) using (PixelAccessor<TColor> targetPixels = new PixelAccessor<TColor>(width, height))
{ {
using (PixelAccessor<TColor> sourcePixels = source.Lock()) using (PixelAccessor<TColor> sourcePixels = source.Lock())
using (PinnedImageBuffer<Vector4> firstPassPixels = new PinnedImageBuffer<Vector4>(width, source.Height)) using (Buffer2D<Vector4> firstPassPixels = new Buffer2D<Vector4>(width, source.Height))
{ {
firstPassPixels.Clear(); firstPassPixels.Clear();
@ -117,7 +117,7 @@ namespace ImageSharp.Processing.Processors
y => y =>
{ {
// TODO: Without Parallel.For() this buffer object could be reused: // TODO: Without Parallel.For() this buffer object could be reused:
using (PinnedBuffer<Vector4> tempRowBuffer = new PinnedBuffer<Vector4>(sourcePixels.Width)) using (Buffer<Vector4> tempRowBuffer = new Buffer<Vector4>(sourcePixels.Width))
{ {
BufferSpan<TColor> sourceRow = sourcePixels.GetRowSpan(y); BufferSpan<TColor> sourceRow = sourcePixels.GetRowSpan(y);

14
tests/ImageSharp.Benchmarks/Color/Bulk/PackFromVector4ReferenceVsPointer.cs

@ -14,9 +14,9 @@
/// </summary> /// </summary>
public unsafe class PackFromVector4ReferenceVsPointer public unsafe class PackFromVector4ReferenceVsPointer
{ {
private PinnedBuffer<ImageSharp.Color> destination; private Buffer<ImageSharp.Color> destination;
private PinnedBuffer<Vector4> source; private Buffer<Vector4> source;
[Params(16, 128, 1024)] [Params(16, 128, 1024)]
public int Count { get; set; } public int Count { get; set; }
@ -24,8 +24,10 @@
[Setup] [Setup]
public void Setup() public void Setup()
{ {
this.destination = new PinnedBuffer<ImageSharp.Color>(this.Count); this.destination = new Buffer<ImageSharp.Color>(this.Count);
this.source = new PinnedBuffer<Vector4>(this.Count * 4); this.source = new Buffer<Vector4>(this.Count * 4);
this.source.Pin();
this.destination.Pin();
} }
[Cleanup] [Cleanup]
@ -38,8 +40,8 @@
[Benchmark(Baseline = true)] [Benchmark(Baseline = true)]
public void PackUsingPointers() public void PackUsingPointers()
{ {
Vector4* sp = (Vector4*)this.source.Pointer; Vector4* sp = (Vector4*)this.source.Pin();
byte* dp = (byte*)this.destination.Pointer; byte* dp = (byte*)this.destination.Pin();
int count = this.Count; int count = this.Count;
int size = sizeof(ImageSharp.Color); int size = sizeof(ImageSharp.Color);

8
tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs

@ -8,9 +8,9 @@ namespace ImageSharp.Benchmarks.Color.Bulk
public abstract class PackFromXyzw<TColor> public abstract class PackFromXyzw<TColor>
where TColor : struct, IPixel<TColor> where TColor : struct, IPixel<TColor>
{ {
private PinnedBuffer<TColor> destination; private Buffer<TColor> destination;
private PinnedBuffer<byte> source; private Buffer<byte> source;
[Params(16, 128, 1024)] [Params(16, 128, 1024)]
public int Count { get; set; } public int Count { get; set; }
@ -18,8 +18,8 @@ namespace ImageSharp.Benchmarks.Color.Bulk
[Setup] [Setup]
public void Setup() public void Setup()
{ {
this.destination = new PinnedBuffer<TColor>(this.Count); this.destination = new Buffer<TColor>(this.Count);
this.source = new PinnedBuffer<byte>(this.Count * 4); this.source = new Buffer<byte>(this.Count * 4);
} }
[Cleanup] [Cleanup]

8
tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs

@ -8,9 +8,9 @@ namespace ImageSharp.Benchmarks.Color.Bulk
public abstract class ToVector4<TColor> public abstract class ToVector4<TColor>
where TColor : struct, IPixel<TColor> where TColor : struct, IPixel<TColor>
{ {
private PinnedBuffer<TColor> source; private Buffer<TColor> source;
private PinnedBuffer<Vector4> destination; private Buffer<Vector4> destination;
[Params(64, 300, 1024)] [Params(64, 300, 1024)]
public int Count { get; set; } public int Count { get; set; }
@ -18,8 +18,8 @@ namespace ImageSharp.Benchmarks.Color.Bulk
[Setup] [Setup]
public void Setup() public void Setup()
{ {
this.source = new PinnedBuffer<TColor>(this.Count); this.source = new Buffer<TColor>(this.Count);
this.destination = new PinnedBuffer<Vector4>(this.Count); this.destination = new Buffer<Vector4>(this.Count);
} }
[Cleanup] [Cleanup]

8
tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs

@ -8,9 +8,9 @@ namespace ImageSharp.Benchmarks.Color.Bulk
public abstract class ToXyz<TColor> public abstract class ToXyz<TColor>
where TColor : struct, IPixel<TColor> where TColor : struct, IPixel<TColor>
{ {
private PinnedBuffer<TColor> source; private Buffer<TColor> source;
private PinnedBuffer<byte> destination; private Buffer<byte> destination;
[Params(16, 128, 1024)] [Params(16, 128, 1024)]
public int Count { get; set; } public int Count { get; set; }
@ -18,8 +18,8 @@ namespace ImageSharp.Benchmarks.Color.Bulk
[Setup] [Setup]
public void Setup() public void Setup()
{ {
this.source = new PinnedBuffer<TColor>(this.Count); this.source = new Buffer<TColor>(this.Count);
this.destination = new PinnedBuffer<byte>(this.Count * 3); this.destination = new Buffer<byte>(this.Count * 3);
} }
[Cleanup] [Cleanup]

8
tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs

@ -13,9 +13,9 @@ namespace ImageSharp.Benchmarks.Color.Bulk
public abstract class ToXyzw<TColor> public abstract class ToXyzw<TColor>
where TColor : struct, IPixel<TColor> where TColor : struct, IPixel<TColor>
{ {
private PinnedBuffer<TColor> source; private Buffer<TColor> source;
private PinnedBuffer<byte> destination; private Buffer<byte> destination;
[Params(16, 128, 1024)] [Params(16, 128, 1024)]
public int Count { get; set; } public int Count { get; set; }
@ -23,8 +23,8 @@ namespace ImageSharp.Benchmarks.Color.Bulk
[Setup] [Setup]
public void Setup() public void Setup()
{ {
this.source = new PinnedBuffer<TColor>(this.Count); this.source = new Buffer<TColor>(this.Count);
this.destination = new PinnedBuffer<byte>(this.Count * 4); this.destination = new Buffer<byte>(this.Count * 4);
} }
[Cleanup] [Cleanup]

6
tests/ImageSharp.Benchmarks/General/ClearBuffer.cs

@ -11,7 +11,7 @@ namespace ImageSharp.Benchmarks.General
public unsafe class ClearBuffer public unsafe class ClearBuffer
{ {
private PinnedBuffer<Color> buffer; private Buffer<Color> buffer;
[Params(32, 128, 512)] [Params(32, 128, 512)]
public int Count { get; set; } public int Count { get; set; }
@ -19,7 +19,7 @@ namespace ImageSharp.Benchmarks.General
[Setup] [Setup]
public void Setup() public void Setup()
{ {
this.buffer = new PinnedBuffer<ImageSharp.Color>(this.Count); this.buffer = new Buffer<ImageSharp.Color>(this.Count);
} }
[Cleanup] [Cleanup]
@ -37,7 +37,7 @@ namespace ImageSharp.Benchmarks.General
[Benchmark] [Benchmark]
public void Unsafe_InitBlock() public void Unsafe_InitBlock()
{ {
Unsafe.InitBlock((void*)this.buffer.Pointer, default(byte), (uint)this.Count*sizeof(uint)); Unsafe.InitBlock((void*)this.buffer.Pin(), default(byte), (uint)this.Count*sizeof(uint));
} }
} }
} }

7
tests/ImageSharp.Benchmarks/General/IterateArray.cs

@ -8,7 +8,7 @@ namespace ImageSharp.Benchmarks.General
public class IterateArray public class IterateArray
{ {
// Usual pinned stuff // Usual pinned stuff
private PinnedBuffer<Vector4> buffer; private Buffer<Vector4> buffer;
// An array that's not pinned by intent! // An array that's not pinned by intent!
private Vector4[] array; private Vector4[] array;
@ -19,7 +19,8 @@ namespace ImageSharp.Benchmarks.General
[Setup] [Setup]
public void Setup() public void Setup()
{ {
this.buffer = new PinnedBuffer<Vector4>(this.Length); this.buffer = new Buffer<Vector4>(this.Length);
this.buffer.Pin();
this.array = new Vector4[this.Length]; this.array = new Vector4[this.Length];
} }
@ -41,7 +42,7 @@ namespace ImageSharp.Benchmarks.General
{ {
Vector4 sum = new Vector4(); Vector4 sum = new Vector4();
Vector4* ptr = (Vector4*) this.buffer.Pointer; Vector4* ptr = (Vector4*) this.buffer.Pin();
Vector4* end = ptr + this.Length; Vector4* end = ptr + this.Length;
for (; ptr < end; ptr++) for (; ptr < end; ptr++)

10
tests/ImageSharp.Benchmarks/General/PixelIndexing.cs

@ -29,9 +29,9 @@
private int width; private int width;
public Data(PinnedImageBuffer<Vector4> buffer) public Data(Buffer2D<Vector4> buffer)
{ {
this.pointer = (Vector4*)buffer.Pointer; this.pointer = (Vector4*)buffer.Pin();
this.pinnable = Unsafe.As<Pinnable<Vector4>>(buffer.Array); this.pinnable = Unsafe.As<Pinnable<Vector4>>(buffer.Array);
this.array = buffer.Array; this.array = buffer.Array;
this.width = buffer.Width; this.width = buffer.Width;
@ -126,7 +126,7 @@
} }
} }
internal PinnedImageBuffer<Vector4> buffer; internal Buffer2D<Vector4> buffer;
protected int width; protected int width;
@ -147,8 +147,8 @@
public void Setup() public void Setup()
{ {
this.width = 2048; this.width = 2048;
this.buffer = new PinnedImageBuffer<Vector4>(2048, 2048); this.buffer = new Buffer2D<Vector4>(2048, 2048);
this.pointer = (Vector4*)this.buffer.Pointer; this.pointer = (Vector4*)this.buffer.Pin();
this.array = this.buffer.Array; this.array = this.buffer.Array;
this.pinnable = Unsafe.As<Pinnable<Vector4>>(this.array); this.pinnable = Unsafe.As<Pinnable<Vector4>>(this.array);

16
tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs

@ -45,8 +45,8 @@ namespace ImageSharp.Tests.Colors
int times = 200000; int times = 200000;
int count = 1024; int count = 1024;
using (PinnedBuffer<ImageSharp.Color> source = new PinnedBuffer<ImageSharp.Color>(count)) using (Buffer<ImageSharp.Color> source = new Buffer<ImageSharp.Color>(count))
using (PinnedBuffer<Vector4> dest = new PinnedBuffer<Vector4>(count)) using (Buffer<Vector4> dest = new Buffer<Vector4>(count))
{ {
this.Measure( this.Measure(
times, times,
@ -310,18 +310,18 @@ namespace ImageSharp.Tests.Colors
where TSource : struct where TSource : struct
where TDest : struct where TDest : struct
{ {
public PinnedBuffer<TSource> SourceBuffer { get; } public Buffer<TSource> SourceBuffer { get; }
public PinnedBuffer<TDest> ActualDestBuffer { get; } public Buffer<TDest> ActualDestBuffer { get; }
public PinnedBuffer<TDest> ExpectedDestBuffer { get; } public Buffer<TDest> ExpectedDestBuffer { get; }
public BufferSpan<TSource> Source => this.SourceBuffer; public BufferSpan<TSource> Source => this.SourceBuffer;
public BufferSpan<TDest> ActualDest => this.ActualDestBuffer; public BufferSpan<TDest> ActualDest => this.ActualDestBuffer;
public TestBuffers(TSource[] source, TDest[] expectedDest) public TestBuffers(TSource[] source, TDest[] expectedDest)
{ {
this.SourceBuffer = new PinnedBuffer<TSource>(source); this.SourceBuffer = new Buffer<TSource>(source);
this.ExpectedDestBuffer = new PinnedBuffer<TDest>(expectedDest); this.ExpectedDestBuffer = new Buffer<TDest>(expectedDest);
this.ActualDestBuffer = new PinnedBuffer<TDest>(expectedDest.Length); this.ActualDestBuffer = new Buffer<TDest>(expectedDest.Length);
} }
public void Dispose() public void Dispose()

27
tests/ImageSharp.Tests/Common/PinnedImageBufferTests.cs → tests/ImageSharp.Tests/Common/Buffer2DTests.cs

@ -8,19 +8,18 @@ namespace ImageSharp.Tests.Common
using static TestStructs; using static TestStructs;
public unsafe class PinnedImageBufferTests public unsafe class Buffer2DTests
{ {
// ReSharper disable once ClassNeverInstantiated.Local // ReSharper disable once ClassNeverInstantiated.Local
private class Assert : Xunit.Assert private class Assert : Xunit.Assert
{ {
public static void SpanPointsTo<T>(IntPtr ptr, BufferSpan<T> span) public static void SpanPointsTo<T>(BufferSpan<T> span, Buffer<T> buffer, int bufferOffset = 0)
where T : struct where T : struct
{ {
ref byte r = ref Unsafe.As<T, byte>(ref span.DangerousGetPinnableReference()); ref T actual = ref span.DangerousGetPinnableReference();
ref T expected = ref Unsafe.Add(ref buffer[0], bufferOffset);
void* p = Unsafe.AsPointer(ref r); Assert.True(Unsafe.AreSame(ref expected, ref actual), "span does not point to the expected position");
Assert.Equal(ptr, (IntPtr)p);
} }
} }
@ -29,7 +28,7 @@ namespace ImageSharp.Tests.Common
[InlineData(1025, 17)] [InlineData(1025, 17)]
public void Construct(int width, int height) public void Construct(int width, int height)
{ {
using (PinnedImageBuffer<Foo> buffer = new PinnedImageBuffer<Foo>(width, height)) using (Buffer2D<Foo> buffer = new Buffer2D<Foo>(width, height))
{ {
Assert.Equal(width, buffer.Width); Assert.Equal(width, buffer.Width);
Assert.Equal(height, buffer.Height); Assert.Equal(height, buffer.Height);
@ -43,7 +42,7 @@ namespace ImageSharp.Tests.Common
public void Construct_FromExternalArray(int width, int height) public void Construct_FromExternalArray(int width, int height)
{ {
Foo[] array = new Foo[width * height + 10]; Foo[] array = new Foo[width * height + 10];
using (PinnedImageBuffer<Foo> buffer = new PinnedImageBuffer<Foo>(array, width, height)) using (Buffer2D<Foo> buffer = new Buffer2D<Foo>(array, width, height))
{ {
Assert.Equal(width, buffer.Width); Assert.Equal(width, buffer.Width);
Assert.Equal(height, buffer.Height); Assert.Equal(height, buffer.Height);
@ -57,7 +56,7 @@ namespace ImageSharp.Tests.Common
{ {
for (int i = 0; i < 100; i++) for (int i = 0; i < 100; i++)
{ {
using (PinnedImageBuffer<int> buffer = PinnedImageBuffer<int>.CreateClean(42, 42)) using (Buffer2D<int> buffer = Buffer2D<int>.CreateClean(42, 42))
{ {
for (int j = 0; j < buffer.Length; j++) for (int j = 0; j < buffer.Length; j++)
{ {
@ -74,13 +73,13 @@ namespace ImageSharp.Tests.Common
[InlineData(17, 42, 41)] [InlineData(17, 42, 41)]
public void GetRowSpanY(int width, int height, int y) public void GetRowSpanY(int width, int height, int y)
{ {
using (PinnedImageBuffer<Foo> buffer = new PinnedImageBuffer<Foo>(width, height)) using (Buffer2D<Foo> buffer = new Buffer2D<Foo>(width, height))
{ {
BufferSpan<Foo> span = buffer.GetRowSpan(y); BufferSpan<Foo> span = buffer.GetRowSpan(y);
Assert.Equal(width * y, span.Start); Assert.Equal(width * y, span.Start);
Assert.Equal(width, span.Length); Assert.Equal(width, span.Length);
Assert.SpanPointsTo(buffer.Pointer + sizeof(Foo) * width * y, span); Assert.SpanPointsTo(span, buffer, width * y);
} }
} }
@ -90,13 +89,13 @@ namespace ImageSharp.Tests.Common
[InlineData(17, 42, 0, 41)] [InlineData(17, 42, 0, 41)]
public void GetRowSpanXY(int width, int height, int x, int y) public void GetRowSpanXY(int width, int height, int x, int y)
{ {
using (PinnedImageBuffer<Foo> buffer = new PinnedImageBuffer<Foo>(width, height)) using (Buffer2D<Foo> buffer = new Buffer2D<Foo>(width, height))
{ {
BufferSpan<Foo> span = buffer.GetRowSpan(x, y); BufferSpan<Foo> span = buffer.GetRowSpan(x, y);
Assert.Equal(width * y + x, span.Start); Assert.Equal(width * y + x, span.Start);
Assert.Equal(width - x, span.Length); Assert.Equal(width - x, span.Length);
Assert.SpanPointsTo(buffer.Pointer + sizeof(Foo) * (width * y + x), span); Assert.SpanPointsTo(span, buffer, width * y + x);
} }
} }
@ -106,7 +105,7 @@ namespace ImageSharp.Tests.Common
[InlineData(99, 88, 98, 87)] [InlineData(99, 88, 98, 87)]
public void Indexer(int width, int height, int x, int y) public void Indexer(int width, int height, int x, int y)
{ {
using (PinnedImageBuffer<Foo> buffer = new PinnedImageBuffer<Foo>(width, height)) using (Buffer2D<Foo> buffer = new Buffer2D<Foo>(width, height))
{ {
Foo[] array = buffer.Array; Foo[] array = buffer.Array;

6
tests/ImageSharp.Tests/Common/BufferSpanTests.cs

@ -28,7 +28,7 @@ namespace ImageSharp.Tests.Common
{ {
Foo[] fooz = { new Foo(1, 2), new Foo(3, 4), new Foo(5, 6) }; Foo[] fooz = { new Foo(1, 2), new Foo(3, 4), new Foo(5, 6) };
using (PinnedBuffer<Foo> colorBuf = new PinnedBuffer<Foo>(fooz)) using (Buffer<Foo> colorBuf = new Buffer<Foo>(fooz))
{ {
BufferSpan<Foo> orig = colorBuf.Slice(1); BufferSpan<Foo> orig = colorBuf.Slice(1);
BufferSpan<byte> asBytes = orig.AsBytes(); BufferSpan<byte> asBytes = orig.AsBytes();
@ -414,8 +414,8 @@ namespace ImageSharp.Tests.Common
{ {
Color[] colors = { new Color(0, 1, 2, 3), new Color(4, 5, 6, 7), new Color(8, 9, 10, 11), }; Color[] colors = { new Color(0, 1, 2, 3), new Color(4, 5, 6, 7), new Color(8, 9, 10, 11), };
using (PinnedBuffer<Color> colorBuf = new PinnedBuffer<Color>(colors)) using (Buffer<Color> colorBuf = new Buffer<Color>(colors))
using (PinnedBuffer<byte> byteBuf = new PinnedBuffer<byte>(colors.Length * 4)) using (Buffer<byte> byteBuf = new Buffer<byte>(colors.Length * 4))
{ {
BufferSpan.Copy(colorBuf.Span.AsBytes(), byteBuf, colorBuf.Length*sizeof(Color)); BufferSpan.Copy(colorBuf.Span.AsBytes(), byteBuf, colorBuf.Length*sizeof(Color));

93
tests/ImageSharp.Tests/Common/PinnedBufferTests.cs → tests/ImageSharp.Tests/Common/BufferTests.cs

@ -1,4 +1,5 @@
namespace ImageSharp.Tests.Common // ReSharper disable InconsistentNaming
namespace ImageSharp.Tests.Common
{ {
using System; using System;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
@ -9,18 +10,23 @@
using static TestStructs; using static TestStructs;
public unsafe class PinnedBufferTests public unsafe class BufferTests
{ {
// ReSharper disable once ClassNeverInstantiated.Local
private class Assert : Xunit.Assert private class Assert : Xunit.Assert
{ {
public static void SpanPointsTo<T>(IntPtr ptr, BufferSpan<T> span) public static void SpanPointsTo<T>(BufferSpan<T> span, Buffer<T> buffer, int bufferOffset = 0)
where T : struct where T : struct
{ {
ref byte r = ref Unsafe.As<T, byte>(ref span.DangerousGetPinnableReference()); ref T actual = ref span.DangerousGetPinnableReference();
ref T expected = ref Unsafe.Add(ref buffer[0], bufferOffset);
void* p = Unsafe.AsPointer(ref r); Assert.True(Unsafe.AreSame(ref expected, ref actual), "span does not point to the expected position");
}
Assert.Equal(ptr, (IntPtr)p); public static void Equal(void* expected, void* actual)
{
Assert.Equal((IntPtr)expected, (IntPtr)actual);
} }
} }
@ -29,14 +35,12 @@
[InlineData(1111)] [InlineData(1111)]
public void ConstructWithOwnArray(int count) public void ConstructWithOwnArray(int count)
{ {
using (PinnedBuffer<Foo> buffer = new PinnedBuffer<Foo>(count)) using (Buffer<Foo> buffer = new Buffer<Foo>(count))
{ {
Assert.False(buffer.IsDisposedOrLostArrayOwnership); Assert.False(buffer.IsDisposedOrLostArrayOwnership);
Assert.NotNull(buffer.Array); Assert.NotNull(buffer.Array);
Assert.Equal(count, buffer.Length); Assert.Equal(count, buffer.Length);
Assert.True(buffer.Array.Length >= count); Assert.True(buffer.Array.Length >= count);
VerifyPointer(buffer);
} }
} }
@ -46,13 +50,11 @@
public void ConstructWithExistingArray(int count) public void ConstructWithExistingArray(int count)
{ {
Foo[] array = new Foo[count]; Foo[] array = new Foo[count];
using (PinnedBuffer<Foo> buffer = new PinnedBuffer<Foo>(array)) using (Buffer<Foo> buffer = new Buffer<Foo>(array))
{ {
Assert.False(buffer.IsDisposedOrLostArrayOwnership); Assert.False(buffer.IsDisposedOrLostArrayOwnership);
Assert.Equal(array, buffer.Array); Assert.Equal(array, buffer.Array);
Assert.Equal(count, buffer.Length); Assert.Equal(count, buffer.Length);
VerifyPointer(buffer);
} }
} }
@ -62,7 +64,7 @@
public void Clear(int count) public void Clear(int count)
{ {
Foo[] a = { new Foo() { A = 1, B = 2 }, new Foo() { A = 3, B = 4 } }; Foo[] a = { new Foo() { A = 1, B = 2 }, new Foo() { A = 3, B = 4 } };
using (PinnedBuffer<Foo> buffer = new PinnedBuffer<Foo>(a)) using (Buffer<Foo> buffer = new Buffer<Foo>(a))
{ {
buffer.Clear(); buffer.Clear();
@ -76,7 +78,7 @@
{ {
for (int i = 0; i < 100; i++) for (int i = 0; i < 100; i++)
{ {
using (PinnedBuffer<int> buffer = PinnedBuffer<int>.CreateClean(42)) using (Buffer<int> buffer = Buffer<int>.CreateClean(42))
{ {
for (int j = 0; j < buffer.Length; j++) for (int j = 0; j < buffer.Length; j++)
{ {
@ -103,7 +105,7 @@
{ {
Foo[] a = Foo.CreateArray(length); Foo[] a = Foo.CreateArray(length);
using (PinnedBuffer<Foo> buffer = new PinnedBuffer<Foo>(a)) using (Buffer<Foo> buffer = new Buffer<Foo>(a))
{ {
Foo element = buffer[index]; Foo element = buffer[index];
@ -117,7 +119,7 @@
{ {
Foo[] a = Foo.CreateArray(length); Foo[] a = Foo.CreateArray(length);
using (PinnedBuffer<Foo> buffer = new PinnedBuffer<Foo>(a)) using (Buffer<Foo> buffer = new Buffer<Foo>(a))
{ {
buffer[index] = new Foo(666, 666); buffer[index] = new Foo(666, 666);
@ -129,7 +131,7 @@
[Fact] [Fact]
public void Dispose() public void Dispose()
{ {
PinnedBuffer<Foo> buffer = new PinnedBuffer<Foo>(42); Buffer<Foo> buffer = new Buffer<Foo>(42);
buffer.Dispose(); buffer.Dispose();
Assert.True(buffer.IsDisposedOrLostArrayOwnership); Assert.True(buffer.IsDisposedOrLostArrayOwnership);
@ -140,13 +142,13 @@
[InlineData(123)] [InlineData(123)]
public void CastToSpan(int bufferLength) public void CastToSpan(int bufferLength)
{ {
using (PinnedBuffer<Foo> buffer = new PinnedBuffer<Foo>(bufferLength)) using (Buffer<Foo> buffer = new Buffer<Foo>(bufferLength))
{ {
BufferSpan<Foo> span = buffer; BufferSpan<Foo> span = buffer;
Assert.Equal(buffer.Array, span.Array); Assert.Equal(buffer.Array, span.Array);
Assert.Equal(0, span.Start); Assert.Equal(0, span.Start);
Assert.SpanPointsTo(buffer.Pointer, span); Assert.SpanPointsTo(span, buffer);
Assert.Equal(span.Length, bufferLength); Assert.Equal(span.Length, bufferLength);
} }
} }
@ -154,13 +156,13 @@
[Fact] [Fact]
public void Span() public void Span()
{ {
using (PinnedBuffer<Foo> buffer = new PinnedBuffer<Foo>(42)) using (Buffer<Foo> buffer = new Buffer<Foo>(42))
{ {
BufferSpan<Foo> span = buffer.Span; BufferSpan<Foo> span = buffer.Span;
Assert.Equal(buffer.Array, span.Array); Assert.Equal(buffer.Array, span.Array);
Assert.Equal(0, span.Start); Assert.Equal(0, span.Start);
Assert.SpanPointsTo(buffer.Pointer, span); Assert.SpanPointsTo(span, buffer);
Assert.Equal(span.Length, 42); Assert.Equal(span.Length, 42);
} }
} }
@ -173,13 +175,13 @@
[InlineData(123, 17)] [InlineData(123, 17)]
public void WithStartOnly(int bufferLength, int start) public void WithStartOnly(int bufferLength, int start)
{ {
using (PinnedBuffer<Foo> buffer = new PinnedBuffer<Foo>(bufferLength)) using (Buffer<Foo> buffer = new Buffer<Foo>(bufferLength))
{ {
BufferSpan<Foo> span = buffer.Slice(start); BufferSpan<Foo> span = buffer.Slice(start);
Assert.Equal(buffer.Array, span.Array); Assert.Equal(buffer.Array, span.Array);
Assert.Equal(start, span.Start); Assert.Equal(start, span.Start);
Assert.SpanPointsTo(buffer.Pointer + start * Unsafe.SizeOf<Foo>(), span); Assert.SpanPointsTo(span, buffer, start);
Assert.Equal(span.Length, bufferLength - start); Assert.Equal(span.Length, bufferLength - start);
} }
} }
@ -189,13 +191,13 @@
[InlineData(123, 17, 42)] [InlineData(123, 17, 42)]
public void WithStartAndLength(int bufferLength, int start, int spanLength) public void WithStartAndLength(int bufferLength, int start, int spanLength)
{ {
using (PinnedBuffer<Foo> buffer = new PinnedBuffer<Foo>(bufferLength)) using (Buffer<Foo> buffer = new Buffer<Foo>(bufferLength))
{ {
BufferSpan<Foo> span = buffer.Slice(start, spanLength); BufferSpan<Foo> span = buffer.Slice(start, spanLength);
Assert.Equal(buffer.Array, span.Array); Assert.Equal(buffer.Array, span.Array);
Assert.Equal(start, span.Start); Assert.Equal(start, span.Start);
Assert.SpanPointsTo(buffer.Pointer + start * Unsafe.SizeOf<Foo>(), span); Assert.SpanPointsTo(span, buffer, start);
Assert.Equal(span.Length, spanLength); Assert.Equal(span.Length, spanLength);
} }
} }
@ -205,9 +207,9 @@
public void UnPinAndTakeArrayOwnership() public void UnPinAndTakeArrayOwnership()
{ {
Foo[] data = null; Foo[] data = null;
using (PinnedBuffer<Foo> buffer = new PinnedBuffer<Foo>(42)) using (Buffer<Foo> buffer = new Buffer<Foo>(42))
{ {
data = buffer.UnPinAndTakeArrayOwnership(); data = buffer.TakeArrayOwnership();
Assert.True(buffer.IsDisposedOrLostArrayOwnership); Assert.True(buffer.IsDisposedOrLostArrayOwnership);
} }
@ -215,10 +217,41 @@
Assert.True(data.Length >= 42); Assert.True(data.Length >= 42);
} }
private static void VerifyPointer(PinnedBuffer<Foo> buffer) public class Pin
{ {
IntPtr ptr = (IntPtr)Unsafe.AsPointer(ref buffer.Array[0]); [Fact]
Assert.Equal(ptr, buffer.Pointer); public void ReturnsPinnedPointerToTheBeginningOfArray()
{
using (Buffer<Foo> buffer = new Buffer<Foo>(42))
{
Foo* actual = (Foo*)buffer.Pin();
fixed (Foo* expected = buffer.Array)
{
Assert.Equal(expected, actual);
}
}
}
[Fact]
public void SecondCallReturnsTheSamePointer()
{
using (Buffer<Foo> buffer = new Buffer<Foo>(42))
{
IntPtr ptr1 = buffer.Pin();
IntPtr ptr2 = buffer.Pin();
Assert.Equal(ptr1, ptr2);
}
}
[Fact]
public void WhenCalledOnDisposedBuffer_ThrowsInvalidOperationException()
{
Buffer<Foo> buffer = new Buffer<Foo>(42);
buffer.Dispose();
Assert.Throws<InvalidOperationException>(() => buffer.Pin());
}
} }
} }
} }
Loading…
Cancel
Save