diff --git a/src/ImageSharp/Colors/Color.BulkOperations.cs b/src/ImageSharp/Colors/Color.BulkOperations.cs index 7d3b12ea0b..08d70330df 100644 --- a/src/ImageSharp/Colors/Color.BulkOperations.cs +++ b/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(ref sourceColors.DangerousGetPinnableReference()); - using (PinnedBuffer tempBuf = new PinnedBuffer( - unpackedRawCount + Vector.Count)) + using (PinnedBuffer tempBuf = new PinnedBuffer(unpackedRawCount + Vector.Count)) { uint* tPtr = (uint*)tempBuf.Pointer; uint[] temp = tempBuf.Array; float[] fTemp = Unsafe.As(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 } /// - internal override unsafe void PackFromXyzBytes(BufferSpan sourceBytes, BufferSpan destColors, int count) + internal override void PackFromXyzBytes( + BufferSpan sourceBytes, + BufferSpan destColors, + int count) { - byte* source = (byte*)sourceBytes; - byte* destination = (byte*)destColors; + ref RGB24 sourceRef = ref Unsafe.As(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(ref dp) = sp; + dp.A = 255; } } /// - internal override unsafe void ToXyzBytes(BufferSpan sourceColors, BufferSpan destBytes, int count) + internal override void ToXyzBytes(BufferSpan sourceColors, BufferSpan destBytes, int count) { - byte* source = (byte*)sourceColors; - byte* destination = (byte*)destBytes; + ref Color sourceRef = ref sourceColors.DangerousGetPinnableReference(); + ref RGB24 destRef = ref Unsafe.As(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(ref sp); } } /// - internal override void PackFromXyzwBytes(BufferSpan sourceBytes, BufferSpan destColors, int count) + internal override void PackFromXyzwBytes( + BufferSpan sourceBytes, + BufferSpan destColors, + int count) { BufferSpan.Copy(sourceBytes, destColors, count); } @@ -167,87 +171,132 @@ namespace ImageSharp } /// - internal override unsafe void PackFromZyxBytes(BufferSpan sourceBytes, BufferSpan destColors, int count) + internal override void PackFromZyxBytes( + BufferSpan sourceBytes, + BufferSpan destColors, + int count) { - byte* source = (byte*)sourceBytes; - byte* destination = (byte*)destColors; + ref RGB24 sourceRef = ref Unsafe.As(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(ref dp) = sp.ToZyx(); + dp.A = 255; } } /// - internal override unsafe void ToZyxBytes(BufferSpan sourceColors, BufferSpan destBytes, int count) + internal override void ToZyxBytes( + BufferSpan sourceColors, + BufferSpan destBytes, + int count) { - byte* source = (byte*)sourceColors; - byte* destination = (byte*)destBytes; + ref Color sourceRef = ref sourceColors.DangerousGetPinnableReference(); + ref RGB24 destRef = ref Unsafe.As(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(ref sp).ToZyx(); } } /// - internal override unsafe void PackFromZyxwBytes(BufferSpan sourceBytes, BufferSpan destColors, int count) + internal override void PackFromZyxwBytes( + BufferSpan sourceBytes, + BufferSpan destColors, + int count) { - byte* source = (byte*)sourceBytes; - byte* destination = (byte*)destColors; + ref RGBA32 sourceRef = ref Unsafe.As(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(ref zyxw); } } /// - internal override unsafe void ToZyxwBytes(BufferSpan sourceColors, BufferSpan destBytes, int count) + internal override void ToZyxwBytes( + BufferSpan sourceColors, + BufferSpan destBytes, + int count) { - byte* source = (byte*)sourceColors; - byte* destination = (byte*)destBytes; + ref Color sourceRef = ref sourceColors.DangerousGetPinnableReference(); + ref RGBA32 destRef = ref Unsafe.As(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(ref Unsafe.Add(ref sourceRef, i)); + ref RGBA32 dp = ref Unsafe.Add(ref destRef, i); + dp = sp.ToZyxw(); } } + /// + /// Helper struct to manipulate 3-byte RGB data. + /// + [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 }; + } + + /// + /// Helper struct to manipulate 4-byte RGBA data. + /// + [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 }; + } + /// /// Value type to store -s unpacked into multiple -s. /// + [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; } } } diff --git a/src/ImageSharp/Colors/Color.cs b/src/ImageSharp/Colors/Color.cs index 5977309373..1e1e73bab4 100644 --- a/src/ImageSharp/Colors/Color.cs +++ b/src/ImageSharp/Colors/Color.cs @@ -62,6 +62,7 @@ namespace ImageSharp /// The green component. /// The blue component. /// The alpha component. + [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 /// The green component. /// The blue component. /// The alpha component. + [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 /// /// The vector containing the components for the packed vector. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Color(Vector3 vector) { this.packedValue = Pack(ref vector); @@ -96,6 +99,7 @@ namespace ImageSharp /// /// The vector containing the components for the packed vector. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Color(Vector4 vector) { this.packedValue = Pack(ref vector); @@ -107,6 +111,7 @@ namespace ImageSharp /// /// The packed value. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Color(uint packed) { this.packedValue = packed; diff --git a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs index 7b6169f9c6..a3bd328251 100644 --- a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs +++ b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs @@ -13,14 +13,9 @@ namespace ImageSharp /// for pixel buffers of type . /// /// The pixel format. - public unsafe class BulkPixelOperations + public class BulkPixelOperations where TColor : struct, IPixel { - /// - /// The size of in bytes - /// - private static readonly int ColorSize = Unsafe.SizeOf(); - /// /// Gets the global instance for the pixel type /// @@ -37,18 +32,14 @@ namespace ImageSharp BufferSpan 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(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 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(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 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 /// The number of pixels to convert. internal virtual void ToXyzBytes(BufferSpan sourceColors, BufferSpan 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(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 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 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(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 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 /// The number of pixels to convert. internal virtual void ToZyxBytes(BufferSpan sourceColors, BufferSpan 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(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 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 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(sp); - c.ToZyxwBytes(dest, i); - sp += ColorSize; + ref TColor sp = ref Unsafe.Add(ref sourceRef, i); + sp.ToZyxwBytes(dest, i * 4); } } } diff --git a/src/ImageSharp/Common/Memory/BufferSpan.cs b/src/ImageSharp/Common/Memory/BufferSpan.cs index 42a6fbc6be..09adadc7d6 100644 --- a/src/ImageSharp/Common/Memory/BufferSpan.cs +++ b/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 } /// - /// Copy 'countInDest' number of elements into 'dest' from a raw byte buffer defined by 'source'. + /// Copy 'countInDest' number of elements into 'dest' from a raw byte buffer defined by 'source'. /// - /// The element type. + /// The element type. /// The raw source buffer to copy from"/> /// The destination buffer"/> - /// The number of elements to copy. + /// The number of elements to copy. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void Copy(BufferSpan source, BufferSpan destination, int countInDest) - where T : struct + public static unsafe void Copy(BufferSpan source, BufferSpan destination, int countInDest) + where TDest : struct { - int byteCount = SizeOf(countInDest); + // TODO: Refactor this method when Unsafe.CopyBlock(ref T, ref T) gets available! + int byteCount = SizeOf(countInDest); - if (byteCount > (int)ByteCountThreshold) + if (PerTypeValues.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(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(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(count); [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe void CopyImpl(BufferSpan source, BufferSpan destination, int count) + private static unsafe void CopyImpl(BufferSpan source, BufferSpan destination, int countInSource) where T : struct where TDest : struct { - int byteCount = SizeOf(count); + // TODO: Refactor this method when Unsafe.CopyBlock(ref T, ref T) gets available! + int byteCount = SizeOf(countInSource); - if (byteCount > ByteCountThreshold) + if (PerTypeValues.IsPrimitiveType && PerTypeValues.IsPrimitiveType) { - if (Unsafe.SizeOf() == sizeof(long)) - { - Marshal.Copy(Unsafe.As(source.Array), source.Start, destination.PointerAtOffset, count); - return; - } - else if (Unsafe.SizeOf() == sizeof(int)) - { - Marshal.Copy(Unsafe.As(source.Array), source.Start, destination.PointerAtOffset, count); - return; - } - else if (Unsafe.SizeOf() == sizeof(short)) + Buffer.BlockCopy(source.Array, source.ByteOffset, destination.Array, destination.ByteOffset, byteCount); + } + else if (byteCount > ByteCountThreshold) + { + ref byte destRef = ref Unsafe.As(ref destination.DangerousGetPinnableReference()); + + fixed (void* pinnedPtr = &destRef) { - Marshal.Copy(Unsafe.As(source.Array), source.Start, destination.PointerAtOffset, count); - return; + IntPtr ptr = (IntPtr)pinnedPtr; + if (Unsafe.SizeOf() == sizeof(long)) + { + Marshal.Copy(Unsafe.As(source.Array), source.Start, ptr, countInSource); + } + else if (Unsafe.SizeOf() == sizeof(int)) + { + Marshal.Copy(Unsafe.As(source.Array), source.Start, ptr, countInSource); + } + else if (Unsafe.SizeOf() == sizeof(short)) + { + Marshal.Copy(Unsafe.As(source.Array), source.Start, ptr, countInSource); + } + else if (Unsafe.SizeOf() == sizeof(byte)) + { + Marshal.Copy(Unsafe.As(source.Array), source.Start, ptr, countInSource); + } } - else if (Unsafe.SizeOf() == sizeof(byte)) + } + else + { + ref byte srcRef = ref Unsafe.As(ref source.DangerousGetPinnableReference()); + ref byte destRef = ref Unsafe.As(ref destination.DangerousGetPinnableReference()); + + fixed (void* pinnedSrc = &srcRef) + fixed (void* pinnedDest = &destRef) { - Marshal.Copy(Unsafe.As(source.Array), source.Start, destination.PointerAtOffset, count); - return; + Unsafe.CopyBlock(pinnedDest, pinnedSrc, (uint)byteCount); } } + } - Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, (uint)byteCount); + /// + /// Per-type static value cache for type 'T' + /// + /// The type + internal class PerTypeValues + { + /// + /// Gets a value indicating whether 'T' is a primitive type. + /// + 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); } } } \ No newline at end of file diff --git a/src/ImageSharp/Common/Memory/BufferSpan{T}.cs b/src/ImageSharp/Common/Memory/BufferSpan{T}.cs index 5efa7bc07a..25d01631d9 100644 --- a/src/ImageSharp/Common/Memory/BufferSpan{T}.cs +++ b/src/ImageSharp/Common/Memory/BufferSpan{T}.cs @@ -30,13 +30,12 @@ namespace ImageSharp /// Initializes a new instance of the struct from a pinned array and an start. /// /// The pinned array - /// Pointer to the beginning of the array /// The index at which to begin the span. /// The length [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() * start); } /// /// Initializes a new instance of the struct from a pinned array and an start. /// /// The pinned array - /// Pointer to the beginning of the array /// The index at which to begin the span. [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() * start); } /// /// Initializes a new instance of the struct from a pinned array. /// /// The pinned array - /// Pointer to the start of 'array' [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; } /// @@ -101,11 +95,6 @@ namespace ImageSharp /// public int ByteOffset => this.Start * Unsafe.SizeOf(); - /// - /// Gets the pointer to the offseted array position - /// - public IntPtr PointerAtOffset { get; private set; } - /// /// Returns a reference to specified element of the span. /// @@ -117,35 +106,14 @@ namespace ImageSharp get { DebugGuard.MustBeLessThan(index, this.Length, nameof(index)); - - byte* ptr = (byte*)this.PointerAtOffset + BufferSpan.SizeOf(index); - return ref Unsafe.AsRef(ptr); + ref T arrayRef = ref this.DangerousGetPinnableReference(); + return ref Unsafe.Add(ref arrayRef, this.Start); } } - /// - /// Convertes instance to a raw 'void*' pointer - /// - /// The to convert - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator void*(BufferSpan bufferSpan) - { - return (void*)bufferSpan.PointerAtOffset; - } - - /// - /// Converts instance to a raw 'byte*' pointer - /// - /// The to convert - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator byte*(BufferSpan bufferSpan) - { - return (byte*)bufferSpan.PointerAtOffset; - } - /// /// Converts generic to a of bytes - /// setting it's and to correct values. + /// setting it's to correct value. /// /// The to convert [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -154,7 +122,6 @@ namespace ImageSharp BufferSpan result = default(BufferSpan); result.Array = Unsafe.As(source.Array); result.Start = source.Start * Unsafe.SizeOf(); - result.PointerAtOffset = source.PointerAtOffset; return result; } @@ -164,7 +131,7 @@ namespace ImageSharp /// /// The reference to the 0th element [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ref T DangerousGetPinnableReference() => ref Unsafe.AsRef((void*)this.PointerAtOffset); + public ref T DangerousGetPinnableReference() => ref this.Array[this.Start]; /// /// Forms a slice out of the given BufferSpan, beginning at 'start'. @@ -179,7 +146,6 @@ namespace ImageSharp BufferSpan result = default(BufferSpan); result.Array = this.Array; result.Start = this.Start + start; - result.PointerAtOffset = this.PointerAtOffset + (Unsafe.SizeOf() * start); result.Length = this.Length - start; return result; } @@ -199,7 +165,6 @@ namespace ImageSharp BufferSpan result = default(BufferSpan); result.Array = this.Array; result.Start = this.Start + start; - result.PointerAtOffset = this.PointerAtOffset + (Unsafe.SizeOf() * 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(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); } /// @@ -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!"); } } } \ No newline at end of file diff --git a/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs b/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs index 611688c995..665e92e097 100644 --- a/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs +++ b/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs @@ -126,9 +126,9 @@ namespace ImageSharp /// /// The to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe implicit operator BufferSpan(PinnedBuffer buffer) + public static implicit operator BufferSpan(PinnedBuffer buffer) { - return new BufferSpan(buffer.Array, (void*)buffer.Pointer, 0, buffer.Length); + return new BufferSpan(buffer.Array, 0, buffer.Length); } /// @@ -150,9 +150,9 @@ namespace ImageSharp /// The start /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe BufferSpan Slice(int start) + public BufferSpan Slice(int start) { - return new BufferSpan(this.Array, (void*)this.Pointer, start, this.Length - start); + return new BufferSpan(this.Array, start, this.Length - start); } /// @@ -162,9 +162,9 @@ namespace ImageSharp /// The length of the slice /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe BufferSpan Slice(int start, int length) + public BufferSpan Slice(int start, int length) { - return new BufferSpan(this.Array, (void*)this.Pointer, start, length); + return new BufferSpan(this.Array, start, length); } /// diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs index 99b143de67..886c055692 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs +++ b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.Weights.cs @@ -41,7 +41,7 @@ namespace ImageSharp.Processing.Processors /// /// Gets an unsafe float* pointer to the beginning of . /// - public float* Ptr => (float*)this.Span.PointerAtOffset; + public ref float Ptr => ref this.Span.DangerousGetPinnableReference(); /// /// Gets the lenghth of the weights window @@ -56,19 +56,18 @@ namespace ImageSharp.Processing.Processors [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ComputeWeightedRowSum(BufferSpan 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 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 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; } diff --git a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs b/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs index 1374e58156..50c75a3fdf 100644 --- a/src/ImageSharp/Processing/Processors/Transforms/ResamplingWeightedProcessor.cs +++ b/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; /// @@ -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; } } } diff --git a/tests/ImageSharp.Tests/Common/BufferSpanTests.cs b/tests/ImageSharp.Tests/Common/BufferSpanTests.cs index 32ec5dec62..f2fcb2539c 100644 --- a/tests/ImageSharp.Tests/Common/BufferSpanTests.cs +++ b/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(ref T1 a, ref T2 b) + { + ref T1 bb = ref Unsafe.As(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 colorBuf = new PinnedBuffer(fooz)) { BufferSpan orig = colorBuf.Slice(1); - BufferSpan asBytes = (BufferSpan < byte > )orig; + BufferSpan asBytes = (BufferSpan)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 span = new BufferSpan(array, p); - // Assert: - Assert.Equal(array, span.Array); - Assert.Equal((IntPtr)p, span.PointerAtOffset); - Assert.Equal(3, span.Length); - } + // Act: + BufferSpan span = new BufferSpan(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 span = new BufferSpan(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 span = new BufferSpan(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 span = new BufferSpan(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 span = new BufferSpan(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 span = new BufferSpan(array, p, start0); + BufferSpan span = new BufferSpan(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 span = new BufferSpan(array, p, start0); + BufferSpan span = new BufferSpan(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 ap = new BufferSpan(array, p, offset); + BufferSpan ap = new BufferSpan(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 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 span = new BufferSpan(a, p, start); + BufferSpan span = new BufferSpan(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 span = new BufferSpan(a, p, start); + BufferSpan span = new BufferSpan(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 span = new BufferSpan(a, p, start); - ref Foo r = ref span.DangerousGetPinnableReference(); + BufferSpan span = new BufferSpan(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 apSource = new BufferSpan(source, pSource, 1); - BufferSpan apDest = new BufferSpan(dest, pDest, 1); + BufferSpan apSource = new BufferSpan(source, 1); + BufferSpan apDest = new BufferSpan(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 apSource = new BufferSpan(source, pSource, 1); - BufferSpan apDest = new BufferSpan(dest, pDest, 1); + BufferSpan apSource = new BufferSpan(source, 1); + BufferSpan apDest = new BufferSpan(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 apSource = new BufferSpan(source, pSource, 1); - BufferSpan apDest = new BufferSpan(dest, pDest, 1); + BufferSpan apSource = new BufferSpan(source, 1); + BufferSpan apDest = new BufferSpan(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 apSource = new BufferSpan(source, pSource, 1); - BufferSpan apDest = new BufferSpan(dest, pDest, sizeof(Foo)); + BufferSpan apSource = new BufferSpan(source, 1); + BufferSpan apDest = new BufferSpan(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 apSource = new BufferSpan(source, pSource, 1); - BufferSpan apDest = new BufferSpan(dest, pDest, sizeof(AlignedFoo)); + BufferSpan apSource = new BufferSpan(source, 1); + BufferSpan apDest = new BufferSpan(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 apSource = new BufferSpan(source, pSource); - BufferSpan apDest = new BufferSpan(dest, pDest); + BufferSpan apSource = new BufferSpan(source); + BufferSpan apDest = new BufferSpan(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 apSource = new BufferSpan(source, pSource); - BufferSpan apDest = new BufferSpan(dest, pDest); - BufferSpan.Copy(apSource, apDest, count); - } + BufferSpan apSource = new BufferSpan(source); + BufferSpan apDest = new BufferSpan(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 colorBuf = new PinnedBuffer(colors)) - using (PinnedBuffer byteBuf = new PinnedBuffer(colors.Length*4)) + using (PinnedBuffer byteBuf = new PinnedBuffer(colors.Length * 4)) { BufferSpan.Copy(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) diff --git a/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs b/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs index 5e812d5a01..67430976a7 100644 --- a/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs +++ b/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs @@ -11,6 +11,19 @@ public unsafe class PinnedBufferTests { + private class Assert : Xunit.Assert + { + public static void SpanPointsTo(IntPtr ptr, BufferSpan span) + where T : struct + { + ref byte r = ref Unsafe.As(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(), span.PointerAtOffset); + Assert.SpanPointsTo(buffer.Pointer + start * Unsafe.SizeOf(), 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(), span.PointerAtOffset); + Assert.SpanPointsTo(buffer.Pointer + start * Unsafe.SizeOf(), span); Assert.Equal(span.Length, spanLength); } } diff --git a/tests/ImageSharp.Tests/Common/PinnedImageBufferTests.cs b/tests/ImageSharp.Tests/Common/PinnedImageBufferTests.cs index a23f93a70b..a8ccea5eb8 100644 --- a/tests/ImageSharp.Tests/Common/PinnedImageBufferTests.cs +++ b/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(IntPtr ptr, BufferSpan span) + where T : struct + { + ref byte r = ref Unsafe.As(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); } }