Browse Source

BufferPointer<T> became BufferSpan<T>

pull/141/head
Anton Firszov 9 years ago
parent
commit
b2fb41fab5
  1. 32
      src/ImageSharp/Colors/Color.BulkOperations.cs
  2. 84
      src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs
  3. 143
      src/ImageSharp/Common/Memory/BufferPointer{T}.cs
  4. 28
      src/ImageSharp/Common/Memory/BufferSpan.cs
  5. 209
      src/ImageSharp/Common/Memory/BufferSpan{T}.cs
  6. 48
      src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs
  7. 38
      src/ImageSharp/Image/PixelAccessor{TColor}.cs
  8. 6
      src/ImageSharp/Image/PixelArea{TColor}.cs
  9. 8
      tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs
  10. 184
      tests/ImageSharp.Tests/Common/BufferSpanTests.cs
  11. 10
      tests/ImageSharp.Tests/Common/PinnedBufferTests.cs

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

@ -24,8 +24,8 @@ namespace ImageSharp
/// SIMD optimized bulk implementation of <see cref="IPixel.PackFromVector4(Vector4)"/> /// SIMD optimized bulk implementation of <see cref="IPixel.PackFromVector4(Vector4)"/>
/// that works only with `count` divisible by <see cref="Vector{UInt32}.Count"/>. /// that works only with `count` divisible by <see cref="Vector{UInt32}.Count"/>.
/// </summary> /// </summary>
/// <param name="sourceColors">The <see cref="BufferPointer{T}"/> to the source colors.</param> /// <param name="sourceColors">The <see cref="BufferSpan{T}"/> to the source colors.</param>
/// <param name="destVectors">The <see cref="BufferPointer{T}"/> to the dstination vectors.</param> /// <param name="destVectors">The <see cref="BufferSpan{T}"/> to the dstination vectors.</param>
/// <param name="count">The number of pixels to convert.</param> /// <param name="count">The number of pixels to convert.</param>
/// <remarks> /// <remarks>
/// Implementation adapted from: /// Implementation adapted from:
@ -38,8 +38,8 @@ namespace ImageSharp
/// </see> /// </see>
/// </remarks> /// </remarks>
internal static unsafe void ToVector4SimdAligned( internal static unsafe void ToVector4SimdAligned(
BufferPointer<Color> sourceColors, BufferSpan<Color> sourceColors,
BufferPointer<Vector4> destVectors, BufferSpan<Vector4> destVectors,
int count) int count)
{ {
int vecSize = Vector<uint>.Count; int vecSize = Vector<uint>.Count;
@ -85,12 +85,12 @@ namespace ImageSharp
vf.CopyTo(fTemp, i); vf.CopyTo(fTemp, i);
} }
BufferPointer.Copy<uint>(tempBuf, (BufferPointer<byte>)destVectors, unpackedRawCount); BufferSpan.Copy<uint>(tempBuf, (BufferSpan<byte>)destVectors, unpackedRawCount);
} }
} }
/// <inheritdoc /> /// <inheritdoc />
internal override void ToVector4(BufferPointer<Color> sourceColors, BufferPointer<Vector4> destVectors, int count) internal override void ToVector4(BufferSpan<Color> sourceColors, BufferSpan<Vector4> destVectors, int count)
{ {
if (count < 256) if (count < 256)
{ {
@ -117,7 +117,7 @@ namespace ImageSharp
} }
/// <inheritdoc /> /// <inheritdoc />
internal override unsafe void PackFromXyzBytes(BufferPointer<byte> sourceBytes, BufferPointer<Color> destColors, int count) internal override unsafe void PackFromXyzBytes(BufferSpan<byte> sourceBytes, BufferSpan<Color> destColors, int count)
{ {
byte* source = (byte*)sourceBytes; byte* source = (byte*)sourceBytes;
byte* destination = (byte*)destColors; byte* destination = (byte*)destColors;
@ -132,7 +132,7 @@ namespace ImageSharp
} }
/// <inheritdoc /> /// <inheritdoc />
internal override unsafe void ToXyzBytes(BufferPointer<Color> sourceColors, BufferPointer<byte> destBytes, int count) internal override unsafe void ToXyzBytes(BufferSpan<Color> sourceColors, BufferSpan<byte> destBytes, int count)
{ {
byte* source = (byte*)sourceColors; byte* source = (byte*)sourceColors;
byte* destination = (byte*)destBytes; byte* destination = (byte*)destBytes;
@ -149,19 +149,19 @@ namespace ImageSharp
} }
/// <inheritdoc /> /// <inheritdoc />
internal override void PackFromXyzwBytes(BufferPointer<byte> sourceBytes, BufferPointer<Color> destColors, int count) internal override void PackFromXyzwBytes(BufferSpan<byte> sourceBytes, BufferSpan<Color> destColors, int count)
{ {
BufferPointer.Copy(sourceBytes, destColors, count); BufferSpan.Copy(sourceBytes, destColors, count);
} }
/// <inheritdoc /> /// <inheritdoc />
internal override void ToXyzwBytes(BufferPointer<Color> sourceColors, BufferPointer<byte> destBytes, int count) internal override void ToXyzwBytes(BufferSpan<Color> sourceColors, BufferSpan<byte> destBytes, int count)
{ {
BufferPointer.Copy(sourceColors, destBytes, count); BufferSpan.Copy(sourceColors, destBytes, count);
} }
/// <inheritdoc /> /// <inheritdoc />
internal override unsafe void PackFromZyxBytes(BufferPointer<byte> sourceBytes, BufferPointer<Color> destColors, int count) internal override unsafe void PackFromZyxBytes(BufferSpan<byte> sourceBytes, BufferSpan<Color> destColors, int count)
{ {
byte* source = (byte*)sourceBytes; byte* source = (byte*)sourceBytes;
byte* destination = (byte*)destColors; byte* destination = (byte*)destColors;
@ -176,7 +176,7 @@ namespace ImageSharp
} }
/// <inheritdoc /> /// <inheritdoc />
internal override unsafe void ToZyxBytes(BufferPointer<Color> sourceColors, BufferPointer<byte> destBytes, int count) internal override unsafe void ToZyxBytes(BufferSpan<Color> sourceColors, BufferSpan<byte> destBytes, int count)
{ {
byte* source = (byte*)sourceColors; byte* source = (byte*)sourceColors;
byte* destination = (byte*)destBytes; byte* destination = (byte*)destBytes;
@ -193,7 +193,7 @@ namespace ImageSharp
} }
/// <inheritdoc /> /// <inheritdoc />
internal override unsafe void PackFromZyxwBytes(BufferPointer<byte> sourceBytes, BufferPointer<Color> destColors, int count) internal override unsafe void PackFromZyxwBytes(BufferSpan<byte> sourceBytes, BufferSpan<Color> destColors, int count)
{ {
byte* source = (byte*)sourceBytes; byte* source = (byte*)sourceBytes;
byte* destination = (byte*)destColors; byte* destination = (byte*)destColors;
@ -208,7 +208,7 @@ namespace ImageSharp
} }
/// <inheritdoc /> /// <inheritdoc />
internal override unsafe void ToZyxwBytes(BufferPointer<Color> sourceColors, BufferPointer<byte> destBytes, int count) internal override unsafe void ToZyxwBytes(BufferSpan<Color> sourceColors, BufferSpan<byte> destBytes, int count)
{ {
byte* source = (byte*)sourceColors; byte* source = (byte*)sourceColors;
byte* destination = (byte*)destBytes; byte* destination = (byte*)destBytes;

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

@ -29,12 +29,12 @@ namespace ImageSharp
/// <summary> /// <summary>
/// Bulk version of <see cref="IPixel.PackFromVector4(Vector4)"/> /// Bulk version of <see cref="IPixel.PackFromVector4(Vector4)"/>
/// </summary> /// </summary>
/// <param name="sourceVectors">The <see cref="BufferPointer{T}"/> to the source vectors.</param> /// <param name="sourceVectors">The <see cref="BufferSpan{T}"/> to the source vectors.</param>
/// <param name="destColors">The <see cref="BufferPointer{T}"/> to the destination colors.</param> /// <param name="destColors">The <see cref="BufferSpan{T}"/> to the destination colors.</param>
/// <param name="count">The number of pixels to convert.</param> /// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFromVector4( internal virtual void PackFromVector4(
BufferPointer<Vector4> sourceVectors, BufferSpan<Vector4> sourceVectors,
BufferPointer<TColor> destColors, BufferSpan<TColor> destColors,
int count) int count)
{ {
Vector4* sp = (Vector4*)sourceVectors.PointerAtOffset; Vector4* sp = (Vector4*)sourceVectors.PointerAtOffset;
@ -55,12 +55,12 @@ namespace ImageSharp
/// <summary> /// <summary>
/// Bulk version of <see cref="IPixel.ToVector4()"/>. /// Bulk version of <see cref="IPixel.ToVector4()"/>.
/// </summary> /// </summary>
/// <param name="sourceColors">The <see cref="BufferPointer{T}"/> to the source colors.</param> /// <param name="sourceColors">The <see cref="BufferSpan{T}"/> to the source colors.</param>
/// <param name="destVectors">The <see cref="BufferPointer{T}"/> to the destination vectors.</param> /// <param name="destVectors">The <see cref="BufferSpan{T}"/> to the destination vectors.</param>
/// <param name="count">The number of pixels to convert.</param> /// <param name="count">The number of pixels to convert.</param>
internal virtual void ToVector4( internal virtual void ToVector4(
BufferPointer<TColor> sourceColors, BufferSpan<TColor> sourceColors,
BufferPointer<Vector4> destVectors, BufferSpan<Vector4> destVectors,
int count) int count)
{ {
byte* sp = (byte*)sourceColors; byte* sp = (byte*)sourceColors;
@ -78,12 +78,12 @@ namespace ImageSharp
/// <summary> /// <summary>
/// Bulk version of <see cref="IPixel.PackFromBytes(byte, byte, byte, byte)"/> that converts data in <see cref="ComponentOrder.Xyz"/>. /// Bulk version of <see cref="IPixel.PackFromBytes(byte, byte, byte, byte)"/> that converts data in <see cref="ComponentOrder.Xyz"/>.
/// </summary> /// </summary>
/// <param name="sourceBytes">The <see cref="BufferPointer{T}"/> to the source bytes.</param> /// <param name="sourceBytes">The <see cref="BufferSpan{T}"/> to the source bytes.</param>
/// <param name="destColors">The <see cref="BufferPointer{T}"/> to the destination colors.</param> /// <param name="destColors">The <see cref="BufferSpan{T}"/> to the destination colors.</param>
/// <param name="count">The number of pixels to convert.</param> /// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFromXyzBytes( internal virtual void PackFromXyzBytes(
BufferPointer<byte> sourceBytes, BufferSpan<byte> sourceBytes,
BufferPointer<TColor> destColors, BufferSpan<TColor> destColors,
int count) int count)
{ {
byte* sp = (byte*)sourceBytes; byte* sp = (byte*)sourceBytes;
@ -102,15 +102,15 @@ namespace ImageSharp
/// <summary> /// <summary>
/// Bulk version of <see cref="IPixel.ToXyzBytes(byte[], int)"/>. /// Bulk version of <see cref="IPixel.ToXyzBytes(byte[], int)"/>.
/// </summary> /// </summary>
/// <param name="sourceColors">The <see cref="BufferPointer{T}"/> to the source colors.</param> /// <param name="sourceColors">The <see cref="BufferSpan{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="BufferPointer{T}"/> to the destination bytes.</param> /// <param name="destBytes">The <see cref="BufferSpan{T}"/> to the destination bytes.</param>
/// <param name="count">The number of pixels to convert.</param> /// <param name="count">The number of pixels to convert.</param>
internal virtual void ToXyzBytes(BufferPointer<TColor> sourceColors, BufferPointer<byte> destBytes, int count) internal virtual void ToXyzBytes(BufferSpan<TColor> sourceColors, BufferSpan<byte> destBytes, int count)
{ {
byte* sp = (byte*)sourceColors; byte* sp = (byte*)sourceColors;
byte[] dest = destBytes.Array; 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<TColor>(sp); TColor c = Unsafe.Read<TColor>(sp);
c.ToXyzBytes(dest, i); c.ToXyzBytes(dest, i);
@ -121,12 +121,12 @@ namespace ImageSharp
/// <summary> /// <summary>
/// Bulk version of <see cref="IPixel.PackFromBytes(byte, byte, byte, byte)"/> that converts data in <see cref="ComponentOrder.Xyzw"/>. /// Bulk version of <see cref="IPixel.PackFromBytes(byte, byte, byte, byte)"/> that converts data in <see cref="ComponentOrder.Xyzw"/>.
/// </summary> /// </summary>
/// <param name="sourceBytes">The <see cref="BufferPointer{T}"/> to the source bytes.</param> /// <param name="sourceBytes">The <see cref="BufferSpan{T}"/> to the source bytes.</param>
/// <param name="destColors">The <see cref="BufferPointer{T}"/> to the destination colors.</param> /// <param name="destColors">The <see cref="BufferSpan{T}"/> to the destination colors.</param>
/// <param name="count">The number of pixels to convert.</param> /// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFromXyzwBytes( internal virtual void PackFromXyzwBytes(
BufferPointer<byte> sourceBytes, BufferSpan<byte> sourceBytes,
BufferPointer<TColor> destColors, BufferSpan<TColor> destColors,
int count) int count)
{ {
byte* sp = (byte*)sourceBytes; byte* sp = (byte*)sourceBytes;
@ -145,18 +145,18 @@ namespace ImageSharp
/// <summary> /// <summary>
/// Bulk version of <see cref="IPixel.ToXyzwBytes(byte[], int)"/>. /// Bulk version of <see cref="IPixel.ToXyzwBytes(byte[], int)"/>.
/// </summary> /// </summary>
/// <param name="sourceColors">The <see cref="BufferPointer{T}"/> to the source colors.</param> /// <param name="sourceColors">The <see cref="BufferSpan{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="BufferPointer{T}"/> to the destination bytes.</param> /// <param name="destBytes">The <see cref="BufferSpan{T}"/> to the destination bytes.</param>
/// <param name="count">The number of pixels to convert.</param> /// <param name="count">The number of pixels to convert.</param>
internal virtual void ToXyzwBytes( internal virtual void ToXyzwBytes(
BufferPointer<TColor> sourceColors, BufferSpan<TColor> sourceColors,
BufferPointer<byte> destBytes, BufferSpan<byte> destBytes,
int count) int count)
{ {
byte* sp = (byte*)sourceColors; byte* sp = (byte*)sourceColors;
byte[] dest = destBytes.Array; 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<TColor>(sp); TColor c = Unsafe.Read<TColor>(sp);
c.ToXyzwBytes(dest, i); c.ToXyzwBytes(dest, i);
@ -167,12 +167,12 @@ namespace ImageSharp
/// <summary> /// <summary>
/// Bulk version of <see cref="IPixel.PackFromBytes(byte, byte, byte, byte)"/> that converts data in <see cref="ComponentOrder.Zyx"/>. /// Bulk version of <see cref="IPixel.PackFromBytes(byte, byte, byte, byte)"/> that converts data in <see cref="ComponentOrder.Zyx"/>.
/// </summary> /// </summary>
/// <param name="sourceBytes">The <see cref="BufferPointer{T}"/> to the source bytes.</param> /// <param name="sourceBytes">The <see cref="BufferSpan{T}"/> to the source bytes.</param>
/// <param name="destColors">The <see cref="BufferPointer{T}"/> to the destination colors.</param> /// <param name="destColors">The <see cref="BufferSpan{T}"/> to the destination colors.</param>
/// <param name="count">The number of pixels to convert.</param> /// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFromZyxBytes( internal virtual void PackFromZyxBytes(
BufferPointer<byte> sourceBytes, BufferSpan<byte> sourceBytes,
BufferPointer<TColor> destColors, BufferSpan<TColor> destColors,
int count) int count)
{ {
byte* sp = (byte*)sourceBytes; byte* sp = (byte*)sourceBytes;
@ -191,15 +191,15 @@ namespace ImageSharp
/// <summary> /// <summary>
/// Bulk version of <see cref="IPixel.ToZyxBytes(byte[], int)"/>. /// Bulk version of <see cref="IPixel.ToZyxBytes(byte[], int)"/>.
/// </summary> /// </summary>
/// <param name="sourceColors">The <see cref="BufferPointer{T}"/> to the source colors.</param> /// <param name="sourceColors">The <see cref="BufferSpan{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="BufferPointer{T}"/> to the destination bytes.</param> /// <param name="destBytes">The <see cref="BufferSpan{T}"/> to the destination bytes.</param>
/// <param name="count">The number of pixels to convert.</param> /// <param name="count">The number of pixels to convert.</param>
internal virtual void ToZyxBytes(BufferPointer<TColor> sourceColors, BufferPointer<byte> destBytes, int count) internal virtual void ToZyxBytes(BufferSpan<TColor> sourceColors, BufferSpan<byte> destBytes, int count)
{ {
byte* sp = (byte*)sourceColors; byte* sp = (byte*)sourceColors;
byte[] dest = destBytes.Array; 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<TColor>(sp); TColor c = Unsafe.Read<TColor>(sp);
c.ToZyxBytes(dest, i); c.ToZyxBytes(dest, i);
@ -210,12 +210,12 @@ namespace ImageSharp
/// <summary> /// <summary>
/// Bulk version of <see cref="IPixel.PackFromBytes(byte, byte, byte, byte)"/> that converts data in <see cref="ComponentOrder.Zyxw"/>. /// Bulk version of <see cref="IPixel.PackFromBytes(byte, byte, byte, byte)"/> that converts data in <see cref="ComponentOrder.Zyxw"/>.
/// </summary> /// </summary>
/// <param name="sourceBytes">The <see cref="BufferPointer{T}"/> to the source bytes.</param> /// <param name="sourceBytes">The <see cref="BufferSpan{T}"/> to the source bytes.</param>
/// <param name="destColors">The <see cref="BufferPointer{T}"/> to the destination colors.</param> /// <param name="destColors">The <see cref="BufferSpan{T}"/> to the destination colors.</param>
/// <param name="count">The number of pixels to convert.</param> /// <param name="count">The number of pixels to convert.</param>
internal virtual void PackFromZyxwBytes( internal virtual void PackFromZyxwBytes(
BufferPointer<byte> sourceBytes, BufferSpan<byte> sourceBytes,
BufferPointer<TColor> destColors, BufferSpan<TColor> destColors,
int count) int count)
{ {
byte* sp = (byte*)sourceBytes; byte* sp = (byte*)sourceBytes;
@ -234,18 +234,18 @@ namespace ImageSharp
/// <summary> /// <summary>
/// Bulk version of <see cref="IPixel.ToZyxwBytes(byte[], int)"/>. /// Bulk version of <see cref="IPixel.ToZyxwBytes(byte[], int)"/>.
/// </summary> /// </summary>
/// <param name="sourceColors">The <see cref="BufferPointer{T}"/> to the source colors.</param> /// <param name="sourceColors">The <see cref="BufferSpan{T}"/> to the source colors.</param>
/// <param name="destBytes">The <see cref="BufferPointer{T}"/> to the destination bytes.</param> /// <param name="destBytes">The <see cref="BufferSpan{T}"/> to the destination bytes.</param>
/// <param name="count">The number of pixels to convert.</param> /// <param name="count">The number of pixels to convert.</param>
internal virtual void ToZyxwBytes( internal virtual void ToZyxwBytes(
BufferPointer<TColor> sourceColors, BufferSpan<TColor> sourceColors,
BufferPointer<byte> destBytes, BufferSpan<byte> destBytes,
int count) int count)
{ {
byte* sp = (byte*)sourceColors; byte* sp = (byte*)sourceColors;
byte[] dest = destBytes.Array; 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<TColor>(sp); TColor c = Unsafe.Read<TColor>(sp);
c.ToZyxwBytes(dest, i); c.ToZyxwBytes(dest, i);

143
src/ImageSharp/Common/Memory/BufferPointer{T}.cs

@ -1,143 +0,0 @@
// <copyright file="BufferPointer{T}.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
/// <summary>
/// Provides access to elements in an array from a given position.
/// This type shares many similarities with corefx System.Span&lt;T&gt; 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 (<see cref="Array"/>) so we can pass it to API-s like <see cref="Marshal.Copy(byte[], int, IntPtr, int)"/>
/// - 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 <see cref="BufferPointer{T}"/> an unsafe type!
/// - Currently the arrays provided to BufferPointer need to be pinned. This behaviour could be changed using C#7 features.
/// </summary>
/// <typeparam name="T">The type of elements of the array</typeparam>
internal unsafe struct BufferPointer<T>
where T : struct
{
/// <summary>
/// Initializes a new instance of the <see cref="BufferPointer{T}"/> struct from a pinned array and an offset.
/// </summary>
/// <param name="array">The pinned array</param>
/// <param name="pointerToArray">Pointer to the beginning of array</param>
/// <param name="offset">The offset inside the array</param>
[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<T>() * offset);
}
/// <summary>
/// Initializes a new instance of the <see cref="BufferPointer{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 BufferPointer(T[] array, void* pointerToArray)
{
DebugGuard.NotNull(array, nameof(array));
this.Array = array;
this.Offset = 0;
this.PointerAtOffset = (IntPtr)pointerToArray;
}
/// <summary>
/// Gets the array
/// </summary>
public T[] Array { get; private set; }
/// <summary>
/// Gets the offset inside <see cref="Array"/>
/// </summary>
public int Offset { get; private set; }
/// <summary>
/// Gets the offset inside <see cref="Array"/> in bytes.
/// </summary>
public int ByteOffset => this.Offset * Unsafe.SizeOf<T>();
/// <summary>
/// Gets the pointer to the offseted array position
/// </summary>
public IntPtr PointerAtOffset { get; private set; }
/// <summary>
/// Convertes <see cref="BufferPointer{T}"/> instance to a raw 'void*' pointer
/// </summary>
/// <param name="bufferPointer">The <see cref="BufferPointer{T}"/> to convert</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator void*(BufferPointer<T> bufferPointer)
{
return (void*)bufferPointer.PointerAtOffset;
}
/// <summary>
/// Converts <see cref="BufferPointer{T}"/> instance to a raw 'byte*' pointer
/// </summary>
/// <param name="bufferPointer">The <see cref="BufferPointer{T}"/> to convert</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator byte*(BufferPointer<T> bufferPointer)
{
return (byte*)bufferPointer.PointerAtOffset;
}
/// <summary>
/// Converts <see cref="BufferPointer{T}"/> instance to <see cref="BufferPointer{Byte}"/>
/// setting it's <see cref="Offset"/> and <see cref="PointerAtOffset"/> to correct values.
/// </summary>
/// <param name="source">The <see cref="BufferPointer{T}"/> to convert</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator BufferPointer<byte>(BufferPointer<T> source)
{
BufferPointer<byte> result = default(BufferPointer<byte>);
result.Array = Unsafe.As<byte[]>(source.Array);
result.Offset = source.Offset * Unsafe.SizeOf<T>();
result.PointerAtOffset = source.PointerAtOffset;
return result;
}
/// <summary>
/// Forms a slice out of the given BufferPointer, beginning at 'offset'.
/// </summary>
/// <param name="offset">The offset in number of elements</param>
/// <returns>The offseted (sliced) BufferPointer</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public BufferPointer<T> Slice(int offset)
{
BufferPointer<T> result = default(BufferPointer<T>);
result.Array = this.Array;
result.Offset = this.Offset + offset;
result.PointerAtOffset = this.PointerAtOffset + (Unsafe.SizeOf<T>() * offset);
return result;
}
/// <summary>
/// Clears `count` elements beginning from the pointed position.
/// </summary>
/// <param name="count">The number of elements to clear</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear(int count)
{
if (count < 256)
{
Unsafe.InitBlock((void*)this.PointerAtOffset, 0, BufferPointer.USizeOf<T>(count));
}
else
{
System.Array.Clear(this.Array, this.Offset, count);
}
}
}
}

28
src/ImageSharp/Common/Memory/BufferPointer.cs → src/ImageSharp/Common/Memory/BufferSpan.cs

@ -1,4 +1,4 @@
// <copyright file="BufferPointer.cs" company="James Jackson-South"> // <copyright file="BufferSpan.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors. // Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
// </copyright> // </copyright>
@ -11,9 +11,9 @@ namespace ImageSharp
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
/// <summary> /// <summary>
/// Utility methods for <see cref="BufferPointer{T}"/> /// Utility methods for <see cref="BufferSpan{T}"/>
/// </summary> /// </summary>
internal static class BufferPointer internal static class BufferSpan
{ {
/// <summary> /// <summary>
/// It's worth to use Marshal.Copy() or Buffer.BlockCopy() over this size. /// 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' /// Copy 'count' number of elements of the same type from 'source' to 'dest'
/// </summary> /// </summary>
/// <typeparam name="T">The element type.</typeparam> /// <typeparam name="T">The element type.</typeparam>
/// <param name="source">The input <see cref="BufferPointer{T}"/></param> /// <param name="source">The input <see cref="BufferSpan{T}"/></param>
/// <param name="destination">The destination <see cref="BufferPointer{T}"/>.</param> /// <param name="destination">The destination <see cref="BufferSpan{T}"/>.</param>
/// <param name="count">The number of elements to copy</param> /// <param name="count">The number of elements to copy</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Copy<T>(BufferPointer<T> source, BufferPointer<T> destination, int count) public static void Copy<T>(BufferSpan<T> source, BufferSpan<T> destination, int count)
where T : struct where T : struct
{ {
CopyImpl(source, destination, count); CopyImpl(source, destination, count);
@ -42,7 +42,7 @@ namespace ImageSharp
/// <param name="destination">The destination buffer.</param> /// <param name="destination">The destination buffer.</param>
/// <param name="countInSource">The number of elements to copy from 'source'</param> /// <param name="countInSource">The number of elements to copy from 'source'</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Copy<T>(BufferPointer<T> source, BufferPointer<byte> destination, int countInSource) public static void Copy<T>(BufferSpan<T> source, BufferSpan<byte> destination, int countInSource)
where T : struct where T : struct
{ {
CopyImpl(source, destination, countInSource); CopyImpl(source, destination, countInSource);
@ -56,14 +56,14 @@ namespace ImageSharp
/// <param name="destination">The destination buffer"/></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="T"/> elements to copy.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static unsafe void Copy<T>(BufferPointer<byte> source, BufferPointer<T> destination, int countInDest) public static unsafe void Copy<T>(BufferSpan<byte> source, BufferSpan<T> destination, int countInDest)
where T : struct where T : struct
{ {
int byteCount = SizeOf<T>(countInDest); int byteCount = SizeOf<T>(countInDest);
if (byteCount > (int)ByteCountThreshold) if (byteCount > (int)ByteCountThreshold)
{ {
Marshal.Copy(source.Array, source.Offset, destination.PointerAtOffset, byteCount); Marshal.Copy(source.Array, source.Start, destination.PointerAtOffset, byteCount);
} }
else else
{ {
@ -93,7 +93,7 @@ namespace ImageSharp
=> (uint)SizeOf<T>(count); => (uint)SizeOf<T>(count);
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe void CopyImpl<T, TDest>(BufferPointer<T> source, BufferPointer<TDest> destination, int count) private static unsafe void CopyImpl<T, TDest>(BufferSpan<T> source, BufferSpan<TDest> destination, int count)
where T : struct where T : struct
where TDest : struct where TDest : struct
{ {
@ -103,22 +103,22 @@ namespace ImageSharp
{ {
if (Unsafe.SizeOf<T>() == sizeof(long)) if (Unsafe.SizeOf<T>() == sizeof(long))
{ {
Marshal.Copy(Unsafe.As<long[]>(source.Array), source.Offset, destination.PointerAtOffset, count); Marshal.Copy(Unsafe.As<long[]>(source.Array), source.Start, destination.PointerAtOffset, count);
return; return;
} }
else if (Unsafe.SizeOf<T>() == sizeof(int)) else if (Unsafe.SizeOf<T>() == sizeof(int))
{ {
Marshal.Copy(Unsafe.As<int[]>(source.Array), source.Offset, destination.PointerAtOffset, count); Marshal.Copy(Unsafe.As<int[]>(source.Array), source.Start, destination.PointerAtOffset, count);
return; return;
} }
else if (Unsafe.SizeOf<T>() == sizeof(short)) else if (Unsafe.SizeOf<T>() == sizeof(short))
{ {
Marshal.Copy(Unsafe.As<short[]>(source.Array), source.Offset, destination.PointerAtOffset, count); Marshal.Copy(Unsafe.As<short[]>(source.Array), source.Start, destination.PointerAtOffset, count);
return; return;
} }
else if (Unsafe.SizeOf<T>() == sizeof(byte)) else if (Unsafe.SizeOf<T>() == sizeof(byte))
{ {
Marshal.Copy(Unsafe.As<byte[]>(source.Array), source.Offset, destination.PointerAtOffset, count); Marshal.Copy(Unsafe.As<byte[]>(source.Array), source.Start, destination.PointerAtOffset, count);
return; return;
} }
} }

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

@ -0,0 +1,209 @@
// <copyright file="BufferSpan{T}.cs" company="James Jackson-South">
// Copyright (c) James Jackson-South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
namespace ImageSharp
{
using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
/// <summary>
/// Represents a contiguous region of a pinned managed array.
/// The array is usually owned by a <see cref="PinnedBuffer{T}"/> instance.
/// </summary>
/// <remarks>
/// <see cref="BufferSpan{T}"/> is very similar to corefx System.Span&lt;T&gt;, 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 (<see cref="Array"/>) so we can pass it to API-s like <see cref="Marshal.Copy(byte[], int, IntPtr, int)"/>
/// - 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 <see cref="BufferSpan{T}"/> an unsafe type!
/// </remarks>
/// <typeparam name="T">The type of elements of the array</typeparam>
internal unsafe struct BufferSpan<T>
where T : struct
{
/// <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>
/// <param name="length">The length</param>
[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<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)
{
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<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)
{
GuardArrayAndPointer(array, pointerToArray);
this.Array = array;
this.Start = 0;
this.Length = array.Length;
this.PointerAtOffset = (IntPtr)pointerToArray;
}
/// <summary>
/// Gets the backing array
/// </summary>
public T[] Array { get; private set; }
/// <summary>
/// Gets the length of the <see cref="BufferSpan{T}"/>
/// </summary>
public int Length { get; private set; }
/// <summary>
/// Gets the start inside <see cref="Array"/>
/// </summary>
public int Start { get; private set; }
/// <summary>
/// Gets the start inside <see cref="Array"/> in bytes.
/// </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>
/// 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.
/// </summary>
/// <param name="source">The <see cref="BufferSpan{T}"/> to convert</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static explicit operator BufferSpan<byte>(BufferSpan<T> source)
{
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;
}
/// <summary>
/// Forms a slice out of the given BufferSpan, beginning at 'start'.
/// </summary>
/// <param name="start">TThe index at which to begin this slice.</param>
/// <returns>The offseted (sliced) BufferSpan</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public BufferSpan<T> Slice(int start)
{
DebugGuard.MustBeLessThan(start, this.Length, nameof(start));
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;
}
/// <summary>
/// Forms a slice out of the given BufferSpan, beginning at 'start'.
/// </summary>
/// <param name="start">The index at which to begin this slice.</param>
/// <param name="length">The desired length for the slice (exclusive).</param>
/// <returns>The sliced BufferSpan</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public BufferSpan<T> Slice(int start, int length)
{
DebugGuard.MustBeLessThanOrEqualTo(start, this.Length, nameof(start));
DebugGuard.MustBeLessThanOrEqualTo(length, this.Length - start, nameof(length));
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;
}
/// <summary>
/// Clears `count` elements beginning from the pointed position.
/// </summary>
/// <param name="count">The number of elements to clear</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear(int count)
{
if (count < 256)
{
Unsafe.InitBlock((void*)this.PointerAtOffset, 0, BufferSpan.USizeOf<T>(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!");
}
}
}

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

@ -32,11 +32,11 @@ namespace ImageSharp
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PinnedBuffer{T}"/> class. /// Initializes a new instance of the <see cref="PinnedBuffer{T}"/> class.
/// </summary> /// </summary>
/// <param name="count">The desired count of elements. (Minimum size for <see cref="Array"/>)</param> /// <param name="length">The desired count of elements. (Minimum size for <see cref="Array"/>)</param>
public PinnedBuffer(int count) public PinnedBuffer(int length)
{ {
this.Count = count; this.Length = length;
this.Array = PixelDataPool<T>.Rent(count); this.Array = PixelDataPool<T>.Rent(length);
this.isPoolingOwner = true; this.isPoolingOwner = true;
this.Pin(); this.Pin();
} }
@ -47,7 +47,7 @@ namespace ImageSharp
/// <param name="array">The array to pin.</param> /// <param name="array">The array to pin.</param>
public PinnedBuffer(T[] array) public PinnedBuffer(T[] array)
{ {
this.Count = array.Length; this.Length = array.Length;
this.Array = array; this.Array = array;
this.isPoolingOwner = false; this.isPoolingOwner = false;
this.Pin(); this.Pin();
@ -56,16 +56,16 @@ namespace ImageSharp
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="PinnedBuffer{T}"/> class. /// Initializes a new instance of the <see cref="PinnedBuffer{T}"/> class.
/// </summary> /// </summary>
/// <param name="count">The count of "relevant" elements in 'array'.</param> /// <param name="length">The count of "relevant" elements in 'array'.</param>
/// <param name="array">The array to pin.</param> /// <param name="array">The array to pin.</param>
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)); throw new ArgumentException("Can't initialize a PinnedBuffer with array.Length < count", nameof(array));
} }
this.Count = count; this.Length = length;
this.Array = array; this.Array = array;
this.isPoolingOwner = false; this.isPoolingOwner = false;
this.Pin(); this.Pin();
@ -85,9 +85,9 @@ namespace ImageSharp
public bool IsDisposedOrLostArrayOwnership { get; private set; } public bool IsDisposedOrLostArrayOwnership { get; private set; }
/// <summary> /// <summary>
/// Gets the count of "relevant" elements. Usually be smaller than 'Array.Length' when <see cref="Array"/> is pooled. /// Gets the count of "relevant" elements. It's usually smaller than 'Array.Length' when <see cref="Array"/> is pooled.
/// </summary> /// </summary>
public int Count { get; private set; } public int Length { get; private set; }
/// <summary> /// <summary>
/// Gets the backing pinned array. /// Gets the backing pinned array.
@ -100,11 +100,11 @@ namespace ImageSharp
public IntPtr Pointer { get; private set; } public IntPtr Pointer { get; private set; }
/// <summary> /// <summary>
/// Converts <see cref="PinnedBuffer{T}"/> to an <see cref="BufferPointer{T}"/>. /// Converts <see cref="PinnedBuffer{T}"/> to an <see cref="BufferSpan{T}"/>.
/// </summary> /// </summary>
/// <param name="buffer">The <see cref="PinnedBuffer{T}"/> to convert.</param> /// <param name="buffer">The <see cref="PinnedBuffer{T}"/> to convert.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator BufferPointer<T>(PinnedBuffer<T> buffer) public static implicit operator BufferSpan<T>(PinnedBuffer<T> buffer)
{ {
return buffer.Slice(); return buffer.Slice();
} }
@ -122,24 +122,24 @@ namespace ImageSharp
} }
/// <summary> /// <summary>
/// Gets a <see cref="BufferPointer{T}"/> to the beginning of the raw data of the buffer. /// Gets a <see cref="BufferSpan{T}"/> to the beginning of the raw data of the buffer.
/// </summary> /// </summary>
/// <returns>The <see cref="BufferPointer{T}"/></returns> /// <returns>The <see cref="BufferSpan{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe BufferPointer<T> Slice() public unsafe BufferSpan<T> Slice()
{ {
return new BufferPointer<T>(this.Array, (void*)this.Pointer); return new BufferSpan<T>(this.Array, (void*)this.Pointer);
} }
/// <summary> /// <summary>
/// Gets a <see cref="BufferPointer{T}"/> to an offseted position inside the buffer. /// Gets a <see cref="BufferSpan{T}"/> to an offseted position inside the buffer.
/// </summary> /// </summary>
/// <param name="offset">The offset</param> /// <param name="offset">The offset</param>
/// <returns>The <see cref="BufferPointer{T}"/></returns> /// <returns>The <see cref="BufferSpan{T}"/></returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe BufferPointer<T> Slice(int offset) public unsafe BufferSpan<T> Slice(int offset)
{ {
return new BufferPointer<T>(this.Array, (void*)this.Pointer, offset); return new BufferSpan<T>(this.Array, (void*)this.Pointer, offset);
} }
/// <summary> /// <summary>
@ -163,7 +163,7 @@ namespace ImageSharp
this.isPoolingOwner = false; this.isPoolingOwner = false;
this.Array = null; this.Array = null;
this.Count = 0; this.Length = 0;
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
@ -190,12 +190,12 @@ namespace ImageSharp
} }
/// <summary> /// <summary>
/// Clears the buffer, filling elements between 0 and <see cref="Count"/>-1 with default(T) /// Clears the buffer, filling elements between 0 and <see cref="Length"/>-1 with default(T)
/// </summary> /// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear() public void Clear()
{ {
this.Slice().Clear(this.Count); this.Slice().Clear(this.Length);
} }
/// <summary> /// <summary>

38
src/ImageSharp/Image/PixelAccessor{TColor}.cs

@ -239,12 +239,12 @@ namespace ImageSharp
} }
/// <summary> /// <summary>
/// Gets a <see cref="BufferPointer{TColor}"/> to the row 'y' beginning from the pixel at 'x'. /// Gets a <see cref="BufferSpan{T}"/> to the row 'y' beginning from the pixel at 'x'.
/// </summary> /// </summary>
/// <param name="x">The x coordinate</param> /// <param name="x">The x coordinate</param>
/// <param name="y">The y coordinate</param> /// <param name="y">The y coordinate</param>
/// <returns>The <see cref="BufferPointer{TColor}"/></returns> /// <returns>The <see cref="BufferSpan{T}"/></returns>
internal BufferPointer<TColor> GetRowPointer(int x, int y) internal BufferSpan<TColor> GetRowPointer(int x, int y)
{ {
return this.pixelBuffer.Slice((y * this.Width) + x); return this.pixelBuffer.Slice((y * this.Width) + x);
} }
@ -288,8 +288,8 @@ namespace ImageSharp
{ {
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
BufferPointer<byte> source = area.GetRowPointer(y); BufferSpan<byte> source = area.GetRowPointer(y);
BufferPointer<TColor> destination = this.GetRowPointer(targetX, targetY + y); BufferSpan<TColor> destination = this.GetRowPointer(targetX, targetY + y);
Operations.PackFromZyxBytes(source, destination, width); Operations.PackFromZyxBytes(source, destination, width);
} }
@ -308,8 +308,8 @@ namespace ImageSharp
{ {
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
BufferPointer<byte> source = area.GetRowPointer(y); BufferSpan<byte> source = area.GetRowPointer(y);
BufferPointer<TColor> destination = this.GetRowPointer(targetX, targetY + y); BufferSpan<TColor> destination = this.GetRowPointer(targetX, targetY + y);
Operations.PackFromZyxwBytes(source, destination, width); Operations.PackFromZyxwBytes(source, destination, width);
} }
@ -328,8 +328,8 @@ namespace ImageSharp
{ {
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
BufferPointer<byte> source = area.GetRowPointer(y); BufferSpan<byte> source = area.GetRowPointer(y);
BufferPointer<TColor> destination = this.GetRowPointer(targetX, targetY + y); BufferSpan<TColor> destination = this.GetRowPointer(targetX, targetY + y);
Operations.PackFromXyzBytes(source, destination, width); Operations.PackFromXyzBytes(source, destination, width);
} }
@ -348,8 +348,8 @@ namespace ImageSharp
{ {
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
BufferPointer<byte> source = area.GetRowPointer(y); BufferSpan<byte> source = area.GetRowPointer(y);
BufferPointer<TColor> destination = this.GetRowPointer(targetX, targetY + y); BufferSpan<TColor> destination = this.GetRowPointer(targetX, targetY + y);
Operations.PackFromXyzwBytes(source, destination, width); Operations.PackFromXyzwBytes(source, destination, width);
} }
} }
@ -367,8 +367,8 @@ namespace ImageSharp
{ {
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
BufferPointer<TColor> source = this.GetRowPointer(sourceX, sourceY + y); BufferSpan<TColor> source = this.GetRowPointer(sourceX, sourceY + y);
BufferPointer<byte> destination = area.GetRowPointer(y); BufferSpan<byte> destination = area.GetRowPointer(y);
Operations.ToZyxBytes(source, destination, width); Operations.ToZyxBytes(source, destination, width);
} }
} }
@ -386,8 +386,8 @@ namespace ImageSharp
{ {
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
BufferPointer<TColor> source = this.GetRowPointer(sourceX, sourceY + y); BufferSpan<TColor> source = this.GetRowPointer(sourceX, sourceY + y);
BufferPointer<byte> destination = area.GetRowPointer(y); BufferSpan<byte> destination = area.GetRowPointer(y);
Operations.ToZyxwBytes(source, destination, width); Operations.ToZyxwBytes(source, destination, width);
} }
} }
@ -405,8 +405,8 @@ namespace ImageSharp
{ {
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
BufferPointer<TColor> source = this.GetRowPointer(sourceX, sourceY + y); BufferSpan<TColor> source = this.GetRowPointer(sourceX, sourceY + y);
BufferPointer<byte> destination = area.GetRowPointer(y); BufferSpan<byte> destination = area.GetRowPointer(y);
Operations.ToXyzBytes(source, destination, width); Operations.ToXyzBytes(source, destination, width);
} }
} }
@ -424,8 +424,8 @@ namespace ImageSharp
{ {
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
BufferPointer<TColor> source = this.GetRowPointer(sourceX, sourceY + y); BufferSpan<TColor> source = this.GetRowPointer(sourceX, sourceY + y);
BufferPointer<byte> destination = area.GetRowPointer(y); BufferSpan<byte> destination = area.GetRowPointer(y);
Operations.ToXyzwBytes(source, destination, width); Operations.ToXyzwBytes(source, destination, width);
} }
} }

6
src/ImageSharp/Image/PixelArea{TColor}.cs

@ -202,11 +202,11 @@ namespace ImageSharp
} }
/// <summary> /// <summary>
/// Gets a <see cref="BufferPointer{Byte}"/> to the row y. /// Gets a <see cref="BufferSpan{T}"/> to the row y.
/// </summary> /// </summary>
/// <param name="y">The y coordinate</param> /// <param name="y">The y coordinate</param>
/// <returns>The <see cref="BufferPointer{Byte}"/></returns> /// <returns>The <see cref="BufferSpan{T}"/></returns>
internal BufferPointer<byte> GetRowPointer(int y) internal BufferSpan<byte> GetRowPointer(int y)
{ {
return this.byteBuffer.Slice(y * this.RowStride); return this.byteBuffer.Slice(y * this.RowStride);
} }

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

@ -314,8 +314,8 @@ namespace ImageSharp.Tests.Colors
public PinnedBuffer<TDest> ActualDestBuffer { get; } public PinnedBuffer<TDest> ActualDestBuffer { get; }
public PinnedBuffer<TDest> ExpectedDestBuffer { get; } public PinnedBuffer<TDest> ExpectedDestBuffer { get; }
public BufferPointer<TSource> Source => this.SourceBuffer.Slice(); public BufferSpan<TSource> Source => this.SourceBuffer.Slice();
public BufferPointer<TDest> ActualDest => this.ActualDestBuffer.Slice(); public BufferSpan<TDest> ActualDest => this.ActualDestBuffer.Slice();
public TestBuffers(TSource[] source, TDest[] expectedDest) public TestBuffers(TSource[] source, TDest[] expectedDest)
{ {
@ -335,7 +335,7 @@ namespace ImageSharp.Tests.Colors
public void Verify() public void Verify()
{ {
int count = this.ExpectedDestBuffer.Count; int count = this.ExpectedDestBuffer.Length;
if (typeof(TDest) == typeof(Vector4)) if (typeof(TDest) == typeof(Vector4))
{ {
@ -364,7 +364,7 @@ namespace ImageSharp.Tests.Colors
internal static void TestOperation<TSource, TDest>( internal static void TestOperation<TSource, TDest>(
TSource[] source, TSource[] source,
TDest[] expected, TDest[] expected,
Action<BufferPointer<TSource>, BufferPointer<TDest>> action) Action<BufferSpan<TSource>, BufferSpan<TDest>> action)
where TSource : struct where TSource : struct
where TDest : struct where TDest : struct
{ {

184
tests/ImageSharp.Tests/Common/BufferPointerTests.cs → tests/ImageSharp.Tests/Common/BufferSpanTests.cs

@ -7,7 +7,7 @@ namespace ImageSharp.Tests.Common
using Xunit; using Xunit;
public unsafe class BufferPointerTests public unsafe class BufferSpanTests
{ {
public struct Foo public struct Foo
{ {
@ -70,68 +70,122 @@ namespace ImageSharp.Tests.Common
using (PinnedBuffer<Foo> colorBuf = new PinnedBuffer<Foo>(fooz)) using (PinnedBuffer<Foo> colorBuf = new PinnedBuffer<Foo>(fooz))
{ {
BufferPointer<Foo> orig = colorBuf.Slice(1); BufferSpan<Foo> orig = colorBuf.Slice(1);
BufferPointer<byte> asBytes = (BufferPointer < byte > )orig; BufferSpan<byte> asBytes = (BufferSpan < byte > )orig;
Assert.Equal(asBytes.Offset, sizeof(Foo)); Assert.Equal(asBytes.Start, sizeof(Foo));
Assert.Equal(orig.PointerAtOffset, asBytes.PointerAtOffset); Assert.Equal(orig.PointerAtOffset, asBytes.PointerAtOffset);
} }
} }
[Fact] public class Construct
public void ConstructWithoutOffset()
{ {
Foo[] array = Foo.CreateArray(3); [Fact]
fixed (Foo* p = array) public void Basic()
{ {
// Act: Foo[] array = Foo.CreateArray(3);
BufferPointer<Foo> ap = new BufferPointer<Foo>(array, p); fixed (Foo* p = array)
{
// Act:
BufferSpan<Foo> span = new BufferSpan<Foo>(array, p);
// Assert: // Assert:
Assert.Equal(array, ap.Array); Assert.Equal(array, span.Array);
Assert.Equal((IntPtr)p, ap.PointerAtOffset); Assert.Equal((IntPtr)p, span.PointerAtOffset);
Assert.Equal(3, span.Length);
}
} }
}
[Fact] [Fact]
public void ConstructWithOffset() public void WithStart()
{
Foo[] array = Foo.CreateArray(3);
int offset = 2;
fixed (Foo* p = array)
{ {
// Act: Foo[] array = Foo.CreateArray(4);
BufferPointer<Foo> ap = new BufferPointer<Foo>(array, p, offset); 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);
}
}
// Assert: [Fact]
Assert.Equal(array, ap.Array); public void WithStartAndLength()
Assert.Equal(offset, ap.Offset); {
Assert.Equal((IntPtr)(p+offset), ap.PointerAtOffset); 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);
}
} }
} }
[Fact] public class Slice
public void Slice()
{ {
Foo[] array = Foo.CreateArray(5); [Fact]
int offset0 = 2; public void StartOnly()
int offset1 = 2;
int totalOffset = offset0 + offset1;
fixed (Foo* p = array)
{ {
BufferPointer<Foo> ap = new BufferPointer<Foo>(array, p, offset0); Foo[] array = Foo.CreateArray(5);
int start0 = 2;
int start1 = 2;
int totalOffset = start0 + start1;
// Act: fixed (Foo* p = array)
ap = ap.Slice(offset1); {
BufferSpan<Foo> span = new BufferSpan<Foo>(array, p, start0);
// Act:
span = span.Slice(start1);
// Assert: // Assert:
Assert.Equal(array, ap.Array); Assert.Equal(array, span.Array);
Assert.Equal(totalOffset, ap.Offset); Assert.Equal(totalOffset, span.Start);
Assert.Equal((IntPtr)(p + totalOffset), ap.PointerAtOffset); 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<Foo> span = new BufferSpan<Foo>(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] [Theory]
[InlineData(4)] [InlineData(4)]
[InlineData(1500)] [InlineData(1500)]
@ -142,7 +196,7 @@ namespace ImageSharp.Tests.Common
int offset = 2; int offset = 2;
fixed (Foo* p = array) fixed (Foo* p = array)
{ {
BufferPointer<Foo> ap = new BufferPointer<Foo>(array, p, offset); BufferSpan<Foo> ap = new BufferSpan<Foo>(array, p, offset);
// Act: // Act:
ap.Clear(count); ap.Clear(count);
@ -194,10 +248,10 @@ namespace ImageSharp.Tests.Common
fixed (Foo* pSource = source) fixed (Foo* pSource = source)
fixed (Foo* pDest = dest) fixed (Foo* pDest = dest)
{ {
BufferPointer<Foo> apSource = new BufferPointer<Foo>(source, pSource, 1); BufferSpan<Foo> apSource = new BufferSpan<Foo>(source, pSource, 1);
BufferPointer<Foo> apDest = new BufferPointer<Foo>(dest, pDest, 1); BufferSpan<Foo> apDest = new BufferSpan<Foo>(dest, pDest, 1);
BufferPointer.Copy(apSource, apDest, count-1); BufferSpan.Copy(apSource, apDest, count-1);
} }
AssertNotDefault(source, 1); AssertNotDefault(source, 1);
@ -221,10 +275,10 @@ namespace ImageSharp.Tests.Common
fixed (AlignedFoo* pSource = source) fixed (AlignedFoo* pSource = source)
fixed (AlignedFoo* pDest = dest) fixed (AlignedFoo* pDest = dest)
{ {
BufferPointer<AlignedFoo> apSource = new BufferPointer<AlignedFoo>(source, pSource, 1); BufferSpan<AlignedFoo> apSource = new BufferSpan<AlignedFoo>(source, pSource, 1);
BufferPointer<AlignedFoo> apDest = new BufferPointer<AlignedFoo>(dest, pDest, 1); BufferSpan<AlignedFoo> apDest = new BufferSpan<AlignedFoo>(dest, pDest, 1);
BufferPointer.Copy(apSource, apDest, count - 1); BufferSpan.Copy(apSource, apDest, count - 1);
} }
AssertNotDefault(source, 1); AssertNotDefault(source, 1);
@ -248,10 +302,10 @@ namespace ImageSharp.Tests.Common
fixed (int* pSource = source) fixed (int* pSource = source)
fixed (int* pDest = dest) fixed (int* pDest = dest)
{ {
BufferPointer<int> apSource = new BufferPointer<int>(source, pSource, 1); BufferSpan<int> apSource = new BufferSpan<int>(source, pSource, 1);
BufferPointer<int> apDest = new BufferPointer<int>(dest, pDest, 1); BufferSpan<int> apDest = new BufferSpan<int>(dest, pDest, 1);
BufferPointer.Copy(apSource, apDest, count -1); BufferSpan.Copy(apSource, apDest, count -1);
} }
AssertNotDefault(source, 1); AssertNotDefault(source, 1);
@ -276,10 +330,10 @@ namespace ImageSharp.Tests.Common
fixed (Foo* pSource = source) fixed (Foo* pSource = source)
fixed (byte* pDest = dest) fixed (byte* pDest = dest)
{ {
BufferPointer<Foo> apSource = new BufferPointer<Foo>(source, pSource, 1); BufferSpan<Foo> apSource = new BufferSpan<Foo>(source, pSource, 1);
BufferPointer<byte> apDest = new BufferPointer<byte>(dest, pDest, sizeof(Foo)); BufferSpan<byte> apDest = new BufferSpan<byte>(dest, pDest, sizeof(Foo));
BufferPointer.Copy(apSource, apDest, count - 1); BufferSpan.Copy(apSource, apDest, count - 1);
} }
AssertNotDefault(source, 1); AssertNotDefault(source, 1);
@ -303,10 +357,10 @@ namespace ImageSharp.Tests.Common
fixed (AlignedFoo* pSource = source) fixed (AlignedFoo* pSource = source)
fixed (byte* pDest = dest) fixed (byte* pDest = dest)
{ {
BufferPointer<AlignedFoo> apSource = new BufferPointer<AlignedFoo>(source, pSource, 1); BufferSpan<AlignedFoo> apSource = new BufferSpan<AlignedFoo>(source, pSource, 1);
BufferPointer<byte> apDest = new BufferPointer<byte>(dest, pDest, sizeof(AlignedFoo)); BufferSpan<byte> apDest = new BufferSpan<byte>(dest, pDest, sizeof(AlignedFoo));
BufferPointer.Copy(apSource, apDest, count - 1); BufferSpan.Copy(apSource, apDest, count - 1);
} }
AssertNotDefault(source, 1); AssertNotDefault(source, 1);
@ -330,10 +384,10 @@ namespace ImageSharp.Tests.Common
fixed (int* pSource = source) fixed (int* pSource = source)
fixed (byte* pDest = dest) fixed (byte* pDest = dest)
{ {
BufferPointer<int> apSource = new BufferPointer<int>(source, pSource); BufferSpan<int> apSource = new BufferSpan<int>(source, pSource);
BufferPointer<byte> apDest = new BufferPointer<byte>(dest, pDest); BufferSpan<byte> apDest = new BufferSpan<byte>(dest, pDest);
BufferPointer.Copy(apSource, apDest, count); BufferSpan.Copy(apSource, apDest, count);
} }
AssertNotDefault(source, 1); AssertNotDefault(source, 1);
@ -355,10 +409,10 @@ namespace ImageSharp.Tests.Common
fixed(byte* pSource = source) fixed(byte* pSource = source)
fixed (Foo* pDest = dest) fixed (Foo* pDest = dest)
{ {
BufferPointer<byte> apSource = new BufferPointer<byte>(source, pSource); BufferSpan<byte> apSource = new BufferSpan<byte>(source, pSource);
BufferPointer<Foo> apDest = new BufferPointer<Foo>(dest, pDest); BufferSpan<Foo> apDest = new BufferSpan<Foo>(dest, pDest);
BufferPointer.Copy(apSource, apDest, count); BufferSpan.Copy(apSource, apDest, count);
} }
AssertNotDefault(source, sizeof(Foo) + 1); AssertNotDefault(source, sizeof(Foo) + 1);
@ -378,11 +432,11 @@ namespace ImageSharp.Tests.Common
using (PinnedBuffer<Color> colorBuf = new PinnedBuffer<Color>(colors)) 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))
{ {
BufferPointer.Copy<Color>(colorBuf, byteBuf, colorBuf.Count); BufferSpan.Copy<Color>(colorBuf, byteBuf, colorBuf.Length);
byte[] a = byteBuf.Array; 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]); Assert.Equal((byte)i, a[i]);
} }

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

@ -25,7 +25,7 @@
{ {
Assert.False(buffer.IsDisposedOrLostArrayOwnership); Assert.False(buffer.IsDisposedOrLostArrayOwnership);
Assert.NotNull(buffer.Array); Assert.NotNull(buffer.Array);
Assert.Equal(count, buffer.Count); Assert.Equal(count, buffer.Length);
Assert.True(buffer.Array.Length >= count); Assert.True(buffer.Array.Length >= count);
VerifyPointer(buffer); VerifyPointer(buffer);
@ -42,7 +42,7 @@
{ {
Assert.False(buffer.IsDisposedOrLostArrayOwnership); Assert.False(buffer.IsDisposedOrLostArrayOwnership);
Assert.Equal(array, buffer.Array); Assert.Equal(array, buffer.Array);
Assert.Equal(count, buffer.Count); Assert.Equal(count, buffer.Length);
VerifyPointer(buffer); VerifyPointer(buffer);
} }
@ -71,7 +71,7 @@
{ {
using (PinnedBuffer<int> buffer = PinnedBuffer<int>.CreateClean(42)) using (PinnedBuffer<int> buffer = PinnedBuffer<int>.CreateClean(42))
{ {
for (int j = 0; j < buffer.Count; j++) for (int j = 0; j < buffer.Length; j++)
{ {
Assert.Equal(0, buffer.Array[j]); Assert.Equal(0, buffer.Array[j]);
buffer.Array[j] = 666; buffer.Array[j] = 666;
@ -96,10 +96,10 @@
using (PinnedBuffer<Foo> buffer = new PinnedBuffer<Foo>(a)) using (PinnedBuffer<Foo> buffer = new PinnedBuffer<Foo>(a))
{ {
BufferPointer<Foo> arrayPtr = buffer.Slice(); BufferSpan<Foo> arrayPtr = buffer.Slice();
Assert.Equal(a, arrayPtr.Array); Assert.Equal(a, arrayPtr.Array);
Assert.Equal(0, arrayPtr.Offset); Assert.Equal(0, arrayPtr.Start);
Assert.Equal(buffer.Pointer, arrayPtr.PointerAtOffset); Assert.Equal(buffer.Pointer, arrayPtr.PointerAtOffset);
} }
} }

Loading…
Cancel
Save