mirror of https://github.com/SixLabors/ImageSharp
5 changed files with 257 additions and 43 deletions
@ -0,0 +1,109 @@ |
|||
namespace ImageSharp |
|||
{ |
|||
using System; |
|||
using System.Buffers; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
/// <summary>
|
|||
/// Manages a pinned buffer of 'T' as a Disposable resource.
|
|||
/// The backing array is either pooled or comes from the outside.
|
|||
/// TODO: Should replace the pinning/dispose logic in several classes like <see cref="PixelAccessor{TColor}"/> or <see cref="PixelArea{TColor}"/>!
|
|||
/// </summary>
|
|||
/// <typeparam name="T">The value type.</typeparam>
|
|||
internal class PinnedBuffer<T> : IDisposable |
|||
where T : struct |
|||
{ |
|||
private GCHandle handle; |
|||
|
|||
private bool isBufferRented; |
|||
|
|||
private bool isDisposed; |
|||
|
|||
/// <summary>
|
|||
/// TODO: Consider reusing functionality of <see cref="PixelPool{TColor}"/>
|
|||
/// </summary>
|
|||
private static readonly ArrayPool<T> ArrayPool = ArrayPool<T>.Create(); |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="PinnedBuffer{T}"/> class.
|
|||
/// </summary>
|
|||
/// <param name="count">The desired count of elements. (Minimum size for <see cref="Array"/>)</param>
|
|||
public PinnedBuffer(int count) |
|||
{ |
|||
this.Count = count; |
|||
this.Array = ArrayPool.Rent(count); |
|||
this.isBufferRented = true; |
|||
this.Pin(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of the <see cref="PinnedBuffer{T}"/> class.
|
|||
/// </summary>
|
|||
/// <param name="array">The array to pin.</param>
|
|||
public PinnedBuffer(T[] array) |
|||
{ |
|||
this.Count = array.Length; |
|||
this.Array = array; |
|||
this.Pin(); |
|||
} |
|||
|
|||
/// <summary>
|
|||
/// The count of "relevant" elements. Usually be smaller than 'Array.Length' when <see cref="Array"/> is pooled.
|
|||
/// </summary>
|
|||
public int Count { get; private set; } |
|||
|
|||
/// <summary>
|
|||
/// The (pinned) array of elements.
|
|||
/// </summary>
|
|||
public T[] Array { get; private set; } |
|||
|
|||
/// <summary>
|
|||
/// Pointer to the pinned <see cref="Array"/>.
|
|||
/// </summary>
|
|||
public IntPtr Pointer { get; private set; } |
|||
|
|||
/// <summary>
|
|||
/// Disposes the <see cref="PinnedBuffer{T}"/> instance by unpinning the array, and returning the pooled buffer when necessary.
|
|||
/// </summary>
|
|||
public void Dispose() |
|||
{ |
|||
if (this.isDisposed) |
|||
{ |
|||
return; |
|||
} |
|||
this.isDisposed = true; |
|||
this.UnPin(); |
|||
|
|||
if (this.isBufferRented) |
|||
{ |
|||
ArrayPool.Return(this.Array, true); |
|||
} |
|||
|
|||
this.Array = null; |
|||
this.Count = 0; |
|||
|
|||
GC.SuppressFinalize(this); |
|||
} |
|||
|
|||
private void Pin() |
|||
{ |
|||
this.handle = GCHandle.Alloc(this.Array, GCHandleType.Pinned); |
|||
this.Pointer = this.handle.AddrOfPinnedObject(); |
|||
} |
|||
|
|||
private void UnPin() |
|||
{ |
|||
if (this.Pointer == IntPtr.Zero || !this.handle.IsAllocated) |
|||
{ |
|||
return; |
|||
} |
|||
this.handle.Free(); |
|||
this.Pointer = IntPtr.Zero; |
|||
} |
|||
|
|||
~PinnedBuffer() |
|||
{ |
|||
this.UnPin(); |
|||
} |
|||
} |
|||
} |
|||
@ -1,104 +1,132 @@ |
|||
namespace ImageSharp.Tests.Colors |
|||
{ |
|||
using System; |
|||
using System.Numerics; |
|||
|
|||
using Xunit; |
|||
|
|||
public class BulkPixelOperationsTests |
|||
|
|||
public abstract class BulkPixelOperationsTests<TColor> |
|||
where TColor : struct, IPixel<TColor> |
|||
{ |
|||
public class TypeParam<TColor> |
|||
public class ColorPixels : BulkPixelOperationsTests<Color> |
|||
{ |
|||
} |
|||
|
|||
public class ArgbPixels : BulkPixelOperationsTests<Argb> |
|||
{ |
|||
} |
|||
|
|||
public static TheoryData<int> ArraySizesData = new TheoryData<int> { 7, 16, 1111 }; |
|||
|
|||
[Theory] |
|||
[InlineData(default(TypeParam<Color>))] |
|||
[InlineData(default(TypeParam<Argb>))] |
|||
public virtual void PackFromVector4<TColor>(TypeParam<TColor> dummy) |
|||
where TColor : struct, IPixel<TColor> |
|||
[MemberData(nameof(ArraySizesData))] |
|||
public virtual void PackFromVector4(int count) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(default(TypeParam<Color>))] |
|||
[InlineData(default(TypeParam<Argb>))] |
|||
public virtual void PackToVector4<TColor>(TypeParam<TColor> dummy) |
|||
where TColor : struct, IPixel<TColor> |
|||
[MemberData(nameof(ArraySizesData))] |
|||
public virtual void PackToVector4(int count) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(default(TypeParam<Color>))] |
|||
[InlineData(default(TypeParam<Argb>))] |
|||
public virtual void PackToXyzBytes<TColor>(TypeParam<TColor> dummy) |
|||
where TColor : struct, IPixel<TColor> |
|||
[MemberData(nameof(ArraySizesData))] |
|||
public virtual void PackToXyzBytes(int count) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(default(TypeParam<Color>))] |
|||
[InlineData(default(TypeParam<Argb>))] |
|||
public virtual void PackFromXyzBytes<TColor>(TypeParam<TColor> dummy) |
|||
where TColor : struct, IPixel<TColor> |
|||
[MemberData(nameof(ArraySizesData))] |
|||
public virtual void PackFromXyzBytes(int count) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(default(TypeParam<Color>))] |
|||
[InlineData(default(TypeParam<Argb>))] |
|||
public virtual void PackToXyzwBytes<TColor>(TypeParam<TColor> dummy) |
|||
where TColor : struct, IPixel<TColor> |
|||
[MemberData(nameof(ArraySizesData))] |
|||
public virtual void PackToXyzwBytes(int count) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(default(TypeParam<Color>))] |
|||
[InlineData(default(TypeParam<Argb>))] |
|||
public virtual void PackFromXyzwBytes<TColor>(TypeParam<TColor> dummy) |
|||
where TColor : struct, IPixel<TColor> |
|||
[MemberData(nameof(ArraySizesData))] |
|||
public virtual void PackFromXyzwBytes(int count) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(default(TypeParam<Color>))] |
|||
[InlineData(default(TypeParam<Argb>))] |
|||
public virtual void PackToZyxBytes<TColor>(TypeParam<TColor> dummy) |
|||
where TColor : struct, IPixel<TColor> |
|||
[MemberData(nameof(ArraySizesData))] |
|||
public virtual void PackToZyxBytes(int count) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(default(TypeParam<Color>))] |
|||
[InlineData(default(TypeParam<Argb>))] |
|||
public virtual void PackFromZyxBytes<TColor>(TypeParam<TColor> dummy) |
|||
where TColor : struct, IPixel<TColor> |
|||
[MemberData(nameof(ArraySizesData))] |
|||
public virtual void PackFromZyxBytes(int count) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(default(TypeParam<Color>))] |
|||
[InlineData(default(TypeParam<Argb>))] |
|||
public virtual void PackToZyxwBytes<TColor>(TypeParam<TColor> dummy) |
|||
where TColor : struct, IPixel<TColor> |
|||
[MemberData(nameof(ArraySizesData))] |
|||
public virtual void PackToZyxwBytes(int count) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(default(TypeParam<Color>))] |
|||
[InlineData(default(TypeParam<Argb>))] |
|||
public virtual void PackFromZyxwBytes<TColor>(TypeParam<TColor> dummy) |
|||
where TColor : struct, IPixel<TColor> |
|||
[MemberData(nameof(ArraySizesData))] |
|||
public virtual void PackFromZyxwBytes(int count) |
|||
{ |
|||
throw new NotImplementedException(); |
|||
} |
|||
|
|||
public class TestBuffers |
|||
{ |
|||
internal static PinnedBuffer<Vector4> Vector4(int length) |
|||
{ |
|||
Vector4[] result = new Vector4[length]; |
|||
Random rnd = new Random(42); // Deterministic random values
|
|||
|
|||
for (int i = 0; i < result.Length; i++) |
|||
{ |
|||
result[i] = GetVector(rnd); |
|||
} |
|||
|
|||
return new PinnedBuffer<Vector4>(result); |
|||
} |
|||
|
|||
internal static PinnedBuffer<TColor> Pixel(int length) |
|||
{ |
|||
TColor[] result = new TColor[length]; |
|||
|
|||
Random rnd = new Random(42); // Deterministic random values
|
|||
|
|||
for (int i = 0; i < result.Length; i++) |
|||
{ |
|||
Vector4 v = GetVector(rnd); |
|||
result[i].PackFromVector4(v); |
|||
} |
|||
|
|||
return new PinnedBuffer<TColor>(result); |
|||
} |
|||
|
|||
private static Vector4 GetVector(Random rnd) |
|||
{ |
|||
return new Vector4( |
|||
(float)rnd.NextDouble(), |
|||
(float)rnd.NextDouble(), |
|||
(float)rnd.NextDouble(), |
|||
(float)rnd.NextDouble() |
|||
); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,69 @@ |
|||
namespace ImageSharp.Tests.Common |
|||
{ |
|||
using System; |
|||
using System.Runtime.CompilerServices; |
|||
using System.Runtime.InteropServices; |
|||
|
|||
using Xunit; |
|||
|
|||
public unsafe class PinnedBufferTests |
|||
{ |
|||
public struct Foo |
|||
{ |
|||
public int A; |
|||
|
|||
public double B; |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(42)] |
|||
[InlineData(1111)] |
|||
public void ConstructWithOwnArray(int count) |
|||
{ |
|||
using (PinnedBuffer<Foo> buffer = new PinnedBuffer<Foo>(count)) |
|||
{ |
|||
Assert.NotNull(buffer.Array); |
|||
Assert.Equal(count, buffer.Count); |
|||
Assert.True(buffer.Array.Length >= count); |
|||
|
|||
VerifyPointer(buffer); |
|||
} |
|||
} |
|||
|
|||
[Theory] |
|||
[InlineData(42)] |
|||
[InlineData(1111)] |
|||
public void ConstructWithExistingArray(int count) |
|||
{ |
|||
Foo[] array = new Foo[count]; |
|||
using (PinnedBuffer<Foo> buffer = new PinnedBuffer<Foo>(array)) |
|||
{ |
|||
Assert.Equal(array, buffer.Array); |
|||
Assert.Equal(count, buffer.Count); |
|||
|
|||
VerifyPointer(buffer); |
|||
} |
|||
} |
|||
|
|||
[Fact] |
|||
public void GetArrayPointer() |
|||
{ |
|||
Foo[] a = { new Foo() { A = 1, B = 2 }, new Foo() { A = 3, B = 4 } }; |
|||
|
|||
using (PinnedBuffer<Foo> buffer = new PinnedBuffer<Foo>(a)) |
|||
{ |
|||
var arrayPtr = buffer.GetArrayPointer(); |
|||
|
|||
Assert.Equal(a, arrayPtr.Array); |
|||
Assert.Equal(0, arrayPtr.Offset); |
|||
Assert.Equal(buffer.Pointer, arrayPtr.PointerAtOffset); |
|||
} |
|||
} |
|||
|
|||
private static void VerifyPointer(PinnedBuffer<Foo> buffer) |
|||
{ |
|||
IntPtr ptr = (IntPtr)Unsafe.AsPointer(ref buffer.Array[0]); |
|||
Assert.Equal(ptr, buffer.Pointer); |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue