Browse Source

fixed indexer and copy operations

pull/174/head
Anton Firszov 9 years ago
parent
commit
548fc41711
  1. 10
      src/ImageSharp/Colors/Color.BulkOperations.cs
  2. 158
      src/ImageSharp/Common/Memory/BufferSpan.cs
  3. 16
      src/ImageSharp/Common/Memory/BufferSpan{T}.cs
  4. 4
      tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs
  5. 35
      tests/ImageSharp.Tests/Common/BufferSpanTests.cs
  6. 2
      tests/ImageSharp.Tests/Common/TestStructs.cs

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

@ -90,7 +90,7 @@ namespace ImageSharp
vf.CopyTo(fTemp, i);
}
BufferSpan.Copy<uint>(tempBuf, (BufferSpan<byte>)destVectors, unpackedRawCount);
BufferSpan.Copy(tempBuf.Span.AsBytes(), destVectors.AsBytes(), unpackedRawCount);
}
}
@ -156,18 +156,18 @@ namespace ImageSharp
}
/// <inheritdoc />
internal override void PackFromXyzwBytes(
internal override unsafe void PackFromXyzwBytes(
BufferSpan<byte> sourceBytes,
BufferSpan<Color> destColors,
int count)
{
BufferSpan.Copy(sourceBytes, destColors, count);
BufferSpan.Copy(sourceBytes, destColors.AsBytes(), count * sizeof(Color));
}
/// <inheritdoc />
internal override void ToXyzwBytes(BufferSpan<Color> sourceColors, BufferSpan<byte> destBytes, int count)
internal override unsafe void ToXyzwBytes(BufferSpan<Color> sourceColors, BufferSpan<byte> destBytes, int count)
{
BufferSpan.Copy(sourceColors, destBytes, count);
BufferSpan.Copy(sourceColors.AsBytes(), destBytes, count * sizeof(Color));
}
/// <inheritdoc />

158
src/ImageSharp/Common/Memory/BufferSpan.cs

@ -14,11 +14,6 @@ namespace ImageSharp
/// </summary>
internal static class BufferSpan
{
/// <summary>
/// It's worth to use Marshal.Copy() or Buffer.BlockCopy() over this size.
/// </summary>
private const int ByteCountThreshold = 1024;
/// <summary>
/// Copy 'count' number of elements of the same type from 'source' to 'dest'
/// </summary>
@ -27,69 +22,29 @@ namespace ImageSharp
/// <param name="destination">The destination <see cref="BufferSpan{T}"/>.</param>
/// <param name="count">The number of elements to copy</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Copy<T>(BufferSpan<T> source, BufferSpan<T> destination, int count)
public static unsafe void Copy<T>(BufferSpan<T> source, BufferSpan<T> destination, int count)
where T : struct
{
CopyImpl(source, destination, count);
}
/// <summary>
/// Copy 'countInSource' elements of <typeparamref name="T"/> from 'source' into the raw byte buffer 'destination'.
/// </summary>
/// <typeparam name="T">The element type.</typeparam>
/// <param name="source">The source buffer of <typeparamref name="T"/> elements to copy from.</param>
/// <param name="destination">The destination buffer.</param>
/// <param name="countInSource">The number of elements to copy from 'source'</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Copy<T>(BufferSpan<T> source, BufferSpan<byte> destination, int countInSource)
where T : struct
{
CopyImpl(source, destination, countInSource);
}
/// <summary>
/// Copy 'countInDest' number of <typeparamref name="TDest"/> elements into 'dest' from a raw byte buffer defined by 'source'.
/// </summary>
/// <typeparam name="TDest">The element type.</typeparam>
/// <param name="source">The raw source buffer to copy from"/></param>
/// <param name="destination">The destination buffer"/></param>
/// <param name="countInDest">The number of <typeparamref name="TDest"/> elements to copy.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void Copy<TDest>(BufferSpan<byte> source, BufferSpan<TDest> destination, int countInDest)
where TDest : struct
{
// TODO: Refactor this method when Unsafe.CopyBlock(ref T, ref T) gets available!
int byteCount = SizeOf<TDest>(countInDest);
if (PerTypeValues<TDest>.IsPrimitiveType)
{
Buffer.BlockCopy(source.Array, source.ByteOffset, destination.Array, destination.ByteOffset, byteCount);
return;
}
ref byte srcRef = ref Unsafe.As<T, byte>(ref source.DangerousGetPinnableReference());
ref byte destRef = ref Unsafe.As<T, byte>(ref destination.DangerousGetPinnableReference());
ref byte destRef = ref Unsafe.As<TDest, byte>(ref destination.DangerousGetPinnableReference());
int byteCount = Unsafe.SizeOf<T>() * count;
fixed (void* pinnedDest = &destRef)
// TODO: Use unfixed Unsafe.CopyBlock(ref T, ref T, int) for small blocks, when it gets available!
fixed (byte* pSrc = &srcRef)
fixed (byte* pDest = &destRef)
{
#if !NETSTANDARD1_1
ref byte srcRef = ref source.DangerousGetPinnableReference();
fixed (void* pinnedSrc = &srcRef)
{
Buffer.MemoryCopy(pinnedSrc, pinnedDest, byteCount, byteCount);
return;
}
#if NETSTANDARD1_1
Unsafe.CopyBlock(pDest, pSrc, (uint) byteCount);
#else
if (byteCount > ByteCountThreshold)
if (byteCount > 512)
{
IntPtr ptr = (IntPtr)pinnedDest;
Marshal.Copy(source.Array, source.Start, ptr, byteCount);
int destLength = destination.Length * Unsafe.SizeOf<T>();
Buffer.MemoryCopy(pSrc, pDest, destLength, byteCount);
}
ref byte srcRef = ref source.DangerousGetPinnableReference();
fixed (void* pinnedSrc = &srcRef)
else
{
Unsafe.CopyBlock(pinnedSrc, pinnedDest, (uint)byteCount);
Unsafe.CopyBlock(pDest, pSrc, (uint)byteCount);
}
#endif
}
@ -115,90 +70,5 @@ namespace ImageSharp
public static uint USizeOf<T>(int count)
where T : struct
=> (uint)SizeOf<T>(count);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe void CopyImpl<T, TDest>(BufferSpan<T> source, BufferSpan<TDest> destination, int countInSource)
where T : struct
where TDest : struct
{
// TODO: Use Unsafe.CopyBlock(ref T, ref T) for small buffers when it gets available!
int byteCount = SizeOf<T>(countInSource);
if (PerTypeValues<T>.IsPrimitiveType && PerTypeValues<TDest>.IsPrimitiveType)
{
Buffer.BlockCopy(source.Array, source.ByteOffset, destination.Array, destination.ByteOffset, byteCount);
return;
}
ref byte destRef = ref Unsafe.As<TDest, byte>(ref destination.DangerousGetPinnableReference());
fixed (void* pinnedDest = &destRef)
{
#if !NETSTANDARD1_1
ref byte srcRef = ref Unsafe.As<T, byte>(ref source.DangerousGetPinnableReference());
fixed (void* pinnedSrc = &srcRef)
{
Buffer.MemoryCopy(pinnedSrc, pinnedDest, byteCount, byteCount);
return;
}
#else
if (byteCount > ByteCountThreshold)
{
IntPtr ptr = (IntPtr)pinnedDest;
if (Unsafe.SizeOf<T>() == sizeof(long))
{
Marshal.Copy(Unsafe.As<long[]>(source.Array), source.Start, ptr, countInSource);
return;
}
else if (Unsafe.SizeOf<T>() == sizeof(int))
{
Marshal.Copy(Unsafe.As<int[]>(source.Array), source.Start, ptr, countInSource);
return;
}
else if (Unsafe.SizeOf<T>() == sizeof(short))
{
Marshal.Copy(Unsafe.As<short[]>(source.Array), source.Start, ptr, countInSource);
return;
}
else if (Unsafe.SizeOf<T>() == sizeof(byte))
{
Marshal.Copy(Unsafe.As<byte[]>(source.Array), source.Start, ptr, countInSource);
return;
}
}
ref byte srcRef = ref Unsafe.As<T, byte>(ref source.DangerousGetPinnableReference());
fixed (void* pinnedSrc = &srcRef)
{
Unsafe.CopyBlock(pinnedSrc, pinnedDest, (uint)byteCount);
}
#endif
}
}
/// <summary>
/// Per-type static value cache for type 'T'
/// </summary>
/// <typeparam name="T">The type</typeparam>
internal class PerTypeValues<T>
{
/// <summary>
/// Gets a value indicating whether 'T' is a primitive type.
/// </summary>
public static readonly bool IsPrimitiveType =
typeof(T) == typeof(byte) ||
typeof(T) == typeof(char) ||
typeof(T) == typeof(short) ||
typeof(T) == typeof(ushort) ||
typeof(T) == typeof(int) ||
typeof(T) == typeof(uint) ||
typeof(T) == typeof(float) ||
typeof(T) == typeof(double) ||
typeof(T) == typeof(long) ||
typeof(T) == typeof(ulong) ||
typeof(T) == typeof(IntPtr) ||
typeof(T) == typeof(UIntPtr);
}
}
}

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

@ -106,23 +106,23 @@ namespace ImageSharp
get
{
DebugGuard.MustBeLessThan(index, this.Length, nameof(index));
ref T arrayRef = ref this.DangerousGetPinnableReference();
return ref Unsafe.Add(ref arrayRef, this.Start);
ref T startRef = ref this.DangerousGetPinnableReference();
return ref Unsafe.Add(ref startRef, index);
}
}
/// <summary>
/// Converts generic <see cref="BufferSpan{T}"/> to a <see cref="BufferSpan{T}"/> of bytes
/// setting it's <see cref="Start"/> to correct value.
/// setting it's <see cref="Start"/> and <see cref="Length"/> to correct values.
/// </summary>
/// <param name="source">The <see cref="BufferSpan{T}"/> to convert</param>
/// <returns>The span of bytes</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator BufferSpan<byte>(BufferSpan<T> source)
public BufferSpan<byte> AsBytes()
{
BufferSpan<byte> result = default(BufferSpan<byte>);
result.Array = Unsafe.As<byte[]>(source.Array);
result.Start = source.Start * Unsafe.SizeOf<T>();
result.Length = source.Length * Unsafe.SizeOf<T>();
result.Array = Unsafe.As<byte[]>(this.Array);
result.Start = this.Start * Unsafe.SizeOf<T>();
result.Length = this.Length * Unsafe.SizeOf<T>();
return result;
}

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

@ -18,7 +18,7 @@ namespace ImageSharp.Tests.Colors
}
// For 4.6 test runner MemberData does not work without redeclaring the public field in the derived test class:
public static new TheoryData<int> ArraySizesData => new TheoryData<int> { 7, 16, 1111 };
//public static new TheoryData<int> ArraySizesData => new TheoryData<int> { 7, 16, 1111 };
[Fact]
public void IsSpecialImplementation()
@ -66,7 +66,7 @@ namespace ImageSharp.Tests.Colors
{
}
public static new TheoryData<int> ArraySizesData => new TheoryData<int> { 7, 16, 1111 };
//public static new TheoryData<int> ArraySizesData => new TheoryData<int> { 7, 16, 1111 };
}
[Theory]

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

@ -31,13 +31,14 @@ namespace ImageSharp.Tests.Common
using (PinnedBuffer<Foo> colorBuf = new PinnedBuffer<Foo>(fooz))
{
BufferSpan<Foo> orig = colorBuf.Slice(1);
BufferSpan<byte> asBytes = (BufferSpan<byte>)orig;
BufferSpan<byte> asBytes = orig.AsBytes();
Assert.Equal(asBytes.Start, sizeof(Foo));
Assert.Equal(orig.Length * Unsafe.SizeOf<Foo>(), asBytes.Length);
Assert.SameRefs(ref orig.DangerousGetPinnableReference(), ref asBytes.DangerousGetPinnableReference());
}
}
public class Construct
{
[Fact]
@ -186,6 +187,26 @@ namespace ImageSharp.Tests.Common
Assert.Equal(new Foo(666, 666), a[start + index]);
}
[Theory]
[InlineData(10, 0, 0, 5)]
[InlineData(10, 1, 1, 5)]
[InlineData(10, 1, 1, 6)]
[InlineData(10, 1, 1, 7)]
public void AsBytes_Read(int length, int start, int index, int byteOffset)
{
Foo[] a = Foo.CreateArray(length);
BufferSpan<Foo> span = new BufferSpan<Foo>(a, start);
BufferSpan<byte> bytes = span.AsBytes();
byte actual = bytes[index * Unsafe.SizeOf<Foo>() + byteOffset];
ref byte baseRef = ref Unsafe.As<Foo, byte>(ref a[0]);
byte expected = Unsafe.Add(ref baseRef, (start + index) * Unsafe.SizeOf<Foo>() + byteOffset);
Assert.Equal(expected, actual);
}
}
[Theory]
@ -310,7 +331,7 @@ namespace ImageSharp.Tests.Common
BufferSpan<Foo> apSource = new BufferSpan<Foo>(source, 1);
BufferSpan<byte> apDest = new BufferSpan<byte>(dest, sizeof(Foo));
BufferSpan.Copy(apSource, apDest, count - 1);
BufferSpan.Copy(apSource.AsBytes(), apDest, (count - 1)*sizeof(Foo));
AssertNotDefault(source, 1);
@ -333,7 +354,7 @@ namespace ImageSharp.Tests.Common
BufferSpan<AlignedFoo> apSource = new BufferSpan<AlignedFoo>(source, 1);
BufferSpan<byte> apDest = new BufferSpan<byte>(dest, sizeof(AlignedFoo));
BufferSpan.Copy(apSource, apDest, count - 1);
BufferSpan.Copy(apSource.AsBytes(), apDest, (count - 1) * sizeof(AlignedFoo));
AssertNotDefault(source, 1);
@ -356,7 +377,7 @@ namespace ImageSharp.Tests.Common
BufferSpan<int> apSource = new BufferSpan<int>(source);
BufferSpan<byte> apDest = new BufferSpan<byte>(dest);
BufferSpan.Copy(apSource, apDest, count);
BufferSpan.Copy(apSource.AsBytes(), apDest, count*sizeof(int));
AssertNotDefault(source, 1);
@ -377,7 +398,7 @@ namespace ImageSharp.Tests.Common
BufferSpan<byte> apSource = new BufferSpan<byte>(source);
BufferSpan<Foo> apDest = new BufferSpan<Foo>(dest);
BufferSpan.Copy(apSource, apDest, count);
BufferSpan.Copy(apSource, apDest.AsBytes(), count*sizeof(Foo));
AssertNotDefault(source, sizeof(Foo) + 1);
AssertNotDefault(dest, 1);
@ -396,7 +417,7 @@ namespace ImageSharp.Tests.Common
using (PinnedBuffer<Color> colorBuf = new PinnedBuffer<Color>(colors))
using (PinnedBuffer<byte> byteBuf = new PinnedBuffer<byte>(colors.Length * 4))
{
BufferSpan.Copy<Color>(colorBuf, byteBuf, colorBuf.Length);
BufferSpan.Copy(colorBuf.Span.AsBytes(), byteBuf, colorBuf.Length*sizeof(Color));
byte[] a = byteBuf.Array;

2
tests/ImageSharp.Tests/Common/TestStructs.cs

@ -25,6 +25,8 @@ namespace ImageSharp.Tests.Common
}
return result;
}
public override string ToString() => $"({this.A},{this.B})";
}

Loading…
Cancel
Save