diff --git a/src/ImageSharp/Colors/Color.BulkOperations.cs b/src/ImageSharp/Colors/Color.BulkOperations.cs index 5c040e04c5..039fafced6 100644 --- a/src/ImageSharp/Colors/Color.BulkOperations.cs +++ b/src/ImageSharp/Colors/Color.BulkOperations.cs @@ -24,8 +24,8 @@ namespace ImageSharp /// SIMD optimized bulk implementation of /// that works only with `count` divisible by . /// - /// The to the source colors. - /// The to the dstination vectors. + /// The to the source colors. + /// The to the dstination vectors. /// The number of pixels to convert. /// /// Implementation adapted from: @@ -38,8 +38,8 @@ namespace ImageSharp /// /// internal static unsafe void ToVector4SimdAligned( - BufferPointer sourceColors, - BufferPointer destVectors, + BufferSpan sourceColors, + BufferSpan destVectors, int count) { int vecSize = Vector.Count; @@ -85,12 +85,12 @@ namespace ImageSharp vf.CopyTo(fTemp, i); } - BufferPointer.Copy(tempBuf, (BufferPointer)destVectors, unpackedRawCount); + BufferSpan.Copy(tempBuf, (BufferSpan)destVectors, unpackedRawCount); } } /// - internal override void ToVector4(BufferPointer sourceColors, BufferPointer destVectors, int count) + internal override void ToVector4(BufferSpan sourceColors, BufferSpan destVectors, int count) { if (count < 256) { @@ -117,7 +117,7 @@ namespace ImageSharp } /// - internal override unsafe void PackFromXyzBytes(BufferPointer sourceBytes, BufferPointer destColors, int count) + internal override unsafe void PackFromXyzBytes(BufferSpan sourceBytes, BufferSpan destColors, int count) { byte* source = (byte*)sourceBytes; byte* destination = (byte*)destColors; @@ -132,7 +132,7 @@ namespace ImageSharp } /// - internal override unsafe void ToXyzBytes(BufferPointer sourceColors, BufferPointer destBytes, int count) + internal override unsafe void ToXyzBytes(BufferSpan sourceColors, BufferSpan destBytes, int count) { byte* source = (byte*)sourceColors; byte* destination = (byte*)destBytes; @@ -149,19 +149,19 @@ namespace ImageSharp } /// - internal override void PackFromXyzwBytes(BufferPointer sourceBytes, BufferPointer destColors, int count) + internal override void PackFromXyzwBytes(BufferSpan sourceBytes, BufferSpan destColors, int count) { - BufferPointer.Copy(sourceBytes, destColors, count); + BufferSpan.Copy(sourceBytes, destColors, count); } /// - internal override void ToXyzwBytes(BufferPointer sourceColors, BufferPointer destBytes, int count) + internal override void ToXyzwBytes(BufferSpan sourceColors, BufferSpan destBytes, int count) { - BufferPointer.Copy(sourceColors, destBytes, count); + BufferSpan.Copy(sourceColors, destBytes, count); } /// - internal override unsafe void PackFromZyxBytes(BufferPointer sourceBytes, BufferPointer destColors, int count) + internal override unsafe void PackFromZyxBytes(BufferSpan sourceBytes, BufferSpan destColors, int count) { byte* source = (byte*)sourceBytes; byte* destination = (byte*)destColors; @@ -176,7 +176,7 @@ namespace ImageSharp } /// - internal override unsafe void ToZyxBytes(BufferPointer sourceColors, BufferPointer destBytes, int count) + internal override unsafe void ToZyxBytes(BufferSpan sourceColors, BufferSpan destBytes, int count) { byte* source = (byte*)sourceColors; byte* destination = (byte*)destBytes; @@ -193,7 +193,7 @@ namespace ImageSharp } /// - internal override unsafe void PackFromZyxwBytes(BufferPointer sourceBytes, BufferPointer destColors, int count) + internal override unsafe void PackFromZyxwBytes(BufferSpan sourceBytes, BufferSpan destColors, int count) { byte* source = (byte*)sourceBytes; byte* destination = (byte*)destColors; @@ -208,7 +208,7 @@ namespace ImageSharp } /// - internal override unsafe void ToZyxwBytes(BufferPointer sourceColors, BufferPointer destBytes, int count) + internal override unsafe void ToZyxwBytes(BufferSpan sourceColors, BufferSpan destBytes, int count) { byte* source = (byte*)sourceColors; byte* destination = (byte*)destBytes; diff --git a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs index 259b1c9b47..7b6169f9c6 100644 --- a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs +++ b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs @@ -29,12 +29,12 @@ namespace ImageSharp /// /// Bulk version of /// - /// The to the source vectors. - /// The to the destination colors. + /// The to the source vectors. + /// The to the destination colors. /// The number of pixels to convert. internal virtual void PackFromVector4( - BufferPointer sourceVectors, - BufferPointer destColors, + BufferSpan sourceVectors, + BufferSpan destColors, int count) { Vector4* sp = (Vector4*)sourceVectors.PointerAtOffset; @@ -55,12 +55,12 @@ namespace ImageSharp /// /// Bulk version of . /// - /// The to the source colors. - /// The to the destination vectors. + /// The to the source colors. + /// The to the destination vectors. /// The number of pixels to convert. internal virtual void ToVector4( - BufferPointer sourceColors, - BufferPointer destVectors, + BufferSpan sourceColors, + BufferSpan destVectors, int count) { byte* sp = (byte*)sourceColors; @@ -78,12 +78,12 @@ namespace ImageSharp /// /// Bulk version of that converts data in . /// - /// The to the source bytes. - /// The to the destination colors. + /// The to the source bytes. + /// The to the destination colors. /// The number of pixels to convert. internal virtual void PackFromXyzBytes( - BufferPointer sourceBytes, - BufferPointer destColors, + BufferSpan sourceBytes, + BufferSpan destColors, int count) { byte* sp = (byte*)sourceBytes; @@ -102,15 +102,15 @@ namespace ImageSharp /// /// Bulk version of . /// - /// The to the source colors. - /// The to the destination bytes. + /// The to the source colors. + /// The to the destination bytes. /// The number of pixels to convert. - internal virtual void ToXyzBytes(BufferPointer sourceColors, BufferPointer destBytes, int count) + internal virtual void ToXyzBytes(BufferSpan sourceColors, BufferSpan destBytes, int count) { byte* sp = (byte*)sourceColors; byte[] dest = destBytes.Array; - for (int i = destBytes.Offset; i < destBytes.Offset + (count * 3); i += 3) + for (int i = destBytes.Start; i < destBytes.Start + (count * 3); i += 3) { TColor c = Unsafe.Read(sp); c.ToXyzBytes(dest, i); @@ -121,12 +121,12 @@ namespace ImageSharp /// /// Bulk version of that converts data in . /// - /// The to the source bytes. - /// The to the destination colors. + /// The to the source bytes. + /// The to the destination colors. /// The number of pixels to convert. internal virtual void PackFromXyzwBytes( - BufferPointer sourceBytes, - BufferPointer destColors, + BufferSpan sourceBytes, + BufferSpan destColors, int count) { byte* sp = (byte*)sourceBytes; @@ -145,18 +145,18 @@ namespace ImageSharp /// /// Bulk version of . /// - /// The to the source colors. - /// The to the destination bytes. + /// The to the source colors. + /// The to the destination bytes. /// The number of pixels to convert. internal virtual void ToXyzwBytes( - BufferPointer sourceColors, - BufferPointer destBytes, + BufferSpan sourceColors, + BufferSpan destBytes, int count) { byte* sp = (byte*)sourceColors; byte[] dest = destBytes.Array; - for (int i = destBytes.Offset; i < destBytes.Offset + (count * 4); i += 4) + for (int i = destBytes.Start; i < destBytes.Start + (count * 4); i += 4) { TColor c = Unsafe.Read(sp); c.ToXyzwBytes(dest, i); @@ -167,12 +167,12 @@ namespace ImageSharp /// /// Bulk version of that converts data in . /// - /// The to the source bytes. - /// The to the destination colors. + /// The to the source bytes. + /// The to the destination colors. /// The number of pixels to convert. internal virtual void PackFromZyxBytes( - BufferPointer sourceBytes, - BufferPointer destColors, + BufferSpan sourceBytes, + BufferSpan destColors, int count) { byte* sp = (byte*)sourceBytes; @@ -191,15 +191,15 @@ namespace ImageSharp /// /// Bulk version of . /// - /// The to the source colors. - /// The to the destination bytes. + /// The to the source colors. + /// The to the destination bytes. /// The number of pixels to convert. - internal virtual void ToZyxBytes(BufferPointer sourceColors, BufferPointer destBytes, int count) + internal virtual void ToZyxBytes(BufferSpan sourceColors, BufferSpan destBytes, int count) { byte* sp = (byte*)sourceColors; byte[] dest = destBytes.Array; - for (int i = destBytes.Offset; i < destBytes.Offset + (count * 3); i += 3) + for (int i = destBytes.Start; i < destBytes.Start + (count * 3); i += 3) { TColor c = Unsafe.Read(sp); c.ToZyxBytes(dest, i); @@ -210,12 +210,12 @@ namespace ImageSharp /// /// Bulk version of that converts data in . /// - /// The to the source bytes. - /// The to the destination colors. + /// The to the source bytes. + /// The to the destination colors. /// The number of pixels to convert. internal virtual void PackFromZyxwBytes( - BufferPointer sourceBytes, - BufferPointer destColors, + BufferSpan sourceBytes, + BufferSpan destColors, int count) { byte* sp = (byte*)sourceBytes; @@ -234,18 +234,18 @@ namespace ImageSharp /// /// Bulk version of . /// - /// The to the source colors. - /// The to the destination bytes. + /// The to the source colors. + /// The to the destination bytes. /// The number of pixels to convert. internal virtual void ToZyxwBytes( - BufferPointer sourceColors, - BufferPointer destBytes, + BufferSpan sourceColors, + BufferSpan destBytes, int count) { byte* sp = (byte*)sourceColors; byte[] dest = destBytes.Array; - for (int i = destBytes.Offset; i < destBytes.Offset + (count * 4); i += 4) + for (int i = destBytes.Start; i < destBytes.Start + (count * 4); i += 4) { TColor c = Unsafe.Read(sp); c.ToZyxwBytes(dest, i); diff --git a/src/ImageSharp/Common/Memory/BufferPointer{T}.cs b/src/ImageSharp/Common/Memory/BufferPointer{T}.cs deleted file mode 100644 index 441f6b8ce0..0000000000 --- a/src/ImageSharp/Common/Memory/BufferPointer{T}.cs +++ /dev/null @@ -1,143 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System; - using System.Runtime.CompilerServices; - using System.Runtime.InteropServices; - - /// - /// Provides access to elements in an array from a given position. - /// This type shares many similarities with corefx System.Span<T> but there are significant differences in it's functionalities and semantics: - /// - It's not possible to use it with stack objects or pointers to unmanaged memory, only with managed arrays - /// - It's possible to retrieve a reference to the array () so we can pass it to API-s like - /// - There is no bounds checking for performance reasons. Therefore we don't need to store length. (However this could be added as DEBUG-only feature.) - /// This makes an unsafe type! - /// - Currently the arrays provided to BufferPointer need to be pinned. This behaviour could be changed using C#7 features. - /// - /// The type of elements of the array - internal unsafe struct BufferPointer - where T : struct - { - /// - /// Initializes a new instance of the struct from a pinned array and an offset. - /// - /// The pinned array - /// Pointer to the beginning of array - /// The offset inside the array - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public BufferPointer(T[] array, void* pointerToArray, int offset) - { - DebugGuard.NotNull(array, nameof(array)); - - this.Array = array; - this.Offset = offset; - this.PointerAtOffset = (IntPtr)pointerToArray + (Unsafe.SizeOf() * offset); - } - - /// - /// Initializes a new instance of the struct from a pinned array. - /// - /// The pinned array - /// Pointer to the start of 'array' - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public BufferPointer(T[] array, void* pointerToArray) - { - DebugGuard.NotNull(array, nameof(array)); - - this.Array = array; - this.Offset = 0; - this.PointerAtOffset = (IntPtr)pointerToArray; - } - - /// - /// Gets the array - /// - public T[] Array { get; private set; } - - /// - /// Gets the offset inside - /// - public int Offset { get; private set; } - - /// - /// Gets the offset inside in bytes. - /// - public int ByteOffset => this.Offset * Unsafe.SizeOf(); - - /// - /// Gets the pointer to the offseted array position - /// - public IntPtr PointerAtOffset { get; private set; } - - /// - /// Convertes instance to a raw 'void*' pointer - /// - /// The to convert - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator void*(BufferPointer bufferPointer) - { - return (void*)bufferPointer.PointerAtOffset; - } - - /// - /// Converts instance to a raw 'byte*' pointer - /// - /// The to convert - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator byte*(BufferPointer bufferPointer) - { - return (byte*)bufferPointer.PointerAtOffset; - } - - /// - /// Converts instance to - /// setting it's and to correct values. - /// - /// The to convert - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator BufferPointer(BufferPointer source) - { - BufferPointer result = default(BufferPointer); - result.Array = Unsafe.As(source.Array); - result.Offset = source.Offset * Unsafe.SizeOf(); - result.PointerAtOffset = source.PointerAtOffset; - return result; - } - - /// - /// Forms a slice out of the given BufferPointer, beginning at 'offset'. - /// - /// The offset in number of elements - /// The offseted (sliced) BufferPointer - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public BufferPointer Slice(int offset) - { - BufferPointer result = default(BufferPointer); - result.Array = this.Array; - result.Offset = this.Offset + offset; - result.PointerAtOffset = this.PointerAtOffset + (Unsafe.SizeOf() * offset); - return result; - } - - /// - /// Clears `count` elements beginning from the pointed position. - /// - /// The number of elements to clear - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Clear(int count) - { - if (count < 256) - { - Unsafe.InitBlock((void*)this.PointerAtOffset, 0, BufferPointer.USizeOf(count)); - } - else - { - System.Array.Clear(this.Array, this.Offset, count); - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Common/Memory/BufferPointer.cs b/src/ImageSharp/Common/Memory/BufferSpan.cs similarity index 81% rename from src/ImageSharp/Common/Memory/BufferPointer.cs rename to src/ImageSharp/Common/Memory/BufferSpan.cs index 523889611f..42a6fbc6be 100644 --- a/src/ImageSharp/Common/Memory/BufferPointer.cs +++ b/src/ImageSharp/Common/Memory/BufferSpan.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -11,9 +11,9 @@ namespace ImageSharp using System.Runtime.InteropServices; /// - /// Utility methods for + /// Utility methods for /// - internal static class BufferPointer + internal static class BufferSpan { /// /// It's worth to use Marshal.Copy() or Buffer.BlockCopy() over this size. @@ -24,11 +24,11 @@ namespace ImageSharp /// Copy 'count' number of elements of the same type from 'source' to 'dest' /// /// The element type. - /// The input - /// The destination . + /// The input + /// The destination . /// The number of elements to copy [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Copy(BufferPointer source, BufferPointer destination, int count) + public static void Copy(BufferSpan source, BufferSpan destination, int count) where T : struct { CopyImpl(source, destination, count); @@ -42,7 +42,7 @@ namespace ImageSharp /// The destination buffer. /// The number of elements to copy from 'source' [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Copy(BufferPointer source, BufferPointer destination, int countInSource) + public static void Copy(BufferSpan source, BufferSpan destination, int countInSource) where T : struct { CopyImpl(source, destination, countInSource); @@ -56,14 +56,14 @@ namespace ImageSharp /// The destination buffer"/> /// The number of elements to copy. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void Copy(BufferPointer source, BufferPointer destination, int countInDest) + public static unsafe void Copy(BufferSpan source, BufferSpan destination, int countInDest) where T : struct { int byteCount = SizeOf(countInDest); if (byteCount > (int)ByteCountThreshold) { - Marshal.Copy(source.Array, source.Offset, destination.PointerAtOffset, byteCount); + Marshal.Copy(source.Array, source.Start, destination.PointerAtOffset, byteCount); } else { @@ -93,7 +93,7 @@ namespace ImageSharp => (uint)SizeOf(count); [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static unsafe void CopyImpl(BufferPointer source, BufferPointer destination, int count) + private static unsafe void CopyImpl(BufferSpan source, BufferSpan destination, int count) where T : struct where TDest : struct { @@ -103,22 +103,22 @@ namespace ImageSharp { if (Unsafe.SizeOf() == sizeof(long)) { - Marshal.Copy(Unsafe.As(source.Array), source.Offset, destination.PointerAtOffset, count); + 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.Offset, destination.PointerAtOffset, count); + Marshal.Copy(Unsafe.As(source.Array), source.Start, destination.PointerAtOffset, count); return; } else if (Unsafe.SizeOf() == sizeof(short)) { - Marshal.Copy(Unsafe.As(source.Array), source.Offset, destination.PointerAtOffset, count); + Marshal.Copy(Unsafe.As(source.Array), source.Start, destination.PointerAtOffset, count); return; } else if (Unsafe.SizeOf() == sizeof(byte)) { - Marshal.Copy(Unsafe.As(source.Array), source.Offset, destination.PointerAtOffset, count); + Marshal.Copy(Unsafe.As(source.Array), source.Start, destination.PointerAtOffset, count); return; } } diff --git a/src/ImageSharp/Common/Memory/BufferSpan{T}.cs b/src/ImageSharp/Common/Memory/BufferSpan{T}.cs new file mode 100644 index 0000000000..0507173e90 --- /dev/null +++ b/src/ImageSharp/Common/Memory/BufferSpan{T}.cs @@ -0,0 +1,209 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + using System.Diagnostics; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + + /// + /// Represents a contiguous region of a pinned managed array. + /// The array is usually owned by a instance. + /// + /// + /// is very similar to corefx System.Span<T>, and we try to maintain a compatible API. + /// There are several differences though: + /// - It's not possible to use it with stack objects or pointers to unmanaged memory, only with managed arrays. + /// - It's possible to retrieve a reference to the array () so we can pass it to API-s like + /// - It's possible to retrieve the pinned pointer. This enables optimized (unchecked) unsafe operations. + /// - There is no bounds checking for performance reasons, only in debug mode. This makes an unsafe type! + /// + /// The type of elements of the array + internal unsafe struct BufferSpan + where T : struct + { + /// + /// 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) + { + GuardArrayAndPointer(array, pointerToArray); + + DebugGuard.MustBeLessThanOrEqualTo(start, array.Length, nameof(start)); + DebugGuard.MustBeLessThanOrEqualTo(length, array.Length - start, nameof(length)); + + 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) + { + GuardArrayAndPointer(array, pointerToArray); + 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) + { + GuardArrayAndPointer(array, pointerToArray); + + this.Array = array; + this.Start = 0; + this.Length = array.Length; + this.PointerAtOffset = (IntPtr)pointerToArray; + } + + /// + /// Gets the backing array + /// + public T[] Array { get; private set; } + + /// + /// Gets the length of the + /// + public int Length { get; private set; } + + /// + /// Gets the start inside + /// + public int Start { get; private set; } + + /// + /// Gets the start inside in bytes. + /// + public int ByteOffset => this.Start * Unsafe.SizeOf(); + + /// + /// Gets the pointer to the offseted array position + /// + public IntPtr PointerAtOffset { get; private set; } + + /// + /// 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. + /// + /// The to convert + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator BufferSpan(BufferSpan source) + { + BufferSpan result = default(BufferSpan); + result.Array = Unsafe.As(source.Array); + result.Start = source.Start * Unsafe.SizeOf(); + result.PointerAtOffset = source.PointerAtOffset; + return result; + } + + /// + /// Forms a slice out of the given BufferSpan, beginning at 'start'. + /// + /// TThe index at which to begin this slice. + /// The offseted (sliced) BufferSpan + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public BufferSpan Slice(int start) + { + DebugGuard.MustBeLessThan(start, this.Length, nameof(start)); + + 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; + } + + /// + /// Forms a slice out of the given BufferSpan, beginning at 'start'. + /// + /// The index at which to begin this slice. + /// The desired length for the slice (exclusive). + /// The sliced BufferSpan + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public BufferSpan Slice(int start, int length) + { + DebugGuard.MustBeLessThanOrEqualTo(start, this.Length, nameof(start)); + DebugGuard.MustBeLessThanOrEqualTo(length, this.Length - start, nameof(length)); + + 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; + } + + /// + /// Clears `count` elements beginning from the pointed position. + /// + /// The number of elements to clear + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear(int count) + { + if (count < 256) + { + Unsafe.InitBlock((void*)this.PointerAtOffset, 0, BufferSpan.USizeOf(count)); + } + else + { + System.Array.Clear(this.Array, this.Start, count); + } + } + + [Conditional("DEBUG")] + private static void GuardArrayAndPointer(T[] array, void* pointerToArray) + { + 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 d002e08fbd..7378a8d640 100644 --- a/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs +++ b/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs @@ -32,11 +32,11 @@ namespace ImageSharp /// /// Initializes a new instance of the class. /// - /// The desired count of elements. (Minimum size for ) - public PinnedBuffer(int count) + /// The desired count of elements. (Minimum size for ) + public PinnedBuffer(int length) { - this.Count = count; - this.Array = PixelDataPool.Rent(count); + this.Length = length; + this.Array = PixelDataPool.Rent(length); this.isPoolingOwner = true; this.Pin(); } @@ -47,7 +47,7 @@ namespace ImageSharp /// The array to pin. public PinnedBuffer(T[] array) { - this.Count = array.Length; + this.Length = array.Length; this.Array = array; this.isPoolingOwner = false; this.Pin(); @@ -56,16 +56,16 @@ namespace ImageSharp /// /// Initializes a new instance of the class. /// - /// The count of "relevant" elements in 'array'. + /// The count of "relevant" elements in 'array'. /// The array to pin. - public PinnedBuffer(int count, T[] array) + public PinnedBuffer(int length, T[] array) { - if (array.Length < count) + if (array.Length < length) { throw new ArgumentException("Can't initialize a PinnedBuffer with array.Length < count", nameof(array)); } - this.Count = count; + this.Length = length; this.Array = array; this.isPoolingOwner = false; this.Pin(); @@ -85,9 +85,9 @@ namespace ImageSharp public bool IsDisposedOrLostArrayOwnership { get; private set; } /// - /// Gets the count of "relevant" elements. Usually be smaller than 'Array.Length' when is pooled. + /// Gets the count of "relevant" elements. It's usually smaller than 'Array.Length' when is pooled. /// - public int Count { get; private set; } + public int Length { get; private set; } /// /// Gets the backing pinned array. @@ -100,11 +100,11 @@ namespace ImageSharp public IntPtr Pointer { get; private set; } /// - /// Converts to an . + /// Converts to an . /// /// The to convert. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator BufferPointer(PinnedBuffer buffer) + public static implicit operator BufferSpan(PinnedBuffer buffer) { return buffer.Slice(); } @@ -122,24 +122,24 @@ namespace ImageSharp } /// - /// Gets a to the beginning of the raw data of the buffer. + /// Gets a to the beginning of the raw data of the buffer. /// - /// The + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe BufferPointer Slice() + public unsafe BufferSpan Slice() { - return new BufferPointer(this.Array, (void*)this.Pointer); + return new BufferSpan(this.Array, (void*)this.Pointer); } /// - /// Gets a to an offseted position inside the buffer. + /// Gets a to an offseted position inside the buffer. /// /// The offset - /// The + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public unsafe BufferPointer Slice(int offset) + public unsafe BufferSpan Slice(int offset) { - return new BufferPointer(this.Array, (void*)this.Pointer, offset); + return new BufferSpan(this.Array, (void*)this.Pointer, offset); } /// @@ -163,7 +163,7 @@ namespace ImageSharp this.isPoolingOwner = false; this.Array = null; - this.Count = 0; + this.Length = 0; GC.SuppressFinalize(this); } @@ -190,12 +190,12 @@ namespace ImageSharp } /// - /// Clears the buffer, filling elements between 0 and -1 with default(T) + /// Clears the buffer, filling elements between 0 and -1 with default(T) /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Clear() { - this.Slice().Clear(this.Count); + this.Slice().Clear(this.Length); } /// diff --git a/src/ImageSharp/Image/PixelAccessor{TColor}.cs b/src/ImageSharp/Image/PixelAccessor{TColor}.cs index 25e232cf86..d22deec742 100644 --- a/src/ImageSharp/Image/PixelAccessor{TColor}.cs +++ b/src/ImageSharp/Image/PixelAccessor{TColor}.cs @@ -239,12 +239,12 @@ namespace ImageSharp } /// - /// Gets a to the row 'y' beginning from the pixel at 'x'. + /// Gets a to the row 'y' beginning from the pixel at 'x'. /// /// The x coordinate /// The y coordinate - /// The - internal BufferPointer GetRowPointer(int x, int y) + /// The + internal BufferSpan GetRowPointer(int x, int y) { return this.pixelBuffer.Slice((y * this.Width) + x); } @@ -288,8 +288,8 @@ namespace ImageSharp { for (int y = 0; y < height; y++) { - BufferPointer source = area.GetRowPointer(y); - BufferPointer destination = this.GetRowPointer(targetX, targetY + y); + BufferSpan source = area.GetRowPointer(y); + BufferSpan destination = this.GetRowPointer(targetX, targetY + y); Operations.PackFromZyxBytes(source, destination, width); } @@ -308,8 +308,8 @@ namespace ImageSharp { for (int y = 0; y < height; y++) { - BufferPointer source = area.GetRowPointer(y); - BufferPointer destination = this.GetRowPointer(targetX, targetY + y); + BufferSpan source = area.GetRowPointer(y); + BufferSpan destination = this.GetRowPointer(targetX, targetY + y); Operations.PackFromZyxwBytes(source, destination, width); } @@ -328,8 +328,8 @@ namespace ImageSharp { for (int y = 0; y < height; y++) { - BufferPointer source = area.GetRowPointer(y); - BufferPointer destination = this.GetRowPointer(targetX, targetY + y); + BufferSpan source = area.GetRowPointer(y); + BufferSpan destination = this.GetRowPointer(targetX, targetY + y); Operations.PackFromXyzBytes(source, destination, width); } @@ -348,8 +348,8 @@ namespace ImageSharp { for (int y = 0; y < height; y++) { - BufferPointer source = area.GetRowPointer(y); - BufferPointer destination = this.GetRowPointer(targetX, targetY + y); + BufferSpan source = area.GetRowPointer(y); + BufferSpan destination = this.GetRowPointer(targetX, targetY + y); Operations.PackFromXyzwBytes(source, destination, width); } } @@ -367,8 +367,8 @@ namespace ImageSharp { for (int y = 0; y < height; y++) { - BufferPointer source = this.GetRowPointer(sourceX, sourceY + y); - BufferPointer destination = area.GetRowPointer(y); + BufferSpan source = this.GetRowPointer(sourceX, sourceY + y); + BufferSpan destination = area.GetRowPointer(y); Operations.ToZyxBytes(source, destination, width); } } @@ -386,8 +386,8 @@ namespace ImageSharp { for (int y = 0; y < height; y++) { - BufferPointer source = this.GetRowPointer(sourceX, sourceY + y); - BufferPointer destination = area.GetRowPointer(y); + BufferSpan source = this.GetRowPointer(sourceX, sourceY + y); + BufferSpan destination = area.GetRowPointer(y); Operations.ToZyxwBytes(source, destination, width); } } @@ -405,8 +405,8 @@ namespace ImageSharp { for (int y = 0; y < height; y++) { - BufferPointer source = this.GetRowPointer(sourceX, sourceY + y); - BufferPointer destination = area.GetRowPointer(y); + BufferSpan source = this.GetRowPointer(sourceX, sourceY + y); + BufferSpan destination = area.GetRowPointer(y); Operations.ToXyzBytes(source, destination, width); } } @@ -424,8 +424,8 @@ namespace ImageSharp { for (int y = 0; y < height; y++) { - BufferPointer source = this.GetRowPointer(sourceX, sourceY + y); - BufferPointer destination = area.GetRowPointer(y); + BufferSpan source = this.GetRowPointer(sourceX, sourceY + y); + BufferSpan destination = area.GetRowPointer(y); Operations.ToXyzwBytes(source, destination, width); } } diff --git a/src/ImageSharp/Image/PixelArea{TColor}.cs b/src/ImageSharp/Image/PixelArea{TColor}.cs index 8f2fa5b7a1..0a3c5710ce 100644 --- a/src/ImageSharp/Image/PixelArea{TColor}.cs +++ b/src/ImageSharp/Image/PixelArea{TColor}.cs @@ -202,11 +202,11 @@ namespace ImageSharp } /// - /// Gets a to the row y. + /// Gets a to the row y. /// /// The y coordinate - /// The - internal BufferPointer GetRowPointer(int y) + /// The + internal BufferSpan GetRowPointer(int y) { return this.byteBuffer.Slice(y * this.RowStride); } diff --git a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs index fa950c4cf2..141c9d888d 100644 --- a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs @@ -314,8 +314,8 @@ namespace ImageSharp.Tests.Colors public PinnedBuffer ActualDestBuffer { get; } public PinnedBuffer ExpectedDestBuffer { get; } - public BufferPointer Source => this.SourceBuffer.Slice(); - public BufferPointer ActualDest => this.ActualDestBuffer.Slice(); + public BufferSpan Source => this.SourceBuffer.Slice(); + public BufferSpan ActualDest => this.ActualDestBuffer.Slice(); public TestBuffers(TSource[] source, TDest[] expectedDest) { @@ -335,7 +335,7 @@ namespace ImageSharp.Tests.Colors public void Verify() { - int count = this.ExpectedDestBuffer.Count; + int count = this.ExpectedDestBuffer.Length; if (typeof(TDest) == typeof(Vector4)) { @@ -364,7 +364,7 @@ namespace ImageSharp.Tests.Colors internal static void TestOperation( TSource[] source, TDest[] expected, - Action, BufferPointer> action) + Action, BufferSpan> action) where TSource : struct where TDest : struct { diff --git a/tests/ImageSharp.Tests/Common/BufferPointerTests.cs b/tests/ImageSharp.Tests/Common/BufferSpanTests.cs similarity index 66% rename from tests/ImageSharp.Tests/Common/BufferPointerTests.cs rename to tests/ImageSharp.Tests/Common/BufferSpanTests.cs index c82b63f115..f524bdb437 100644 --- a/tests/ImageSharp.Tests/Common/BufferPointerTests.cs +++ b/tests/ImageSharp.Tests/Common/BufferSpanTests.cs @@ -7,7 +7,7 @@ namespace ImageSharp.Tests.Common using Xunit; - public unsafe class BufferPointerTests + public unsafe class BufferSpanTests { public struct Foo { @@ -70,68 +70,122 @@ namespace ImageSharp.Tests.Common using (PinnedBuffer colorBuf = new PinnedBuffer(fooz)) { - BufferPointer orig = colorBuf.Slice(1); - BufferPointer asBytes = (BufferPointer < byte > )orig; + BufferSpan orig = colorBuf.Slice(1); + BufferSpan asBytes = (BufferSpan < byte > )orig; - Assert.Equal(asBytes.Offset, sizeof(Foo)); + Assert.Equal(asBytes.Start, sizeof(Foo)); Assert.Equal(orig.PointerAtOffset, asBytes.PointerAtOffset); } } - - [Fact] - public void ConstructWithoutOffset() + + public class Construct { - Foo[] array = Foo.CreateArray(3); - fixed (Foo* p = array) + [Fact] + public void Basic() { - // Act: - BufferPointer ap = new BufferPointer(array, p); + Foo[] array = Foo.CreateArray(3); + fixed (Foo* p = array) + { + // Act: + BufferSpan span = new BufferSpan(array, p); - // Assert: - Assert.Equal(array, ap.Array); - Assert.Equal((IntPtr)p, ap.PointerAtOffset); + // Assert: + Assert.Equal(array, span.Array); + Assert.Equal((IntPtr)p, span.PointerAtOffset); + Assert.Equal(3, span.Length); + } } - } - [Fact] - public void ConstructWithOffset() - { - Foo[] array = Foo.CreateArray(3); - int offset = 2; - fixed (Foo* p = array) + [Fact] + public void WithStart() { - // Act: - BufferPointer ap = new BufferPointer(array, p, offset); + 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); + } + } - // Assert: - Assert.Equal(array, ap.Array); - Assert.Equal(offset, ap.Offset); - Assert.Equal((IntPtr)(p+offset), ap.PointerAtOffset); + [Fact] + public void WithStartAndLength() + { + 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); + } } } - [Fact] - public void Slice() + public class Slice { - Foo[] array = Foo.CreateArray(5); - int offset0 = 2; - int offset1 = 2; - int totalOffset = offset0 + offset1; - fixed (Foo* p = array) + [Fact] + public void StartOnly() { - BufferPointer ap = new BufferPointer(array, p, offset0); + Foo[] array = Foo.CreateArray(5); + int start0 = 2; + int start1 = 2; + int totalOffset = start0 + start1; - // Act: - ap = ap.Slice(offset1); + fixed (Foo* p = array) + { + BufferSpan span = new BufferSpan(array, p, start0); + + // Act: + span = span.Slice(start1); - // Assert: - Assert.Equal(array, ap.Array); - Assert.Equal(totalOffset, ap.Offset); - Assert.Equal((IntPtr)(p + totalOffset), ap.PointerAtOffset); + // 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); + } + } + + [Fact] + public void StartAndLength() + { + Foo[] array = Foo.CreateArray(10); + int start0 = 2; + int start1 = 2; + int totalOffset = start0 + start1; + int sliceLength = 3; + + fixed (Foo* p = array) + { + BufferSpan span = new BufferSpan(array, p, start0); + + // 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); + } } } + [Theory] [InlineData(4)] [InlineData(1500)] @@ -142,7 +196,7 @@ namespace ImageSharp.Tests.Common int offset = 2; fixed (Foo* p = array) { - BufferPointer ap = new BufferPointer(array, p, offset); + BufferSpan ap = new BufferSpan(array, p, offset); // Act: ap.Clear(count); @@ -194,10 +248,10 @@ namespace ImageSharp.Tests.Common fixed (Foo* pSource = source) fixed (Foo* pDest = dest) { - BufferPointer apSource = new BufferPointer(source, pSource, 1); - BufferPointer apDest = new BufferPointer(dest, pDest, 1); + BufferSpan apSource = new BufferSpan(source, pSource, 1); + BufferSpan apDest = new BufferSpan(dest, pDest, 1); - BufferPointer.Copy(apSource, apDest, count-1); + BufferSpan.Copy(apSource, apDest, count-1); } AssertNotDefault(source, 1); @@ -221,10 +275,10 @@ namespace ImageSharp.Tests.Common fixed (AlignedFoo* pSource = source) fixed (AlignedFoo* pDest = dest) { - BufferPointer apSource = new BufferPointer(source, pSource, 1); - BufferPointer apDest = new BufferPointer(dest, pDest, 1); + BufferSpan apSource = new BufferSpan(source, pSource, 1); + BufferSpan apDest = new BufferSpan(dest, pDest, 1); - BufferPointer.Copy(apSource, apDest, count - 1); + BufferSpan.Copy(apSource, apDest, count - 1); } AssertNotDefault(source, 1); @@ -248,10 +302,10 @@ namespace ImageSharp.Tests.Common fixed (int* pSource = source) fixed (int* pDest = dest) { - BufferPointer apSource = new BufferPointer(source, pSource, 1); - BufferPointer apDest = new BufferPointer(dest, pDest, 1); + BufferSpan apSource = new BufferSpan(source, pSource, 1); + BufferSpan apDest = new BufferSpan(dest, pDest, 1); - BufferPointer.Copy(apSource, apDest, count -1); + BufferSpan.Copy(apSource, apDest, count -1); } AssertNotDefault(source, 1); @@ -276,10 +330,10 @@ namespace ImageSharp.Tests.Common fixed (Foo* pSource = source) fixed (byte* pDest = dest) { - BufferPointer apSource = new BufferPointer(source, pSource, 1); - BufferPointer apDest = new BufferPointer(dest, pDest, sizeof(Foo)); + BufferSpan apSource = new BufferSpan(source, pSource, 1); + BufferSpan apDest = new BufferSpan(dest, pDest, sizeof(Foo)); - BufferPointer.Copy(apSource, apDest, count - 1); + BufferSpan.Copy(apSource, apDest, count - 1); } AssertNotDefault(source, 1); @@ -303,10 +357,10 @@ namespace ImageSharp.Tests.Common fixed (AlignedFoo* pSource = source) fixed (byte* pDest = dest) { - BufferPointer apSource = new BufferPointer(source, pSource, 1); - BufferPointer apDest = new BufferPointer(dest, pDest, sizeof(AlignedFoo)); + BufferSpan apSource = new BufferSpan(source, pSource, 1); + BufferSpan apDest = new BufferSpan(dest, pDest, sizeof(AlignedFoo)); - BufferPointer.Copy(apSource, apDest, count - 1); + BufferSpan.Copy(apSource, apDest, count - 1); } AssertNotDefault(source, 1); @@ -330,10 +384,10 @@ namespace ImageSharp.Tests.Common fixed (int* pSource = source) fixed (byte* pDest = dest) { - BufferPointer apSource = new BufferPointer(source, pSource); - BufferPointer apDest = new BufferPointer(dest, pDest); + BufferSpan apSource = new BufferSpan(source, pSource); + BufferSpan apDest = new BufferSpan(dest, pDest); - BufferPointer.Copy(apSource, apDest, count); + BufferSpan.Copy(apSource, apDest, count); } AssertNotDefault(source, 1); @@ -355,10 +409,10 @@ namespace ImageSharp.Tests.Common fixed(byte* pSource = source) fixed (Foo* pDest = dest) { - BufferPointer apSource = new BufferPointer(source, pSource); - BufferPointer apDest = new BufferPointer(dest, pDest); + BufferSpan apSource = new BufferSpan(source, pSource); + BufferSpan apDest = new BufferSpan(dest, pDest); - BufferPointer.Copy(apSource, apDest, count); + BufferSpan.Copy(apSource, apDest, count); } AssertNotDefault(source, sizeof(Foo) + 1); @@ -378,11 +432,11 @@ namespace ImageSharp.Tests.Common using (PinnedBuffer colorBuf = new PinnedBuffer(colors)) using (PinnedBuffer byteBuf = new PinnedBuffer(colors.Length*4)) { - BufferPointer.Copy(colorBuf, byteBuf, colorBuf.Count); + BufferSpan.Copy(colorBuf, byteBuf, colorBuf.Length); byte[] a = byteBuf.Array; - for (int i = 0; i < byteBuf.Count; i++) + for (int i = 0; i < byteBuf.Length; i++) { Assert.Equal((byte)i, a[i]); } diff --git a/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs b/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs index d47bd5b94e..9eb12a536a 100644 --- a/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs +++ b/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs @@ -25,7 +25,7 @@ { Assert.False(buffer.IsDisposedOrLostArrayOwnership); Assert.NotNull(buffer.Array); - Assert.Equal(count, buffer.Count); + Assert.Equal(count, buffer.Length); Assert.True(buffer.Array.Length >= count); VerifyPointer(buffer); @@ -42,7 +42,7 @@ { Assert.False(buffer.IsDisposedOrLostArrayOwnership); Assert.Equal(array, buffer.Array); - Assert.Equal(count, buffer.Count); + Assert.Equal(count, buffer.Length); VerifyPointer(buffer); } @@ -71,7 +71,7 @@ { using (PinnedBuffer buffer = PinnedBuffer.CreateClean(42)) { - for (int j = 0; j < buffer.Count; j++) + for (int j = 0; j < buffer.Length; j++) { Assert.Equal(0, buffer.Array[j]); buffer.Array[j] = 666; @@ -96,10 +96,10 @@ using (PinnedBuffer buffer = new PinnedBuffer(a)) { - BufferPointer arrayPtr = buffer.Slice(); + BufferSpan arrayPtr = buffer.Slice(); Assert.Equal(a, arrayPtr.Array); - Assert.Equal(0, arrayPtr.Offset); + Assert.Equal(0, arrayPtr.Start); Assert.Equal(buffer.Pointer, arrayPtr.PointerAtOffset); } }