Browse Source

refactored all BufferSpan pointers to ref-s

af/merge-core
Anton Firszov 9 years ago
parent
commit
4854005733
  1. 171
      src/ImageSharp/Colors/Color.BulkOperations.cs
  2. 5
      src/ImageSharp/Colors/Color.cs
  3. 132
      src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs
  4. 116
      src/ImageSharp/Common/Memory/BufferSpan.cs
  5. 71
      src/ImageSharp/Common/Memory/BufferSpan{T}.cs
  6. 12
      src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs
  7. 28
      src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs
  8. 11
      src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs
  9. 243
      tests/ImageSharp.Tests/Common/BufferSpanTests.cs
  10. 23
      tests/ImageSharp.Tests/Common/PinnedBufferTests.cs
  11. 19
      tests/ImageSharp.Tests/Common/PinnedImageBufferTests.cs

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

@ -62,21 +62,20 @@ namespace ImageSharp
int unpackedRawCount = count * 4;
uint* src = (uint*)sourceColors.PointerAtOffset;
uint* srcEnd = src + count;
ref uint src = ref Unsafe.As<Color, uint>(ref sourceColors.DangerousGetPinnableReference());
using (PinnedBuffer<uint> tempBuf = new PinnedBuffer<uint>(
unpackedRawCount + Vector<uint>.Count))
using (PinnedBuffer<uint> tempBuf = new PinnedBuffer<uint>(unpackedRawCount + Vector<uint>.Count))
{
uint* tPtr = (uint*)tempBuf.Pointer;
uint[] temp = tempBuf.Array;
float[] fTemp = Unsafe.As<float[]>(temp);
UnpackedRGBA* dst = (UnpackedRGBA*)tPtr;
for (; src < srcEnd; src++, dst++)
for (int i = 0; i < count; i++)
{
// This call is the bottleneck now:
dst->Load(*src);
ref uint sp = ref Unsafe.Add(ref src, i);
dst->Load(sp);
}
for (int i = 0; i < unpackedRawCount; i += vecSize)
@ -123,39 +122,44 @@ namespace ImageSharp
}
/// <inheritdoc />
internal override unsafe void PackFromXyzBytes(BufferSpan<byte> sourceBytes, BufferSpan<Color> destColors, int count)
internal override void PackFromXyzBytes(
BufferSpan<byte> sourceBytes,
BufferSpan<Color> destColors,
int count)
{
byte* source = (byte*)sourceBytes;
byte* destination = (byte*)destColors;
ref RGB24 sourceRef = ref Unsafe.As<byte, RGB24>(ref sourceBytes.DangerousGetPinnableReference());
ref Color destRef = ref destColors.DangerousGetPinnableReference();
for (int x = 0; x < count; x++)
for (int i = 0; i < count; i++)
{
Unsafe.Write(destination, (uint)(*source << 0 | *(source + 1) << 8 | *(source + 2) << 16 | 255 << 24));
ref RGB24 sp = ref Unsafe.Add(ref sourceRef, i);
ref Color dp = ref Unsafe.Add(ref destRef, i);
source += 3;
destination += 4;
Unsafe.As<Color, RGB24>(ref dp) = sp;
dp.A = 255;
}
}
/// <inheritdoc />
internal override unsafe void ToXyzBytes(BufferSpan<Color> sourceColors, BufferSpan<byte> destBytes, int count)
internal override void ToXyzBytes(BufferSpan<Color> sourceColors, BufferSpan<byte> destBytes, int count)
{
byte* source = (byte*)sourceColors;
byte* destination = (byte*)destBytes;
ref Color sourceRef = ref sourceColors.DangerousGetPinnableReference();
ref RGB24 destRef = ref Unsafe.As<byte, RGB24>(ref destBytes.DangerousGetPinnableReference());
for (int x = 0; x < count; x++)
for (int i = 0; i < count; i++)
{
*destination = *(source + 0);
*(destination + 1) = *(source + 1);
*(destination + 2) = *(source + 2);
ref Color sp = ref Unsafe.Add(ref sourceRef, i);
ref RGB24 dp = ref Unsafe.Add(ref destRef, i);
source += 4;
destination += 3;
dp = Unsafe.As<Color, RGB24>(ref sp);
}
}
/// <inheritdoc />
internal override void PackFromXyzwBytes(BufferSpan<byte> sourceBytes, BufferSpan<Color> destColors, int count)
internal override void PackFromXyzwBytes(
BufferSpan<byte> sourceBytes,
BufferSpan<Color> destColors,
int count)
{
BufferSpan.Copy(sourceBytes, destColors, count);
}
@ -167,87 +171,132 @@ namespace ImageSharp
}
/// <inheritdoc />
internal override unsafe void PackFromZyxBytes(BufferSpan<byte> sourceBytes, BufferSpan<Color> destColors, int count)
internal override void PackFromZyxBytes(
BufferSpan<byte> sourceBytes,
BufferSpan<Color> destColors,
int count)
{
byte* source = (byte*)sourceBytes;
byte* destination = (byte*)destColors;
ref RGB24 sourceRef = ref Unsafe.As<byte, RGB24>(ref sourceBytes.DangerousGetPinnableReference());
ref Color destRef = ref destColors.DangerousGetPinnableReference();
for (int x = 0; x < count; x++)
for (int i = 0; i < count; i++)
{
Unsafe.Write(destination, (uint)(*(source + 2) << 0 | *(source + 1) << 8 | *source << 16 | 255 << 24));
ref RGB24 sp = ref Unsafe.Add(ref sourceRef, i);
ref Color dp = ref Unsafe.Add(ref destRef, i);
source += 3;
destination += 4;
Unsafe.As<Color, RGB24>(ref dp) = sp.ToZyx();
dp.A = 255;
}
}
/// <inheritdoc />
internal override unsafe void ToZyxBytes(BufferSpan<Color> sourceColors, BufferSpan<byte> destBytes, int count)
internal override void ToZyxBytes(
BufferSpan<Color> sourceColors,
BufferSpan<byte> destBytes,
int count)
{
byte* source = (byte*)sourceColors;
byte* destination = (byte*)destBytes;
ref Color sourceRef = ref sourceColors.DangerousGetPinnableReference();
ref RGB24 destRef = ref Unsafe.As<byte, RGB24>(ref destBytes.DangerousGetPinnableReference());
for (int x = 0; x < count; x++)
for (int i = 0; i < count; i++)
{
*destination = *(source + 2);
*(destination + 1) = *(source + 1);
*(destination + 2) = *(source + 0);
ref Color sp = ref Unsafe.Add(ref sourceRef, i);
ref RGB24 dp = ref Unsafe.Add(ref destRef, i);
source += 4;
destination += 3;
dp = Unsafe.As<Color, RGB24>(ref sp).ToZyx();
}
}
/// <inheritdoc />
internal override unsafe void PackFromZyxwBytes(BufferSpan<byte> sourceBytes, BufferSpan<Color> destColors, int count)
internal override void PackFromZyxwBytes(
BufferSpan<byte> sourceBytes,
BufferSpan<Color> destColors,
int count)
{
byte* source = (byte*)sourceBytes;
byte* destination = (byte*)destColors;
ref RGBA32 sourceRef = ref Unsafe.As<byte, RGBA32>(ref sourceBytes.DangerousGetPinnableReference());
ref Color destRef = ref destColors.DangerousGetPinnableReference();
for (int x = 0; x < count; x++)
for (int i = 0; i < count; i++)
{
Unsafe.Write(destination, (uint)(*(source + 2) << 0 | *(source + 1) << 8 | *source << 16 | *(source + 3) << 24));
source += 4;
destination += 4;
ref RGBA32 sp = ref Unsafe.Add(ref sourceRef, i);
ref Color dp = ref Unsafe.Add(ref destRef, i);
RGBA32 zyxw = sp.ToZyxw();
dp = Unsafe.As<RGBA32, Color>(ref zyxw);
}
}
/// <inheritdoc />
internal override unsafe void ToZyxwBytes(BufferSpan<Color> sourceColors, BufferSpan<byte> destBytes, int count)
internal override void ToZyxwBytes(
BufferSpan<Color> sourceColors,
BufferSpan<byte> destBytes,
int count)
{
byte* source = (byte*)sourceColors;
byte* destination = (byte*)destBytes;
ref Color sourceRef = ref sourceColors.DangerousGetPinnableReference();
ref RGBA32 destRef = ref Unsafe.As<byte, RGBA32>(ref destBytes.DangerousGetPinnableReference());
for (int x = 0; x < count; x++)
for (int i = 0; i < count; i++)
{
*destination = *(source + 2);
*(destination + 1) = *(source + 1);
*(destination + 2) = *(source + 0);
*(destination + 3) = *(source + 3);
source += 4;
destination += 4;
ref RGBA32 sp = ref Unsafe.As<Color, RGBA32>(ref Unsafe.Add(ref sourceRef, i));
ref RGBA32 dp = ref Unsafe.Add(ref destRef, i);
dp = sp.ToZyxw();
}
}
/// <summary>
/// Helper struct to manipulate 3-byte RGB data.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct RGB24
{
private byte x;
private byte y;
private byte z;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public RGB24 ToZyx() => new RGB24 { x = this.z, y = this.y, z = this.x };
}
/// <summary>
/// Helper struct to manipulate 4-byte RGBA data.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct RGBA32
{
private byte x;
private byte y;
private byte z;
private byte w;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public RGBA32 ToZyxw() => new RGBA32 { x = this.z, y = this.y, z = this.x, w = this.w };
}
/// <summary>
/// Value type to store <see cref="Color"/>-s unpacked into multiple <see cref="uint"/>-s.
/// </summary>
[StructLayout(LayoutKind.Sequential)]
private struct UnpackedRGBA
{
private uint r;
private uint g;
private uint b;
private uint a;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Load(uint p)
{
this.r = p;
this.g = p >> Color.GreenShift;
this.b = p >> Color.BlueShift;
this.a = p >> Color.AlphaShift;
this.g = p >> GreenShift;
this.b = p >> BlueShift;
this.a = p >> AlphaShift;
}
}
}

5
src/ImageSharp/Colors/Color.cs

@ -62,6 +62,7 @@ namespace ImageSharp
/// <param name="g">The green component.</param>
/// <param name="b">The blue component.</param>
/// <param name="a">The alpha component.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Color(byte r, byte g, byte b, byte a = 255)
{
this.packedValue = Pack(r, g, b, a);
@ -74,6 +75,7 @@ namespace ImageSharp
/// <param name="g">The green component.</param>
/// <param name="b">The blue component.</param>
/// <param name="a">The alpha component.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Color(float r, float g, float b, float a = 1)
{
this.packedValue = Pack(r, g, b, a);
@ -85,6 +87,7 @@ namespace ImageSharp
/// <param name="vector">
/// The vector containing the components for the packed vector.
/// </param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Color(Vector3 vector)
{
this.packedValue = Pack(ref vector);
@ -96,6 +99,7 @@ namespace ImageSharp
/// <param name="vector">
/// The vector containing the components for the packed vector.
/// </param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Color(Vector4 vector)
{
this.packedValue = Pack(ref vector);
@ -107,6 +111,7 @@ namespace ImageSharp
/// <param name="packed">
/// The packed value.
/// </param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Color(uint packed)
{
this.packedValue = packed;

132
src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs

@ -13,14 +13,9 @@ namespace ImageSharp
/// for pixel buffers of type <typeparamref name="TColor"/>.
/// </summary>
/// <typeparam name="TColor">The pixel format.</typeparam>
public unsafe class BulkPixelOperations<TColor>
public class BulkPixelOperations<TColor>
where TColor : struct, IPixel<TColor>
{
/// <summary>
/// The size of <typeparamref name="TColor"/> in bytes
/// </summary>
private static readonly int ColorSize = Unsafe.SizeOf<TColor>();
/// <summary>
/// Gets the global <see cref="BulkPixelOperations{TColor}"/> instance for the pixel type <typeparamref name="TColor"/>
/// </summary>
@ -37,18 +32,14 @@ namespace ImageSharp
BufferSpan<TColor> destColors,
int count)
{
Vector4* sp = (Vector4*)sourceVectors.PointerAtOffset;
byte* dp = (byte*)destColors;
ref Vector4 sourceRef = ref sourceVectors.DangerousGetPinnableReference();
ref TColor destRef = ref destColors.DangerousGetPinnableReference();
for (int i = 0; i < count; i++)
{
Vector4 v = Unsafe.Read<Vector4>(sp);
TColor c = default(TColor);
c.PackFromVector4(v);
Unsafe.Write(dp, c);
sp++;
dp += ColorSize;
ref Vector4 sp = ref Unsafe.Add(ref sourceRef, i);
ref TColor dp = ref Unsafe.Add(ref destRef, i);
dp.PackFromVector4(sp);
}
}
@ -63,15 +54,14 @@ namespace ImageSharp
BufferSpan<Vector4> destVectors,
int count)
{
byte* sp = (byte*)sourceColors;
Vector4* dp = (Vector4*)destVectors.PointerAtOffset;
ref TColor sourceRef = ref sourceColors.DangerousGetPinnableReference();
ref Vector4 destRef = ref destVectors.DangerousGetPinnableReference();
for (int i = 0; i < count; i++)
{
TColor c = Unsafe.Read<TColor>(sp);
*dp = c.ToVector4();
sp += ColorSize;
dp++;
ref TColor sp = ref Unsafe.Add(ref sourceRef, i);
ref Vector4 dp = ref Unsafe.Add(ref destRef, i);
dp = sp.ToVector4();
}
}
@ -86,16 +76,18 @@ namespace ImageSharp
BufferSpan<TColor> destColors,
int count)
{
byte* sp = (byte*)sourceBytes;
byte* dp = (byte*)destColors.PointerAtOffset;
ref byte sourceRef = ref sourceBytes.DangerousGetPinnableReference();
ref TColor destRef = ref destColors.DangerousGetPinnableReference();
for (int i = 0; i < count; i++)
{
TColor c = default(TColor);
c.PackFromBytes(sp[0], sp[1], sp[2], 255);
Unsafe.Write(dp, c);
sp += 3;
dp += ColorSize;
int i3 = i * 3;
ref TColor dp = ref Unsafe.Add(ref destRef, i);
dp.PackFromBytes(
Unsafe.Add(ref sourceRef, i3),
Unsafe.Add(ref sourceRef, i3 + 1),
Unsafe.Add(ref sourceRef, i3 + 2),
255);
}
}
@ -107,14 +99,13 @@ namespace ImageSharp
/// <param name="count">The number of pixels to convert.</param>
internal virtual void ToXyzBytes(BufferSpan<TColor> sourceColors, BufferSpan<byte> destBytes, int count)
{
byte* sp = (byte*)sourceColors;
ref TColor sourceRef = ref sourceColors.DangerousGetPinnableReference();
byte[] dest = destBytes.Array;
for (int i = destBytes.Start; i < destBytes.Start + (count * 3); i += 3)
for (int i = 0; i < count; i++)
{
TColor c = Unsafe.Read<TColor>(sp);
c.ToXyzBytes(dest, i);
sp += ColorSize;
ref TColor sp = ref Unsafe.Add(ref sourceRef, i);
sp.ToXyzBytes(dest, i * 3);
}
}
@ -129,16 +120,18 @@ namespace ImageSharp
BufferSpan<TColor> destColors,
int count)
{
byte* sp = (byte*)sourceBytes;
byte* dp = (byte*)destColors.PointerAtOffset;
ref byte sourceRef = ref sourceBytes.DangerousGetPinnableReference();
ref TColor destRef = ref destColors.DangerousGetPinnableReference();
for (int i = 0; i < count; i++)
{
TColor c = default(TColor);
c.PackFromBytes(sp[0], sp[1], sp[2], sp[3]);
Unsafe.Write(dp, c);
sp += 4;
dp += ColorSize;
int i4 = i * 4;
ref TColor dp = ref Unsafe.Add(ref destRef, i);
dp.PackFromBytes(
Unsafe.Add(ref sourceRef, i4),
Unsafe.Add(ref sourceRef, i4 + 1),
Unsafe.Add(ref sourceRef, i4 + 2),
Unsafe.Add(ref sourceRef, i4 + 3));
}
}
@ -153,14 +146,13 @@ namespace ImageSharp
BufferSpan<byte> destBytes,
int count)
{
byte* sp = (byte*)sourceColors;
ref TColor sourceRef = ref sourceColors.DangerousGetPinnableReference();
byte[] dest = destBytes.Array;
for (int i = destBytes.Start; i < destBytes.Start + (count * 4); i += 4)
for (int i = 0; i < count; i++)
{
TColor c = Unsafe.Read<TColor>(sp);
c.ToXyzwBytes(dest, i);
sp += ColorSize;
ref TColor sp = ref Unsafe.Add(ref sourceRef, i);
sp.ToXyzwBytes(dest, i * 4);
}
}
@ -175,16 +167,18 @@ namespace ImageSharp
BufferSpan<TColor> destColors,
int count)
{
byte* sp = (byte*)sourceBytes;
byte* dp = (byte*)destColors.PointerAtOffset;
ref byte sourceRef = ref sourceBytes.DangerousGetPinnableReference();
ref TColor destRef = ref destColors.DangerousGetPinnableReference();
for (int i = 0; i < count; i++)
{
TColor c = default(TColor);
c.PackFromBytes(sp[2], sp[1], sp[0], 255);
Unsafe.Write(dp, c);
sp += 3;
dp += ColorSize;
int i3 = i * 3;
ref TColor dp = ref Unsafe.Add(ref destRef, i);
dp.PackFromBytes(
Unsafe.Add(ref sourceRef, i3 + 2),
Unsafe.Add(ref sourceRef, i3 + 1),
Unsafe.Add(ref sourceRef, i3),
255);
}
}
@ -196,14 +190,13 @@ namespace ImageSharp
/// <param name="count">The number of pixels to convert.</param>
internal virtual void ToZyxBytes(BufferSpan<TColor> sourceColors, BufferSpan<byte> destBytes, int count)
{
byte* sp = (byte*)sourceColors;
ref TColor sourceRef = ref sourceColors.DangerousGetPinnableReference();
byte[] dest = destBytes.Array;
for (int i = destBytes.Start; i < destBytes.Start + (count * 3); i += 3)
for (int i = 0; i < count; i++)
{
TColor c = Unsafe.Read<TColor>(sp);
c.ToZyxBytes(dest, i);
sp += ColorSize;
ref TColor sp = ref Unsafe.Add(ref sourceRef, i);
sp.ToZyxBytes(dest, i * 3);
}
}
@ -218,16 +211,18 @@ namespace ImageSharp
BufferSpan<TColor> destColors,
int count)
{
byte* sp = (byte*)sourceBytes;
byte* dp = (byte*)destColors.PointerAtOffset;
ref byte sourceRef = ref sourceBytes.DangerousGetPinnableReference();
ref TColor destRef = ref destColors.DangerousGetPinnableReference();
for (int i = 0; i < count; i++)
{
TColor c = default(TColor);
c.PackFromBytes(sp[2], sp[1], sp[0], sp[3]);
Unsafe.Write(dp, c);
sp += 4;
dp += ColorSize;
int i4 = i * 4;
ref TColor dp = ref Unsafe.Add(ref destRef, i);
dp.PackFromBytes(
Unsafe.Add(ref sourceRef, i4 + 2),
Unsafe.Add(ref sourceRef, i4 + 1),
Unsafe.Add(ref sourceRef, i4),
Unsafe.Add(ref sourceRef, i4 + 3));
}
}
@ -242,14 +237,13 @@ namespace ImageSharp
BufferSpan<byte> destBytes,
int count)
{
byte* sp = (byte*)sourceColors;
ref TColor sourceRef = ref sourceColors.DangerousGetPinnableReference();
byte[] dest = destBytes.Array;
for (int i = destBytes.Start; i < destBytes.Start + (count * 4); i += 4)
for (int i = 0; i < count; i++)
{
TColor c = Unsafe.Read<TColor>(sp);
c.ToZyxwBytes(dest, i);
sp += ColorSize;
ref TColor sp = ref Unsafe.Add(ref sourceRef, i);
sp.ToZyxwBytes(dest, i * 4);
}
}
}

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

@ -6,7 +6,6 @@
namespace ImageSharp
{
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
@ -49,25 +48,42 @@ namespace ImageSharp
}
/// <summary>
/// Copy 'countInDest' number of <typeparamref name="T"/> elements into 'dest' from a raw byte buffer defined by 'source'.
/// Copy 'countInDest' number of <typeparamref name="TDest"/> elements into 'dest' from a raw byte buffer defined by 'source'.
/// </summary>
/// <typeparam name="T">The element type.</typeparam>
/// <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="T"/> elements to copy.</param>
/// <param name="countInDest">The number of <typeparamref name="TDest"/> elements to copy.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void Copy<T>(BufferSpan<byte> source, BufferSpan<T> destination, int countInDest)
where T : struct
public static unsafe void Copy<TDest>(BufferSpan<byte> source, BufferSpan<TDest> destination, int countInDest)
where TDest : struct
{
int byteCount = SizeOf<T>(countInDest);
// TODO: Refactor this method when Unsafe.CopyBlock(ref T, ref T) gets available!
int byteCount = SizeOf<TDest>(countInDest);
if (byteCount > (int)ByteCountThreshold)
if (PerTypeValues<TDest>.IsPrimitiveType)
{
Marshal.Copy(source.Array, source.Start, destination.PointerAtOffset, byteCount);
Buffer.BlockCopy(source.Array, source.ByteOffset, destination.Array, destination.ByteOffset, byteCount);
}
else if (byteCount > ByteCountThreshold)
{
ref byte destRef = ref Unsafe.As<TDest, byte>(ref destination.DangerousGetPinnableReference());
fixed (void* pinnedPtr = &destRef)
{
Marshal.Copy(source.Array, source.Start, (IntPtr)pinnedPtr, byteCount);
}
}
else
{
Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, (uint)byteCount);
ref byte srcRef = ref source.DangerousGetPinnableReference();
ref byte destRef = ref Unsafe.As<TDest, byte>(ref destination.DangerousGetPinnableReference());
fixed (void* pinnedSrc = &srcRef)
fixed (void* pinnedDest = &destRef)
{
Unsafe.CopyBlock(pinnedDest, pinnedSrc, (uint)byteCount);
}
}
}
@ -93,37 +109,77 @@ namespace ImageSharp
=> (uint)SizeOf<T>(count);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe void CopyImpl<T, TDest>(BufferSpan<T> source, BufferSpan<TDest> destination, int count)
private static unsafe void CopyImpl<T, TDest>(BufferSpan<T> source, BufferSpan<TDest> destination, int countInSource)
where T : struct
where TDest : struct
{
int byteCount = SizeOf<T>(count);
// TODO: Refactor this method when Unsafe.CopyBlock(ref T, ref T) gets available!
int byteCount = SizeOf<T>(countInSource);
if (byteCount > ByteCountThreshold)
if (PerTypeValues<T>.IsPrimitiveType && PerTypeValues<TDest>.IsPrimitiveType)
{
if (Unsafe.SizeOf<T>() == sizeof(long))
{
Marshal.Copy(Unsafe.As<long[]>(source.Array), source.Start, destination.PointerAtOffset, count);
return;
}
else if (Unsafe.SizeOf<T>() == sizeof(int))
{
Marshal.Copy(Unsafe.As<int[]>(source.Array), source.Start, destination.PointerAtOffset, count);
return;
}
else if (Unsafe.SizeOf<T>() == sizeof(short))
Buffer.BlockCopy(source.Array, source.ByteOffset, destination.Array, destination.ByteOffset, byteCount);
}
else if (byteCount > ByteCountThreshold)
{
ref byte destRef = ref Unsafe.As<TDest, byte>(ref destination.DangerousGetPinnableReference());
fixed (void* pinnedPtr = &destRef)
{
Marshal.Copy(Unsafe.As<short[]>(source.Array), source.Start, destination.PointerAtOffset, count);
return;
IntPtr ptr = (IntPtr)pinnedPtr;
if (Unsafe.SizeOf<T>() == sizeof(long))
{
Marshal.Copy(Unsafe.As<long[]>(source.Array), source.Start, ptr, countInSource);
}
else if (Unsafe.SizeOf<T>() == sizeof(int))
{
Marshal.Copy(Unsafe.As<int[]>(source.Array), source.Start, ptr, countInSource);
}
else if (Unsafe.SizeOf<T>() == sizeof(short))
{
Marshal.Copy(Unsafe.As<short[]>(source.Array), source.Start, ptr, countInSource);
}
else if (Unsafe.SizeOf<T>() == sizeof(byte))
{
Marshal.Copy(Unsafe.As<byte[]>(source.Array), source.Start, ptr, countInSource);
}
}
else if (Unsafe.SizeOf<T>() == sizeof(byte))
}
else
{
ref byte srcRef = ref Unsafe.As<T, byte>(ref source.DangerousGetPinnableReference());
ref byte destRef = ref Unsafe.As<TDest, byte>(ref destination.DangerousGetPinnableReference());
fixed (void* pinnedSrc = &srcRef)
fixed (void* pinnedDest = &destRef)
{
Marshal.Copy(Unsafe.As<byte[]>(source.Array), source.Start, destination.PointerAtOffset, count);
return;
Unsafe.CopyBlock(pinnedDest, pinnedSrc, (uint)byteCount);
}
}
}
Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, (uint)byteCount);
/// <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);
}
}
}

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

@ -30,13 +30,12 @@ namespace ImageSharp
/// Initializes a new instance of the <see cref="BufferSpan{T}"/> struct from a pinned array and an start.
/// </summary>
/// <param name="array">The pinned array</param>
/// <param name="pointerToArray">Pointer to the beginning of the array</param>
/// <param name="start">The index at which to begin the span.</param>
/// <param name="length">The length</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public BufferSpan(T[] array, void* pointerToArray, int start, int length)
public BufferSpan(T[] array, int start, int length)
{
GuardArrayAndPointer(array, pointerToArray);
GuardArray(array);
DebugGuard.MustBeLessThanOrEqualTo(start, array.Length, nameof(start));
DebugGuard.MustBeLessThanOrEqualTo(length, array.Length - start, nameof(length));
@ -44,41 +43,36 @@ namespace ImageSharp
this.Array = array;
this.Length = length;
this.Start = start;
this.PointerAtOffset = (IntPtr)pointerToArray + (Unsafe.SizeOf<T>() * start);
}
/// <summary>
/// Initializes a new instance of the <see cref="BufferSpan{T}"/> struct from a pinned array and an start.
/// </summary>
/// <param name="array">The pinned array</param>
/// <param name="pointerToArray">Pointer to the beginning of the array</param>
/// <param name="start">The index at which to begin the span.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public BufferSpan(T[] array, void* pointerToArray, int start)
public BufferSpan(T[] array, int start)
{
GuardArrayAndPointer(array, pointerToArray);
GuardArray(array);
DebugGuard.MustBeLessThanOrEqualTo(start, array.Length, nameof(start));
this.Array = array;
this.Length = array.Length - start;
this.Start = start;
this.PointerAtOffset = (IntPtr)pointerToArray + (Unsafe.SizeOf<T>() * start);
}
/// <summary>
/// Initializes a new instance of the <see cref="BufferSpan{T}"/> struct from a pinned array.
/// </summary>
/// <param name="array">The pinned array</param>
/// <param name="pointerToArray">Pointer to the start of 'array'</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public BufferSpan(T[] array, void* pointerToArray)
public BufferSpan(T[] array)
{
GuardArrayAndPointer(array, pointerToArray);
GuardArray(array);
this.Array = array;
this.Start = 0;
this.Length = array.Length;
this.PointerAtOffset = (IntPtr)pointerToArray;
}
/// <summary>
@ -101,11 +95,6 @@ namespace ImageSharp
/// </summary>
public int ByteOffset => this.Start * Unsafe.SizeOf<T>();
/// <summary>
/// Gets the pointer to the offseted array position
/// </summary>
public IntPtr PointerAtOffset { get; private set; }
/// <summary>
/// Returns a reference to specified element of the span.
/// </summary>
@ -117,35 +106,14 @@ namespace ImageSharp
get
{
DebugGuard.MustBeLessThan(index, this.Length, nameof(index));
byte* ptr = (byte*)this.PointerAtOffset + BufferSpan.SizeOf<T>(index);
return ref Unsafe.AsRef<T>(ptr);
ref T arrayRef = ref this.DangerousGetPinnableReference();
return ref Unsafe.Add(ref arrayRef, this.Start);
}
}
/// <summary>
/// Convertes <see cref="BufferSpan{T}"/> instance to a raw 'void*' pointer
/// </summary>
/// <param name="bufferSpan">The <see cref="BufferSpan{T}"/> to convert</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator void*(BufferSpan<T> bufferSpan)
{
return (void*)bufferSpan.PointerAtOffset;
}
/// <summary>
/// Converts <see cref="BufferSpan{T}"/> instance to a raw 'byte*' pointer
/// </summary>
/// <param name="bufferSpan">The <see cref="BufferSpan{T}"/> to convert</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator byte*(BufferSpan<T> bufferSpan)
{
return (byte*)bufferSpan.PointerAtOffset;
}
/// <summary>
/// Converts generic <see cref="BufferSpan{T}"/> to a <see cref="BufferSpan{T}"/> of bytes
/// setting it's <see cref="Start"/> and <see cref="PointerAtOffset"/> to correct values.
/// setting it's <see cref="Start"/> to correct value.
/// </summary>
/// <param name="source">The <see cref="BufferSpan{T}"/> to convert</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -154,7 +122,6 @@ namespace ImageSharp
BufferSpan<byte> result = default(BufferSpan<byte>);
result.Array = Unsafe.As<byte[]>(source.Array);
result.Start = source.Start * Unsafe.SizeOf<T>();
result.PointerAtOffset = source.PointerAtOffset;
return result;
}
@ -164,7 +131,7 @@ namespace ImageSharp
/// </summary>
/// <returns>The reference to the 0th element</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public ref T DangerousGetPinnableReference() => ref Unsafe.AsRef<T>((void*)this.PointerAtOffset);
public ref T DangerousGetPinnableReference() => ref this.Array[this.Start];
/// <summary>
/// Forms a slice out of the given BufferSpan, beginning at 'start'.
@ -179,7 +146,6 @@ namespace ImageSharp
BufferSpan<T> result = default(BufferSpan<T>);
result.Array = this.Array;
result.Start = this.Start + start;
result.PointerAtOffset = this.PointerAtOffset + (Unsafe.SizeOf<T>() * start);
result.Length = this.Length - start;
return result;
}
@ -199,7 +165,6 @@ namespace ImageSharp
BufferSpan<T> result = default(BufferSpan<T>);
result.Array = this.Array;
result.Start = this.Start + start;
result.PointerAtOffset = this.PointerAtOffset + (Unsafe.SizeOf<T>() * start);
result.Length = length;
return result;
}
@ -213,14 +178,8 @@ namespace ImageSharp
{
DebugGuard.MustBeLessThanOrEqualTo(count, this.Length, nameof(count));
if (count < 256)
{
Unsafe.InitBlock((void*)this.PointerAtOffset, 0, BufferSpan.USizeOf<T>(count));
}
else
{
System.Array.Clear(this.Array, this.Start, count);
}
// TODO: Use Unsafe.InitBlock(ref T) for small arrays, when it get's official
System.Array.Clear(this.Array, this.Start, count);
}
/// <summary>
@ -233,13 +192,9 @@ namespace ImageSharp
}
[Conditional("DEBUG")]
private static void GuardArrayAndPointer(T[] array, void* pointerToArray)
private static void GuardArray(T[] array)
{
DebugGuard.NotNull(array, nameof(array));
DebugGuard.IsFalse(
pointerToArray == (void*)0,
nameof(pointerToArray),
"pointerToArray should not be null pointer!");
}
}
}

12
src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs

@ -126,9 +126,9 @@ namespace ImageSharp
/// </summary>
/// <param name="buffer">The <see cref="PinnedBuffer{T}"/> to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe implicit operator BufferSpan<T>(PinnedBuffer<T> buffer)
public static implicit operator BufferSpan<T>(PinnedBuffer<T> buffer)
{
return new BufferSpan<T>(buffer.Array, (void*)buffer.Pointer, 0, buffer.Length);
return new BufferSpan<T>(buffer.Array, 0, buffer.Length);
}
/// <summary>
@ -150,9 +150,9 @@ namespace ImageSharp
/// <param name="start">The start</param>
/// <returns>The <see cref="BufferSpan{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe BufferSpan<T> Slice(int start)
public BufferSpan<T> Slice(int start)
{
return new BufferSpan<T>(this.Array, (void*)this.Pointer, start, this.Length - start);
return new BufferSpan<T>(this.Array, start, this.Length - start);
}
/// <summary>
@ -162,9 +162,9 @@ namespace ImageSharp
/// <param name="length">The length of the slice</param>
/// <returns>The <see cref="BufferSpan{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe BufferSpan<T> Slice(int start, int length)
public BufferSpan<T> Slice(int start, int length)
{
return new BufferSpan<T>(this.Array, (void*)this.Pointer, start, length);
return new BufferSpan<T>(this.Array, start, length);
}
/// <summary>

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

@ -41,7 +41,7 @@ namespace ImageSharp.Processing.Processors
/// <summary>
/// Gets an unsafe float* pointer to the beginning of <see cref="Span"/>.
/// </summary>
public float* Ptr => (float*)this.Span.PointerAtOffset;
public ref float Ptr => ref this.Span.DangerousGetPinnableReference();
/// <summary>
/// Gets the lenghth of the weights window
@ -56,19 +56,18 @@ namespace ImageSharp.Processing.Processors
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ComputeWeightedRowSum(BufferSpan<Vector4> rowSpan)
{
float* horizontalValues = this.Ptr;
ref float horizontalValues = ref this.Ptr;
int left = this.Left;
Vector4* vecPtr = (Vector4*)rowSpan.PointerAtOffset;
vecPtr += left;
ref Vector4 vecPtr = ref Unsafe.Add(ref rowSpan.DangerousGetPinnableReference(), left);
// Destination color components
Vector4 result = Vector4.Zero;
for (int i = 0; i < this.Length; i++)
{
float weight = horizontalValues[i];
result += (*vecPtr) * weight;
vecPtr++;
float weight = Unsafe.Add(ref horizontalValues, i);
Vector4 v = Unsafe.Add(ref vecPtr, i);
result += v * weight;
}
return result;
@ -83,19 +82,18 @@ namespace ImageSharp.Processing.Processors
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ComputeExpandedWeightedRowSum(BufferSpan<Vector4> rowSpan)
{
float* horizontalValues = this.Ptr;
ref float horizontalValues = ref this.Ptr;
int left = this.Left;
Vector4* vecPtr = (Vector4*)rowSpan.PointerAtOffset;
vecPtr += left;
ref Vector4 vecPtr = ref Unsafe.Add(ref rowSpan.DangerousGetPinnableReference(), left);
// Destination color components
Vector4 result = Vector4.Zero;
for (int i = 0; i < this.Length; i++)
{
float weight = horizontalValues[i];
result += (*vecPtr).Expand() * weight;
vecPtr++;
float weight = Unsafe.Add(ref horizontalValues, i);
Vector4 v = Unsafe.Add(ref vecPtr, i);
result += v.Expand() * weight;
}
return result;
@ -111,7 +109,7 @@ namespace ImageSharp.Processing.Processors
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Vector4 ComputeWeightedColumnSum(PinnedImageBuffer<Vector4> firstPassPixels, int x)
{
float* verticalValues = this.Ptr;
ref float verticalValues = ref this.Ptr;
int left = this.Left;
// Destination color components
@ -119,7 +117,7 @@ namespace ImageSharp.Processing.Processors
for (int i = 0; i < this.Length; i++)
{
float yw = verticalValues[i];
float yw = Unsafe.Add(ref verticalValues, i);
int index = left + i;
result += firstPassPixels[x, index] * yw;
}

11
src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs

@ -7,6 +7,7 @@ namespace ImageSharp.Processing.Processors
{
using System;
using System.Buffers;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
/// <summary>
@ -111,13 +112,15 @@ namespace ImageSharp.Processing.Processors
WeightsWindow ws = result.GetWeightsWindow(i, left, right);
result.Weights[i] = ws;
float* weights = ws.Ptr;
ref float weights = ref ws.Ptr;
for (int j = left; j <= right; j++)
{
float weight = sampler.GetValue((j - center) / scale);
sum += weight;
weights[j - left] = weight;
// weights[j - left] = weight:
Unsafe.Add(ref weights, j - left) = weight;
}
// Normalise, best to do it here rather than in the pixel loop later on.
@ -125,7 +128,9 @@ namespace ImageSharp.Processing.Processors
{
for (int w = 0; w < ws.Length; w++)
{
weights[w] = weights[w] / sum;
// weights[w] = weights[w] / sum:
ref float wRef = ref Unsafe.Add(ref weights, w);
wRef = wRef / sum;
}
}
}

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

@ -1,5 +1,6 @@
// ReSharper disable ObjectCreationAsStatement
// ReSharper disable InconsistentNaming
namespace ImageSharp.Tests.Common
{
using System;
@ -8,9 +9,20 @@ namespace ImageSharp.Tests.Common
using Xunit;
using static TestStructs;
public unsafe class BufferSpanTests
{
// ReSharper disable once ClassNeverInstantiated.Local
private class Assert : Xunit.Assert
{
public static void SameRefs<T1, T2>(ref T1 a, ref T2 b)
{
ref T1 bb = ref Unsafe.As<T2, T1>(ref b);
True(Unsafe.AreSame(ref a, ref bb), "References are not same!");
}
}
[Fact]
public void AsBytes()
{
@ -19,10 +31,10 @@ 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 = (BufferSpan<byte>)orig;
Assert.Equal(asBytes.Start, sizeof(Foo));
Assert.Equal(orig.PointerAtOffset, asBytes.PointerAtOffset);
Assert.SameRefs(ref orig.DangerousGetPinnableReference(), ref asBytes.DangerousGetPinnableReference());
}
}
@ -32,16 +44,14 @@ namespace ImageSharp.Tests.Common
public void Basic()
{
Foo[] array = Foo.CreateArray(3);
fixed (Foo* p = array)
{
// Act:
BufferSpan<Foo> span = new BufferSpan<Foo>(array, p);
// Assert:
Assert.Equal(array, span.Array);
Assert.Equal((IntPtr)p, span.PointerAtOffset);
Assert.Equal(3, span.Length);
}
// Act:
BufferSpan<Foo> span = new BufferSpan<Foo>(array);
// Assert:
Assert.Equal(array, span.Array);
Assert.Equal(3, span.Length);
Assert.SameRefs(ref array[0], ref span.DangerousGetPinnableReference());
}
[Fact]
@ -49,17 +59,15 @@ namespace ImageSharp.Tests.Common
{
Foo[] array = Foo.CreateArray(4);
int start = 2;
fixed (Foo* p = array)
{
// Act:
BufferSpan<Foo> span = new BufferSpan<Foo>(array, p, start);
// Assert:
Assert.Equal(array, span.Array);
Assert.Equal(start, span.Start);
Assert.Equal((IntPtr)(p + start), span.PointerAtOffset);
Assert.Equal(array.Length - start, span.Length);
}
// Act:
BufferSpan<Foo> span = new BufferSpan<Foo>(array, start);
// Assert:
Assert.Equal(array, span.Array);
Assert.Equal(start, span.Start);
Assert.SameRefs(ref array[start], ref span.DangerousGetPinnableReference());
Assert.Equal(array.Length - start, span.Length);
}
[Fact]
@ -68,17 +76,14 @@ namespace ImageSharp.Tests.Common
Foo[] array = Foo.CreateArray(10);
int start = 2;
int length = 3;
fixed (Foo* p = array)
{
// Act:
BufferSpan<Foo> span = new BufferSpan<Foo>(array, p, start, length);
// Assert:
Assert.Equal(array, span.Array);
Assert.Equal(start, span.Start);
Assert.Equal((IntPtr)(p + start), span.PointerAtOffset);
Assert.Equal(length, span.Length);
}
// Act:
BufferSpan<Foo> span = new BufferSpan<Foo>(array, start, length);
// Assert:
Assert.Equal(array, span.Array);
Assert.Equal(start, span.Start);
Assert.SameRefs(ref array[start], ref span.DangerousGetPinnableReference());
Assert.Equal(length, span.Length);
}
}
@ -92,19 +97,16 @@ namespace ImageSharp.Tests.Common
int start1 = 2;
int totalOffset = start0 + start1;
fixed (Foo* p = array)
{
BufferSpan<Foo> span = new BufferSpan<Foo>(array, p, start0);
BufferSpan<Foo> span = new BufferSpan<Foo>(array, start0);
// Act:
span = span.Slice(start1);
// Act:
span = span.Slice(start1);
// Assert:
Assert.Equal(array, span.Array);
Assert.Equal(totalOffset, span.Start);
Assert.Equal((IntPtr)(p + totalOffset), span.PointerAtOffset);
Assert.Equal(array.Length - totalOffset, span.Length);
}
// Assert:
Assert.Equal(array, span.Array);
Assert.Equal(totalOffset, span.Start);
Assert.SameRefs(ref array[totalOffset], ref span.DangerousGetPinnableReference());
Assert.Equal(array.Length - totalOffset, span.Length);
}
[Fact]
@ -116,24 +118,19 @@ namespace ImageSharp.Tests.Common
int totalOffset = start0 + start1;
int sliceLength = 3;
fixed (Foo* p = array)
{
BufferSpan<Foo> span = new BufferSpan<Foo>(array, p, start0);
BufferSpan<Foo> span = new BufferSpan<Foo>(array, start0);
// Act:
span = span.Slice(start1, sliceLength);
// Act:
span = span.Slice(start1, sliceLength);
// Assert:
Assert.Equal(array, span.Array);
Assert.Equal(totalOffset, span.Start);
Assert.Equal((IntPtr)(p + totalOffset), span.PointerAtOffset);
Assert.Equal(sliceLength, span.Length);
}
// Assert:
Assert.Equal(array, span.Array);
Assert.Equal(totalOffset, span.Start);
Assert.SameRefs(ref array[totalOffset], ref span.DangerousGetPinnableReference());
Assert.Equal(sliceLength, span.Length);
}
}
[Theory]
[InlineData(4)]
[InlineData(1500)]
@ -142,21 +139,17 @@ namespace ImageSharp.Tests.Common
Foo[] array = Foo.CreateArray(count + 42);
int offset = 2;
fixed (Foo* p = array)
{
BufferSpan<Foo> ap = new BufferSpan<Foo>(array, p, offset);
BufferSpan<Foo> ap = new BufferSpan<Foo>(array, offset);
// Act:
ap.Clear(count);
// Act:
ap.Clear(count);
Assert.NotEqual(default(Foo), array[offset-1]);
Assert.Equal(default(Foo), array[offset]);
Assert.Equal(default(Foo), array[offset + count-1]);
Assert.NotEqual(default(Foo), array[offset + count]);
}
Assert.NotEqual(default(Foo), array[offset - 1]);
Assert.Equal(default(Foo), array[offset]);
Assert.Equal(default(Foo), array[offset + count - 1]);
Assert.NotEqual(default(Foo), array[offset + count]);
}
public class Indexer
{
public static readonly TheoryData<int, int, int> IndexerData =
@ -175,14 +168,11 @@ namespace ImageSharp.Tests.Common
public void Read(int length, int start, int index)
{
Foo[] a = Foo.CreateArray(length);
fixed (Foo* p = a)
{
BufferSpan<Foo> span = new BufferSpan<Foo>(a, p, start);
BufferSpan<Foo> span = new BufferSpan<Foo>(a, start);
Foo element = span[index];
Foo element = span[index];
Assert.Equal(a[start + index], element);
}
Assert.Equal(a[start + index], element);
}
[Theory]
@ -190,14 +180,11 @@ namespace ImageSharp.Tests.Common
public void Write(int length, int start, int index)
{
Foo[] a = Foo.CreateArray(length);
fixed (Foo* p = a)
{
BufferSpan<Foo> span = new BufferSpan<Foo>(a, p, start);
BufferSpan<Foo> span = new BufferSpan<Foo>(a, start);
span[index] = new Foo(666, 666);
span[index] = new Foo(666, 666);
Assert.Equal(new Foo(666, 666), a[start + index]);
}
Assert.Equal(new Foo(666, 666), a[start + index]);
}
}
@ -208,13 +195,10 @@ namespace ImageSharp.Tests.Common
public void DangerousGetPinnableReference(int start, int length)
{
Foo[] a = Foo.CreateArray(length);
fixed (Foo* p = a)
{
BufferSpan<Foo> span = new BufferSpan<Foo>(a, p, start);
ref Foo r = ref span.DangerousGetPinnableReference();
BufferSpan<Foo> span = new BufferSpan<Foo>(a, start);
ref Foo r = ref span.DangerousGetPinnableReference();
Assert.True(Unsafe.AreSame(ref a[start], ref r));
}
Assert.True(Unsafe.AreSame(ref a[start], ref r));
}
public class Copy
@ -253,14 +237,10 @@ namespace ImageSharp.Tests.Common
Foo[] source = Foo.CreateArray(count + 2);
Foo[] dest = new Foo[count + 5];
fixed (Foo* pSource = source)
fixed (Foo* pDest = dest)
{
BufferSpan<Foo> apSource = new BufferSpan<Foo>(source, pSource, 1);
BufferSpan<Foo> apDest = new BufferSpan<Foo>(dest, pDest, 1);
BufferSpan<Foo> apSource = new BufferSpan<Foo>(source, 1);
BufferSpan<Foo> apDest = new BufferSpan<Foo>(dest, 1);
BufferSpan.Copy(apSource, apDest, count-1);
}
BufferSpan.Copy(apSource, apDest, count - 1);
AssertNotDefault(source, 1);
AssertNotDefault(dest, 1);
@ -268,7 +248,7 @@ namespace ImageSharp.Tests.Common
Assert.NotEqual(source[0], dest[0]);
Assert.Equal(source[1], dest[1]);
Assert.Equal(source[2], dest[2]);
Assert.Equal(source[count-1], dest[count-1]);
Assert.Equal(source[count - 1], dest[count - 1]);
Assert.NotEqual(source[count], dest[count]);
}
@ -280,14 +260,10 @@ namespace ImageSharp.Tests.Common
AlignedFoo[] source = AlignedFoo.CreateArray(count + 2);
AlignedFoo[] dest = new AlignedFoo[count + 5];
fixed (AlignedFoo* pSource = source)
fixed (AlignedFoo* pDest = dest)
{
BufferSpan<AlignedFoo> apSource = new BufferSpan<AlignedFoo>(source, pSource, 1);
BufferSpan<AlignedFoo> apDest = new BufferSpan<AlignedFoo>(dest, pDest, 1);
BufferSpan<AlignedFoo> apSource = new BufferSpan<AlignedFoo>(source, 1);
BufferSpan<AlignedFoo> apDest = new BufferSpan<AlignedFoo>(dest, 1);
BufferSpan.Copy(apSource, apDest, count - 1);
}
BufferSpan.Copy(apSource, apDest, count - 1);
AssertNotDefault(source, 1);
AssertNotDefault(dest, 1);
@ -304,17 +280,13 @@ namespace ImageSharp.Tests.Common
[InlineData(1500)]
public void IntToInt(int count)
{
int[] source = CreateTestInts(count+2);
int[] source = CreateTestInts(count + 2);
int[] dest = new int[count + 5];
fixed (int* pSource = source)
fixed (int* pDest = dest)
{
BufferSpan<int> apSource = new BufferSpan<int>(source, pSource, 1);
BufferSpan<int> apDest = new BufferSpan<int>(dest, pDest, 1);
BufferSpan<int> apSource = new BufferSpan<int>(source, 1);
BufferSpan<int> apDest = new BufferSpan<int>(dest, 1);
BufferSpan.Copy(apSource, apDest, count -1);
}
BufferSpan.Copy(apSource, apDest, count - 1);
AssertNotDefault(source, 1);
AssertNotDefault(dest, 1);
@ -332,17 +304,13 @@ namespace ImageSharp.Tests.Common
public void GenericToBytes(int count)
{
int destCount = count * sizeof(Foo);
Foo[] source = Foo.CreateArray(count+2);
byte[] dest = new byte[destCount + sizeof(Foo)*2];
Foo[] source = Foo.CreateArray(count + 2);
byte[] dest = new byte[destCount + sizeof(Foo) * 2];
fixed (Foo* pSource = source)
fixed (byte* pDest = dest)
{
BufferSpan<Foo> apSource = new BufferSpan<Foo>(source, pSource, 1);
BufferSpan<byte> apDest = new BufferSpan<byte>(dest, pDest, sizeof(Foo));
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, apDest, count - 1);
AssertNotDefault(source, 1);
@ -362,14 +330,10 @@ namespace ImageSharp.Tests.Common
AlignedFoo[] source = AlignedFoo.CreateArray(count + 2);
byte[] dest = new byte[destCount + sizeof(AlignedFoo) * 2];
fixed (AlignedFoo* pSource = source)
fixed (byte* pDest = dest)
{
BufferSpan<AlignedFoo> apSource = new BufferSpan<AlignedFoo>(source, pSource, 1);
BufferSpan<byte> apDest = new BufferSpan<byte>(dest, pDest, sizeof(AlignedFoo));
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, apDest, count - 1);
AssertNotDefault(source, 1);
@ -386,17 +350,13 @@ namespace ImageSharp.Tests.Common
public void IntToBytes(int count)
{
int destCount = count * sizeof(int);
int[] source = CreateTestInts(count+2);
int[] source = CreateTestInts(count + 2);
byte[] dest = new byte[destCount + sizeof(int) + 1];
fixed (int* pSource = source)
fixed (byte* pDest = dest)
{
BufferSpan<int> apSource = new BufferSpan<int>(source, pSource);
BufferSpan<byte> apDest = new BufferSpan<byte>(dest, pDest);
BufferSpan<int> apSource = new BufferSpan<int>(source);
BufferSpan<byte> apDest = new BufferSpan<byte>(dest);
BufferSpan.Copy(apSource, apDest, count);
}
BufferSpan.Copy(apSource, apDest, count);
AssertNotDefault(source, 1);
@ -413,15 +373,11 @@ namespace ImageSharp.Tests.Common
int srcCount = count * sizeof(Foo);
byte[] source = CreateTestBytes(srcCount);
Foo[] dest = new Foo[count + 2];
fixed(byte* pSource = source)
fixed (Foo* pDest = dest)
{
BufferSpan<byte> apSource = new BufferSpan<byte>(source, pSource);
BufferSpan<Foo> apDest = new BufferSpan<Foo>(dest, pDest);
BufferSpan.Copy(apSource, apDest, count);
}
BufferSpan<byte> apSource = new BufferSpan<byte>(source);
BufferSpan<Foo> apDest = new BufferSpan<Foo>(dest);
BufferSpan.Copy(apSource, apDest, count);
AssertNotDefault(source, sizeof(Foo) + 1);
AssertNotDefault(dest, 1);
@ -438,7 +394,7 @@ namespace ImageSharp.Tests.Common
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 (PinnedBuffer<byte> byteBuf = new PinnedBuffer<byte>(colors.Length*4))
using (PinnedBuffer<byte> byteBuf = new PinnedBuffer<byte>(colors.Length * 4))
{
BufferSpan.Copy<Color>(colorBuf, byteBuf, colorBuf.Length);
@ -451,7 +407,6 @@ namespace ImageSharp.Tests.Common
}
}
internal static bool ElementsAreEqual(Foo[] array, byte[] rawArray, int index)
{
fixed (Foo* pArray = array)

23
tests/ImageSharp.Tests/Common/PinnedBufferTests.cs

@ -11,6 +11,19 @@
public unsafe class PinnedBufferTests
{
private class Assert : Xunit.Assert
{
public static void SpanPointsTo<T>(IntPtr ptr, BufferSpan<T> span)
where T : struct
{
ref byte r = ref Unsafe.As<T, byte>(ref span.DangerousGetPinnableReference());
void* p = Unsafe.AsPointer(ref r);
Assert.Equal(ptr, (IntPtr)p);
}
}
[Theory]
[InlineData(42)]
[InlineData(1111)]
@ -121,7 +134,7 @@
Assert.True(buffer.IsDisposedOrLostArrayOwnership);
}
[Theory]
[InlineData(7)]
[InlineData(123)]
@ -133,7 +146,7 @@
Assert.Equal(buffer.Array, span.Array);
Assert.Equal(0, span.Start);
Assert.Equal(buffer.Pointer, span.PointerAtOffset);
Assert.SpanPointsTo(buffer.Pointer, span);
Assert.Equal(span.Length, bufferLength);
}
}
@ -147,7 +160,7 @@
Assert.Equal(buffer.Array, span.Array);
Assert.Equal(0, span.Start);
Assert.Equal(buffer.Pointer, span.PointerAtOffset);
Assert.SpanPointsTo(buffer.Pointer, span);
Assert.Equal(span.Length, 42);
}
}
@ -166,7 +179,7 @@
Assert.Equal(buffer.Array, span.Array);
Assert.Equal(start, span.Start);
Assert.Equal(buffer.Pointer + start * Unsafe.SizeOf<Foo>(), span.PointerAtOffset);
Assert.SpanPointsTo(buffer.Pointer + start * Unsafe.SizeOf<Foo>(), span);
Assert.Equal(span.Length, bufferLength - start);
}
}
@ -182,7 +195,7 @@
Assert.Equal(buffer.Array, span.Array);
Assert.Equal(start, span.Start);
Assert.Equal(buffer.Pointer + start * Unsafe.SizeOf<Foo>(), span.PointerAtOffset);
Assert.SpanPointsTo(buffer.Pointer + start * Unsafe.SizeOf<Foo>(), span);
Assert.Equal(span.Length, spanLength);
}
}

19
tests/ImageSharp.Tests/Common/PinnedImageBufferTests.cs

@ -1,6 +1,7 @@
// ReSharper disable InconsistentNaming
namespace ImageSharp.Tests.Common
{
using System;
using System.Runtime.CompilerServices;
using Xunit;
@ -9,6 +10,20 @@ namespace ImageSharp.Tests.Common
public unsafe class PinnedImageBufferTests
{
// ReSharper disable once ClassNeverInstantiated.Local
private class Assert : Xunit.Assert
{
public static void SpanPointsTo<T>(IntPtr ptr, BufferSpan<T> span)
where T : struct
{
ref byte r = ref Unsafe.As<T, byte>(ref span.DangerousGetPinnableReference());
void* p = Unsafe.AsPointer(ref r);
Assert.Equal(ptr, (IntPtr)p);
}
}
[Theory]
[InlineData(7, 42)]
[InlineData(1025, 17)]
@ -65,7 +80,7 @@ namespace ImageSharp.Tests.Common
Assert.Equal(width * y, span.Start);
Assert.Equal(width, span.Length);
Assert.Equal(buffer.Pointer + sizeof(Foo) * width * y, span.PointerAtOffset);
Assert.SpanPointsTo(buffer.Pointer + sizeof(Foo) * width * y, span);
}
}
@ -81,7 +96,7 @@ namespace ImageSharp.Tests.Common
Assert.Equal(width * y + x, span.Start);
Assert.Equal(width - x, span.Length);
Assert.Equal(buffer.Pointer + sizeof(Foo) * (width * y + x), span.PointerAtOffset);
Assert.SpanPointsTo(buffer.Pointer + sizeof(Foo) * (width * y + x), span);
}
}

Loading…
Cancel
Save