diff --git a/.gitignore b/.gitignore
index 61c78d0c8..fb8af2320 100644
--- a/.gitignore
+++ b/.gitignore
@@ -215,4 +215,5 @@ artifacts/
*.csproj.bak
#CodeCoverage
-**/CodeCoverage/*
\ No newline at end of file
+**/CodeCoverage/*
+docs/
diff --git a/codecov.yml b/codecov.yml
new file mode 100644
index 000000000..ae6dd5f6b
--- /dev/null
+++ b/codecov.yml
@@ -0,0 +1,4 @@
+ignore:
+ "src/ImageSharp/Common/Helpers/DebugGuard.cs"
+
+
\ No newline at end of file
diff --git a/config.wyam b/config.wyam
new file mode 100644
index 000000000..3a4b64c54
--- /dev/null
+++ b/config.wyam
@@ -0,0 +1,4 @@
+#recipe Docs
+Settings[Keys.Host] = "imagesharp.org";
+Settings[Keys.Title] = "Image Sharp";
+FileSystem.OutputPath = "./docs";
\ No newline at end of file
diff --git a/input/about.md b/input/about.md
new file mode 100644
index 000000000..42739928a
--- /dev/null
+++ b/input/about.md
@@ -0,0 +1,3 @@
+Title: About This Project
+---
+This project is awesome!
\ No newline at end of file
diff --git a/src/ImageSharp/Colors/Color.BulkOperations.cs b/src/ImageSharp/Colors/Color.BulkOperations.cs
new file mode 100644
index 000000000..5c040e04c
--- /dev/null
+++ b/src/ImageSharp/Colors/Color.BulkOperations.cs
@@ -0,0 +1,249 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using System;
+ using System.Numerics;
+ using System.Runtime.CompilerServices;
+ using System.Runtime.InteropServices;
+
+ ///
+ /// Conains the definition of
+ ///
+ public partial struct Color
+ {
+ ///
+ /// implementation optimized for .
+ ///
+ internal class BulkOperations : BulkPixelOperations
+ {
+ ///
+ /// SIMD optimized bulk implementation of
+ /// that works only with `count` divisible by .
+ ///
+ /// The to the source colors.
+ /// The to the dstination vectors.
+ /// The number of pixels to convert.
+ ///
+ /// Implementation adapted from:
+ ///
+ /// http://stackoverflow.com/a/5362789
+ ///
+ /// TODO: We can replace this implementation in the future using new Vector API-s:
+ ///
+ /// https://github.com/dotnet/corefx/issues/15957
+ ///
+ ///
+ internal static unsafe void ToVector4SimdAligned(
+ BufferPointer sourceColors,
+ BufferPointer destVectors,
+ int count)
+ {
+ int vecSize = Vector.Count;
+
+ DebugGuard.IsTrue(
+ count % vecSize == 0,
+ nameof(count),
+ "Argument 'count' should divisible by Vector.Count!");
+
+ Vector bVec = new Vector(256.0f / 255.0f);
+ Vector magicFloat = new Vector(32768.0f);
+ Vector magicInt = new Vector(1191182336); // reinterpreded value of 32768.0f
+ Vector mask = new Vector(255);
+
+ int unpackedRawCount = count * 4;
+
+ uint* src = (uint*)sourceColors.PointerAtOffset;
+ uint* srcEnd = src + count;
+
+ using (PinnedBuffer tempBuf = new PinnedBuffer(
+ unpackedRawCount + Vector.Count))
+ {
+ uint* tPtr = (uint*)tempBuf.Pointer;
+ uint[] temp = tempBuf.Array;
+ float[] fTemp = Unsafe.As(temp);
+ UnpackedRGBA* dst = (UnpackedRGBA*)tPtr;
+
+ for (; src < srcEnd; src++, dst++)
+ {
+ // This call is the bottleneck now:
+ dst->Load(*src);
+ }
+
+ for (int i = 0; i < unpackedRawCount; i += vecSize)
+ {
+ Vector vi = new Vector(temp, i);
+
+ vi &= mask;
+ vi |= magicInt;
+
+ Vector vf = Vector.AsVectorSingle(vi);
+ vf = (vf - magicFloat) * bVec;
+ vf.CopyTo(fTemp, i);
+ }
+
+ BufferPointer.Copy(tempBuf, (BufferPointer)destVectors, unpackedRawCount);
+ }
+ }
+
+ ///
+ internal override void ToVector4(BufferPointer sourceColors, BufferPointer destVectors, int count)
+ {
+ if (count < 256)
+ {
+ // Doesn't worth to bother with SIMD:
+ base.ToVector4(sourceColors, destVectors, count);
+ return;
+ }
+
+ int remainder = count % Vector.Count;
+
+ int alignedCount = count - remainder;
+
+ if (alignedCount > 0)
+ {
+ ToVector4SimdAligned(sourceColors, destVectors, alignedCount);
+ }
+
+ if (remainder > 0)
+ {
+ sourceColors = sourceColors.Slice(alignedCount);
+ destVectors = destVectors.Slice(alignedCount);
+ base.ToVector4(sourceColors, destVectors, remainder);
+ }
+ }
+
+ ///
+ internal override unsafe void PackFromXyzBytes(BufferPointer sourceBytes, BufferPointer destColors, int count)
+ {
+ byte* source = (byte*)sourceBytes;
+ byte* destination = (byte*)destColors;
+
+ for (int x = 0; x < count; x++)
+ {
+ Unsafe.Write(destination, (uint)(*source << 0 | *(source + 1) << 8 | *(source + 2) << 16 | 255 << 24));
+
+ source += 3;
+ destination += 4;
+ }
+ }
+
+ ///
+ internal override unsafe void ToXyzBytes(BufferPointer sourceColors, BufferPointer destBytes, int count)
+ {
+ byte* source = (byte*)sourceColors;
+ byte* destination = (byte*)destBytes;
+
+ for (int x = 0; x < count; x++)
+ {
+ *destination = *(source + 0);
+ *(destination + 1) = *(source + 1);
+ *(destination + 2) = *(source + 2);
+
+ source += 4;
+ destination += 3;
+ }
+ }
+
+ ///
+ internal override void PackFromXyzwBytes(BufferPointer sourceBytes, BufferPointer destColors, int count)
+ {
+ BufferPointer.Copy(sourceBytes, destColors, count);
+ }
+
+ ///
+ internal override void ToXyzwBytes(BufferPointer sourceColors, BufferPointer destBytes, int count)
+ {
+ BufferPointer.Copy(sourceColors, destBytes, count);
+ }
+
+ ///
+ internal override unsafe void PackFromZyxBytes(BufferPointer sourceBytes, BufferPointer destColors, int count)
+ {
+ byte* source = (byte*)sourceBytes;
+ byte* destination = (byte*)destColors;
+
+ for (int x = 0; x < count; x++)
+ {
+ Unsafe.Write(destination, (uint)(*(source + 2) << 0 | *(source + 1) << 8 | *source << 16 | 255 << 24));
+
+ source += 3;
+ destination += 4;
+ }
+ }
+
+ ///
+ internal override unsafe void ToZyxBytes(BufferPointer sourceColors, BufferPointer destBytes, int count)
+ {
+ byte* source = (byte*)sourceColors;
+ byte* destination = (byte*)destBytes;
+
+ for (int x = 0; x < count; x++)
+ {
+ *destination = *(source + 2);
+ *(destination + 1) = *(source + 1);
+ *(destination + 2) = *(source + 0);
+
+ source += 4;
+ destination += 3;
+ }
+ }
+
+ ///
+ internal override unsafe void PackFromZyxwBytes(BufferPointer sourceBytes, BufferPointer destColors, int count)
+ {
+ byte* source = (byte*)sourceBytes;
+ byte* destination = (byte*)destColors;
+
+ for (int x = 0; x < count; x++)
+ {
+ Unsafe.Write(destination, (uint)(*(source + 2) << 0 | *(source + 1) << 8 | *source << 16 | *(source + 3) << 24));
+
+ source += 4;
+ destination += 4;
+ }
+ }
+
+ ///
+ internal override unsafe void ToZyxwBytes(BufferPointer sourceColors, BufferPointer destBytes, int count)
+ {
+ byte* source = (byte*)sourceColors;
+ byte* destination = (byte*)destBytes;
+
+ for (int x = 0; x < count; x++)
+ {
+ *destination = *(source + 2);
+ *(destination + 1) = *(source + 1);
+ *(destination + 2) = *(source + 0);
+ *(destination + 3) = *(source + 3);
+
+ source += 4;
+ destination += 4;
+ }
+ }
+
+ ///
+ /// Value type to store -s unpacked into multiple -s.
+ ///
+ private struct UnpackedRGBA
+ {
+ private uint r;
+ private uint g;
+ private uint b;
+ private uint a;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Load(uint p)
+ {
+ this.r = p;
+ this.g = p >> Color.GreenShift;
+ this.b = p >> Color.BlueShift;
+ this.a = p >> Color.AlphaShift;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Colors/Color.cs b/src/ImageSharp/Colors/Color.cs
index 1ad6f9a64..597730937 100644
--- a/src/ImageSharp/Colors/Color.cs
+++ b/src/ImageSharp/Colors/Color.cs
@@ -246,7 +246,7 @@ namespace ImageSharp
}
///
- public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations();
+ public BulkPixelOperations CreateBulkOperations() => new Color.BulkOperations();
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/ImageSharp/Common/Helpers/DebugGuard.cs b/src/ImageSharp/Common/Helpers/DebugGuard.cs
index 0ca6f0912..c1fa46191 100644
--- a/src/ImageSharp/Common/Helpers/DebugGuard.cs
+++ b/src/ImageSharp/Common/Helpers/DebugGuard.cs
@@ -29,5 +29,135 @@ namespace ImageSharp
throw new ArgumentNullException(parameterName);
}
}
+
+ ///
+ /// Verifies that the specified value is less than a maximum value
+ /// and throws an exception if it is not.
+ ///
+ /// The target value, which should be validated.
+ /// The maximum value.
+ /// The name of the parameter that is to be checked.
+ /// The type of the value.
+ ///
+ /// is greater than the maximum value.
+ ///
+ [Conditional("DEBUG")]
+ public static void MustBeLessThan(TValue value, TValue max, string parameterName)
+ where TValue : IComparable
+ {
+ if (value.CompareTo(max) >= 0)
+ {
+ throw new ArgumentOutOfRangeException(parameterName, $"Value must be less than {max}.");
+ }
+ }
+
+ ///
+ /// Verifies that the specified value is less than or equal to a maximum value
+ /// and throws an exception if it is not.
+ ///
+ /// The target value, which should be validated.
+ /// The maximum value.
+ /// The name of the parameter that is to be checked.
+ /// The type of the value.
+ ///
+ /// is greater than the maximum value.
+ ///
+ [Conditional("DEBUG")]
+ public static void MustBeLessThanOrEqualTo(TValue value, TValue max, string parameterName)
+ where TValue : IComparable
+ {
+ if (value.CompareTo(max) > 0)
+ {
+ throw new ArgumentOutOfRangeException(parameterName, $"Value must be less than or equal to {max}.");
+ }
+ }
+
+ ///
+ /// Verifies that the specified value is greater than a minimum value
+ /// and throws an exception if it is not.
+ ///
+ /// The target value, which should be validated.
+ /// The minimum value.
+ /// The name of the parameter that is to be checked.
+ /// The type of the value.
+ ///
+ /// is less than the minimum value.
+ ///
+ [Conditional("DEBUG")]
+ public static void MustBeGreaterThan(TValue value, TValue min, string parameterName)
+ where TValue : IComparable
+ {
+ if (value.CompareTo(min) <= 0)
+ {
+ throw new ArgumentOutOfRangeException(
+ parameterName,
+ $"Value must be greater than {min}.");
+ }
+ }
+
+ ///
+ /// Verifies that the specified value is greater than or equal to a minimum value
+ /// and throws an exception if it is not.
+ ///
+ /// The target value, which should be validated.
+ /// The minimum value.
+ /// The name of the parameter that is to be checked.
+ /// The type of the value.
+ ///
+ /// is less than the minimum value.
+ ///
+ [Conditional("DEBUG")]
+ public static void MustBeGreaterThanOrEqualTo(TValue value, TValue min, string parameterName)
+ where TValue : IComparable
+ {
+ if (value.CompareTo(min) < 0)
+ {
+ throw new ArgumentOutOfRangeException(parameterName, $"Value must be greater than or equal to {min}.");
+ }
+ }
+
+ ///
+ /// Verifies, that the method parameter with specified target value is true
+ /// and throws an exception if it is found to be so.
+ ///
+ ///
+ /// The target value, which cannot be false.
+ ///
+ ///
+ /// The name of the parameter that is to be checked.
+ ///
+ ///
+ /// The error message, if any to add to the exception.
+ ///
+ ///
+ /// is false
+ ///
+ [Conditional("DEBUG")]
+ public static void IsTrue(bool target, string parameterName, string message)
+ {
+ if (!target)
+ {
+ throw new ArgumentException(message, parameterName);
+ }
+ }
+
+ ///
+ /// Verifies, that the method parameter with specified target value is false
+ /// and throws an exception if it is found to be so.
+ ///
+ /// The target value, which cannot be true.
+ /// The name of the parameter that is to be checked.
+ /// The error message, if any to add to the exception.
+ ///
+ /// is true
+ ///
+ [Conditional("DEBUG")]
+ public static void IsFalse(bool target, string parameterName, string message)
+ {
+ if (target)
+ {
+ throw new ArgumentException(message, parameterName);
+ }
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Common/Memory/BufferPointer.cs b/src/ImageSharp/Common/Memory/BufferPointer.cs
index c80e22e21..523889611 100644
--- a/src/ImageSharp/Common/Memory/BufferPointer.cs
+++ b/src/ImageSharp/Common/Memory/BufferPointer.cs
@@ -5,7 +5,10 @@
namespace ImageSharp
{
+ using System;
+ using System.Numerics;
using System.Runtime.CompilerServices;
+ using System.Runtime.InteropServices;
///
/// Utility methods for
@@ -13,17 +16,9 @@ namespace ImageSharp
internal static class BufferPointer
{
///
- /// Gets a to the beginning of the raw data in 'buffer'.
+ /// It's worth to use Marshal.Copy() or Buffer.BlockCopy() over this size.
///
- /// The element type
- /// The input
- /// The
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static unsafe BufferPointer Slice(this PinnedBuffer buffer)
- where T : struct
- {
- return new BufferPointer(buffer.Array, (void*)buffer.Pointer);
- }
+ private const int ByteCountThreshold = 1024;
///
/// Copy 'count' number of elements of the same type from 'source' to 'dest'
@@ -33,10 +28,10 @@ namespace ImageSharp
/// The destination .
/// The number of elements to copy
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static unsafe void Copy(BufferPointer source, BufferPointer destination, int count)
+ public static void Copy(BufferPointer source, BufferPointer destination, int count)
where T : struct
{
- Unsafe.CopyBlock((void*)source.PointerAtOffset, (void*)destination.PointerAtOffset, USizeOf(count));
+ CopyImpl(source, destination, count);
}
///
@@ -47,10 +42,10 @@ namespace ImageSharp
/// The destination buffer.
/// The number of elements to copy from 'source'
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static unsafe void Copy(BufferPointer source, BufferPointer destination, int countInSource)
+ public static void Copy(BufferPointer source, BufferPointer destination, int countInSource)
where T : struct
{
- Unsafe.CopyBlock((void*)source.PointerAtOffset, (void*)destination.PointerAtOffset, USizeOf(countInSource));
+ CopyImpl(source, destination, countInSource);
}
///
@@ -64,7 +59,16 @@ namespace ImageSharp
public static unsafe void Copy(BufferPointer source, BufferPointer destination, int countInDest)
where T : struct
{
- Unsafe.CopyBlock((void*)source.PointerAtOffset, (void*)destination.PointerAtOffset, USizeOf(countInDest));
+ int byteCount = SizeOf(countInDest);
+
+ if (byteCount > (int)ByteCountThreshold)
+ {
+ Marshal.Copy(source.Array, source.Offset, destination.PointerAtOffset, byteCount);
+ }
+ else
+ {
+ Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, (uint)byteCount);
+ }
}
///
@@ -87,5 +91,39 @@ namespace ImageSharp
public static uint USizeOf(int count)
where T : struct
=> (uint)SizeOf(count);
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static unsafe void CopyImpl(BufferPointer source, BufferPointer destination, int count)
+ where T : struct
+ where TDest : struct
+ {
+ int byteCount = SizeOf(count);
+
+ if (byteCount > ByteCountThreshold)
+ {
+ if (Unsafe.SizeOf() == sizeof(long))
+ {
+ Marshal.Copy(Unsafe.As(source.Array), source.Offset, destination.PointerAtOffset, count);
+ return;
+ }
+ else if (Unsafe.SizeOf() == sizeof(int))
+ {
+ Marshal.Copy(Unsafe.As(source.Array), source.Offset, destination.PointerAtOffset, count);
+ return;
+ }
+ else if (Unsafe.SizeOf() == sizeof(short))
+ {
+ Marshal.Copy(Unsafe.As(source.Array), source.Offset, destination.PointerAtOffset, count);
+ return;
+ }
+ else if (Unsafe.SizeOf() == sizeof(byte))
+ {
+ Marshal.Copy(Unsafe.As(source.Array), source.Offset, destination.PointerAtOffset, count);
+ return;
+ }
+ }
+
+ Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, (uint)byteCount);
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Common/Memory/BufferPointer{T}.cs b/src/ImageSharp/Common/Memory/BufferPointer{T}.cs
index fff4e513e..441f6b8ce 100644
--- a/src/ImageSharp/Common/Memory/BufferPointer{T}.cs
+++ b/src/ImageSharp/Common/Memory/BufferPointer{T}.cs
@@ -63,6 +63,11 @@ namespace ImageSharp
///
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
///
@@ -79,7 +84,7 @@ namespace ImageSharp
}
///
- /// Convertes instance to a raw 'byte*' pointer
+ /// Converts instance to a raw 'byte*' pointer
///
/// The to convert
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -88,6 +93,21 @@ namespace ImageSharp
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'.
///
@@ -102,5 +122,22 @@ namespace ImageSharp
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/PinnedBuffer{T}.cs b/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs
index 04217a012..2d3d44dda 100644
--- a/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs
+++ b/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs
@@ -7,6 +7,7 @@ namespace ImageSharp
{
using System;
using System.Buffers;
+ using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
///
@@ -23,7 +24,8 @@ namespace ImageSharp
private GCHandle handle;
///
- /// A value indicating whether this instance should return the array to the pool.
+ /// A value indicating wheter should be returned to
+ /// when disposing this instance.
///
private bool isPoolingOwner;
@@ -47,6 +49,7 @@ namespace ImageSharp
{
this.Count = array.Length;
this.Array = array;
+ this.isPoolingOwner = false;
this.Pin();
}
@@ -64,6 +67,7 @@ namespace ImageSharp
this.Count = count;
this.Array = array;
+ this.isPoolingOwner = false;
this.Pin();
}
@@ -95,9 +99,41 @@ namespace ImageSharp
///
public IntPtr Pointer { get; private set; }
+ ///
+ /// Converts to an .
+ ///
+ /// The to convert.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static implicit operator BufferPointer(PinnedBuffer buffer)
+ {
+ return buffer.Slice();
+ }
+
+ ///
+ /// Gets a to the beginning of the raw data of the buffer.
+ ///
+ /// The
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public unsafe BufferPointer Slice()
+ {
+ return new BufferPointer(this.Array, (void*)this.Pointer);
+ }
+
+ ///
+ /// Gets a to an offseted position inside the buffer.
+ ///
+ /// The offset
+ /// The
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public unsafe BufferPointer Slice(int offset)
+ {
+ return new BufferPointer(this.Array, (void*)this.Pointer, offset);
+ }
+
///
/// Disposes the instance by unpinning the array, and returning the pooled buffer when necessary.
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Dispose()
{
if (this.IsDisposedOrLostArrayOwnership)
@@ -113,6 +149,7 @@ namespace ImageSharp
PixelDataPool.Return(this.Array);
}
+ this.isPoolingOwner = false;
this.Array = null;
this.Count = 0;
@@ -124,6 +161,7 @@ namespace ImageSharp
/// If is rented, it's the callers responsibility to return it to it's pool. (Most likely )
///
/// The unpinned
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public T[] UnPinAndTakeArrayOwnership()
{
if (this.IsDisposedOrLostArrayOwnership)
@@ -135,12 +173,23 @@ namespace ImageSharp
this.UnPin();
T[] array = this.Array;
this.Array = null;
+ this.isPoolingOwner = false;
return array;
}
+ ///
+ /// Clears the buffer, filling elements between 0 and -1 with default(T)
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Clear()
+ {
+ this.Slice().Clear(this.Count);
+ }
+
///
/// Pins .
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Pin()
{
this.handle = GCHandle.Alloc(this.Array, GCHandleType.Pinned);
@@ -150,6 +199,7 @@ namespace ImageSharp
///
/// Unpins .
///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void UnPin()
{
if (this.Pointer == IntPtr.Zero || !this.handle.IsAllocated)
diff --git a/src/ImageSharp/Common/Memory/PixelDataPool{T}.cs b/src/ImageSharp/Common/Memory/PixelDataPool{T}.cs
index a97d17fdb..dcd031f6e 100644
--- a/src/ImageSharp/Common/Memory/PixelDataPool{T}.cs
+++ b/src/ImageSharp/Common/Memory/PixelDataPool{T}.cs
@@ -9,15 +9,14 @@ namespace ImageSharp
using System.Buffers;
///
- /// Provides a resource pool that enables reusing instances of value type arrays .
- /// will always return arrays initialized with 'default(T)'
+ /// Provides a resource pool that enables reusing instances of value type arrays for image data .
///
/// The value type.
- public static class PixelDataPool
+ public class PixelDataPool
where T : struct
{
///
- /// The used to pool data.
+ /// The which is not kept clean.
///
private static readonly ArrayPool ArrayPool = ArrayPool.Create(CalculateMaxArrayLength(), 50);
@@ -37,11 +36,11 @@ namespace ImageSharp
/// The array to return to the buffer pool.
public static void Return(T[] array)
{
- ArrayPool.Return(array, true);
+ ArrayPool.Return(array);
}
///
- /// Heuristically calculates a reasonable maxArrayLength value for the backing .
+ /// Heuristically calculates a reasonable maxArrayLength value for the backing .
///
/// The maxArrayLength value
internal static int CalculateMaxArrayLength()
diff --git a/src/ImageSharp/Image.cs b/src/ImageSharp/Image.cs
index af31eff79..8bfd8ee1a 100644
--- a/src/ImageSharp/Image.cs
+++ b/src/ImageSharp/Image.cs
@@ -223,17 +223,5 @@ namespace ImageSharp
: base(other)
{
}
-
- ///
- public override PixelAccessor Lock()
- {
- return new PixelAccessor(this);
- }
-
- ///
- internal override ImageFrame ToFrame()
- {
- return new ImageFrame(this);
- }
}
}
diff --git a/src/ImageSharp/Image/ImageBase{TColor}.cs b/src/ImageSharp/Image/ImageBase{TColor}.cs
index 2badc008a..878ba09b3 100644
--- a/src/ImageSharp/Image/ImageBase{TColor}.cs
+++ b/src/ImageSharp/Image/ImageBase{TColor}.cs
@@ -60,6 +60,7 @@ namespace ImageSharp
{
this.Configuration = configuration ?? Configuration.Default;
this.InitPixels(width, height);
+ this.ClearPixels();
}
///
@@ -150,7 +151,7 @@ namespace ImageSharp
}
///
- public virtual PixelAccessor Lock()
+ public PixelAccessor Lock()
{
return new PixelAccessor(this);
}
@@ -232,5 +233,13 @@ namespace ImageSharp
PixelDataPool.Return(this.pixelBuffer);
this.pixelBuffer = null;
}
+
+ ///
+ /// Clears the pixel array.
+ ///
+ private void ClearPixels()
+ {
+ Array.Clear(this.pixelBuffer, 0, this.Width * this.Height);
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Image/PixelAccessor{TColor}.cs b/src/ImageSharp/Image/PixelAccessor{TColor}.cs
index e104b8ae7..a10676565 100644
--- a/src/ImageSharp/Image/PixelAccessor{TColor}.cs
+++ b/src/ImageSharp/Image/PixelAccessor{TColor}.cs
@@ -15,7 +15,7 @@ namespace ImageSharp
/// Provides per-pixel access to generic pixels.
///
/// The pixel format.
- public unsafe class PixelAccessor : IDisposable
+ public sealed unsafe class PixelAccessor : IDisposable
where TColor : struct, IPixel
{
///
@@ -91,7 +91,7 @@ namespace ImageSharp
///
/// Gets the pixel buffer array.
///
- public TColor[] PixelBuffer => this.pixelBuffer.Array;
+ public TColor[] PixelArray => this.pixelBuffer.Array;
///
/// Gets the pointer to the pixel buffer.
@@ -123,6 +123,8 @@ namespace ImageSharp
///
public ParallelOptions ParallelOptions { get; }
+ private static BulkPixelOperations Operations => BulkPixelOperations.Instance;
+
///
/// Gets or sets the pixel at the specified position.
///
@@ -236,6 +238,17 @@ namespace ImageSharp
Unsafe.InitBlock(this.pixelsBase, 0, (uint)(this.RowStride * this.Height));
}
+ ///
+ /// 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)
+ {
+ return this.pixelBuffer.Slice((y * this.Width) + x);
+ }
+
///
/// Sets the pixel buffer in an unsafe manner. This should not be used unless you know what its doing!!!
///
@@ -270,24 +283,15 @@ namespace ImageSharp
/// The target row index.
/// The width.
/// The height.
- protected virtual void CopyFromZyx(PixelArea area, int targetX, int targetY, int width, int height)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void CopyFromZyx(PixelArea area, int targetX, int targetY, int width, int height)
{
- TColor packed = default(TColor);
- int size = Unsafe.SizeOf();
-
for (int y = 0; y < height; y++)
{
- byte* source = area.PixelBase + (y * area.RowStride);
- byte* destination = this.GetRowPointer(targetX, targetY + y);
-
- for (int x = 0; x < width; x++)
- {
- packed.PackFromBytes(*(source + 2), *(source + 1), *source, 255);
- Unsafe.Write(destination, packed);
+ BufferPointer source = area.GetRowPointer(y);
+ BufferPointer destination = this.GetRowPointer(targetX, targetY + y);
- source += 3;
- destination += size;
- }
+ Operations.PackFromZyxBytes(source, destination, width);
}
}
@@ -299,24 +303,15 @@ namespace ImageSharp
/// The target row index.
/// The width.
/// The height.
- protected virtual void CopyFromZyxw(PixelArea area, int targetX, int targetY, int width, int height)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void CopyFromZyxw(PixelArea area, int targetX, int targetY, int width, int height)
{
- TColor packed = default(TColor);
- int size = Unsafe.SizeOf();
-
for (int y = 0; y < height; y++)
{
- byte* source = area.PixelBase + (y * area.RowStride);
- byte* destination = this.GetRowPointer(targetX, targetY + y);
-
- for (int x = 0; x < width; x++)
- {
- packed.PackFromBytes(*(source + 2), *(source + 1), *source, *(source + 3));
- Unsafe.Write(destination, packed);
+ BufferPointer source = area.GetRowPointer(y);
+ BufferPointer destination = this.GetRowPointer(targetX, targetY + y);
- source += 4;
- destination += size;
- }
+ Operations.PackFromZyxwBytes(source, destination, width);
}
}
@@ -328,24 +323,15 @@ namespace ImageSharp
/// The target row index.
/// The width.
/// The height.
- protected virtual void CopyFromXyz(PixelArea area, int targetX, int targetY, int width, int height)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void CopyFromXyz(PixelArea area, int targetX, int targetY, int width, int height)
{
- TColor packed = default(TColor);
- int size = Unsafe.SizeOf();
-
for (int y = 0; y < height; y++)
{
- byte* source = area.PixelBase + (y * area.RowStride);
- byte* destination = this.GetRowPointer(targetX, targetY + y);
-
- for (int x = 0; x < width; x++)
- {
- packed.PackFromBytes(*source, *(source + 1), *(source + 2), 255);
- Unsafe.Write(destination, packed);
+ BufferPointer source = area.GetRowPointer(y);
+ BufferPointer destination = this.GetRowPointer(targetX, targetY + y);
- source += 3;
- destination += size;
- }
+ Operations.PackFromXyzBytes(source, destination, width);
}
}
@@ -357,24 +343,14 @@ namespace ImageSharp
/// The target row index.
/// The width.
/// The height.
- protected virtual void CopyFromXyzw(PixelArea area, int targetX, int targetY, int width, int height)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void CopyFromXyzw(PixelArea area, int targetX, int targetY, int width, int height)
{
- TColor packed = default(TColor);
- int size = Unsafe.SizeOf();
-
for (int y = 0; y < height; y++)
{
- byte* source = area.PixelBase + (y * area.RowStride);
- byte* destination = this.GetRowPointer(targetX, targetY + y);
-
- for (int x = 0; x < width; x++)
- {
- packed.PackFromBytes(*source, *(source + 1), *(source + 2), *(source + 3));
- Unsafe.Write(destination, packed);
-
- source += 4;
- destination += size;
- }
+ BufferPointer source = area.GetRowPointer(y);
+ BufferPointer destination = this.GetRowPointer(targetX, targetY + y);
+ Operations.PackFromXyzwBytes(source, destination, width);
}
}
@@ -386,16 +362,14 @@ namespace ImageSharp
/// The source row index.
/// The width.
/// The height.
- protected virtual void CopyToZyx(PixelArea area, int sourceX, int sourceY, int width, int height)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void CopyToZyx(PixelArea area, int sourceX, int sourceY, int width, int height)
{
for (int y = 0; y < height; y++)
{
- int offset = y * area.RowStride;
- for (int x = 0; x < width; x++)
- {
- this[sourceX + x, sourceY + y].ToZyxBytes(area.Bytes, offset);
- offset += 3;
- }
+ BufferPointer source = this.GetRowPointer(sourceX, sourceY + y);
+ BufferPointer destination = area.GetRowPointer(y);
+ Operations.ToZyxBytes(source, destination, width);
}
}
@@ -407,16 +381,14 @@ namespace ImageSharp
/// The source row index.
/// The width.
/// The height.
- protected virtual void CopyToZyxw(PixelArea area, int sourceX, int sourceY, int width, int height)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void CopyToZyxw(PixelArea area, int sourceX, int sourceY, int width, int height)
{
for (int y = 0; y < height; y++)
{
- int offset = y * area.RowStride;
- for (int x = 0; x < width; x++)
- {
- this[sourceX + x, sourceY + y].ToZyxwBytes(area.Bytes, offset);
- offset += 4;
- }
+ BufferPointer source = this.GetRowPointer(sourceX, sourceY + y);
+ BufferPointer destination = area.GetRowPointer(y);
+ Operations.ToZyxwBytes(source, destination, width);
}
}
@@ -428,16 +400,14 @@ namespace ImageSharp
/// The source row index.
/// The width.
/// The height.
- protected virtual void CopyToXyz(PixelArea area, int sourceX, int sourceY, int width, int height)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void CopyToXyz(PixelArea area, int sourceX, int sourceY, int width, int height)
{
for (int y = 0; y < height; y++)
{
- int offset = y * area.RowStride;
- for (int x = 0; x < width; x++)
- {
- this[sourceX + x, sourceY + y].ToXyzBytes(area.Bytes, offset);
- offset += 3;
- }
+ BufferPointer source = this.GetRowPointer(sourceX, sourceY + y);
+ BufferPointer destination = area.GetRowPointer(y);
+ Operations.ToXyzBytes(source, destination, width);
}
}
@@ -449,32 +419,17 @@ namespace ImageSharp
/// The source row index.
/// The width.
/// The height.
- protected virtual void CopyToXyzw(PixelArea area, int sourceX, int sourceY, int width, int height)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void CopyToXyzw(PixelArea area, int sourceX, int sourceY, int width, int height)
{
for (int y = 0; y < height; y++)
{
- int offset = y * area.RowStride;
- for (int x = 0; x < width; x++)
- {
- this[sourceX + x, sourceY + y].ToXyzwBytes(area.Bytes, offset);
- offset += 4;
- }
+ BufferPointer source = this.GetRowPointer(sourceX, sourceY + y);
+ BufferPointer destination = area.GetRowPointer(y);
+ Operations.ToXyzwBytes(source, destination, width);
}
}
- ///
- /// Gets the pointer at the specified row.
- ///
- /// The column index.
- /// The row index.
- ///
- /// The .
- ///
- protected byte* GetRowPointer(int x, int y)
- {
- return this.pixelsBase + (((y * this.Width) + x) * Unsafe.SizeOf());
- }
-
private void SetPixelBufferUnsafe(int width, int height, TColor[] pixels)
{
this.SetPixelBufferUnsafe(width, height, new PinnedBuffer(width * height, pixels));
diff --git a/src/ImageSharp/Image/PixelArea{TColor}.cs b/src/ImageSharp/Image/PixelArea{TColor}.cs
index c54de12d6..be6debba2 100644
--- a/src/ImageSharp/Image/PixelArea{TColor}.cs
+++ b/src/ImageSharp/Image/PixelArea{TColor}.cs
@@ -203,6 +203,16 @@ namespace ImageSharp
Unsafe.InitBlock(this.PixelBase, 0, (uint)(this.RowStride * this.Height));
}
+ ///
+ /// Gets a to the row y.
+ ///
+ /// The y coordinate
+ /// The
+ internal BufferPointer GetRowPointer(int y)
+ {
+ return this.byteBuffer.Slice(y * this.RowStride);
+ }
+
///
/// Gets component count for the given order.
///
diff --git a/src/ImageSharp/ImageFrame.cs b/src/ImageSharp/ImageFrame.cs
deleted file mode 100644
index be654111d..000000000
--- a/src/ImageSharp/ImageFrame.cs
+++ /dev/null
@@ -1,52 +0,0 @@
-//
-// Copyright (c) James Jackson-South and contributors.
-// Licensed under the Apache License, Version 2.0.
-//
-
-namespace ImageSharp
-{
- using System.Diagnostics;
-
- ///
- /// An optimized frame for the class.
- ///
- [DebuggerDisplay("ImageFrame: {Width}x{Height}")]
- public sealed class ImageFrame : ImageFrame
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// The width of the image in pixels.
- /// The height of the image in pixels.
- ///
- /// The configuration providing initialization code which allows extending the library.
- ///
- public ImageFrame(int width, int height, Configuration configuration = null)
- : base(width, height, configuration)
- {
- }
-
- ///
- /// Initializes a new instance of the class.
- ///
- ///
- /// The image to create the frame from.
- ///
- public ImageFrame(ImageBase image)
- : base(image)
- {
- }
-
- ///
- public override PixelAccessor Lock()
- {
- return new PixelAccessor(this);
- }
-
- ///
- internal override ImageFrame Clone()
- {
- return new ImageFrame(this);
- }
- }
-}
diff --git a/src/ImageSharp/PixelAccessor.cs b/src/ImageSharp/PixelAccessor.cs
deleted file mode 100644
index 7827e7b47..000000000
--- a/src/ImageSharp/PixelAccessor.cs
+++ /dev/null
@@ -1,153 +0,0 @@
-//
-// Copyright (c) James Jackson-South and contributors.
-// Licensed under the Apache License, Version 2.0.
-//
-
-namespace ImageSharp
-{
- using System.Runtime.CompilerServices;
-
- ///
- /// An optimized pixel accessor for the class.
- ///
- public sealed unsafe class PixelAccessor : PixelAccessor
- {
- ///
- /// Initializes a new instance of the class.
- ///
- /// The image to provide pixel access for.
- public PixelAccessor(ImageBase image)
- : base(image)
- {
- }
-
- ///
- protected override void CopyFromXyzw(PixelArea area, int targetX, int targetY, int width, int height)
- {
- uint byteCount = (uint)width * 4;
-
- for (int y = 0; y < height; y++)
- {
- byte* source = area.PixelBase + (y * area.RowStride);
- byte* destination = this.GetRowPointer(targetX, targetY + y);
-
- Unsafe.CopyBlock(destination, source, byteCount);
- }
- }
-
- ///
- protected override void CopyFromXyz(PixelArea area, int targetX, int targetY, int width, int height)
- {
- for (int y = 0; y < height; y++)
- {
- byte* source = area.PixelBase + (y * area.RowStride);
- byte* destination = this.GetRowPointer(targetX, targetY + y);
-
- for (int x = 0; x < width; x++)
- {
- Unsafe.Write(destination, (uint)(*source << 0 | *(source + 1) << 8 | *(source + 2) << 16 | 255 << 24));
-
- source += 3;
- destination += 4;
- }
- }
- }
-
- ///
- protected override void CopyFromZyx(PixelArea area, int targetX, int targetY, int width, int height)
- {
- for (int y = 0; y < height; y++)
- {
- byte* source = area.PixelBase + (y * area.RowStride);
- byte* destination = this.GetRowPointer(targetX, targetY + y);
-
- for (int x = 0; x < width; x++)
- {
- Unsafe.Write(destination, (uint)(*(source + 2) << 0 | *(source + 1) << 8 | *source << 16 | 255 << 24));
-
- source += 3;
- destination += 4;
- }
- }
- }
-
- ///
- protected override void CopyFromZyxw(PixelArea area, int targetX, int targetY, int width, int height)
- {
- for (int y = 0; y < height; y++)
- {
- byte* source = area.PixelBase + (y * area.RowStride);
- byte* destination = this.GetRowPointer(targetX, targetY + y);
-
- for (int x = 0; x < width; x++)
- {
- Unsafe.Write(destination, (uint)(*(source + 2) << 0 | *(source + 1) << 8 | *source << 16 | *(source + 3) << 24));
-
- source += 4;
- destination += 4;
- }
- }
- }
-
- ///
- protected override void CopyToZyx(PixelArea area, int sourceX, int sourceY, int width, int height)
- {
- for (int y = 0; y < height; y++)
- {
- byte* source = this.GetRowPointer(sourceX, sourceY + y);
- byte* destination = area.PixelBase + (y * area.RowStride);
-
- for (int x = 0; x < width; x++)
- {
- *destination = *(source + 2);
- *(destination + 1) = *(source + 1);
- *(destination + 2) = *(source + 0);
-
- source += 4;
- destination += 3;
- }
- }
- }
-
- ///
- protected override void CopyToXyz(PixelArea area, int sourceX, int sourceY, int width, int height)
- {
- for (int y = 0; y < height; y++)
- {
- byte* source = this.GetRowPointer(sourceX, sourceY + y);
- byte* destination = area.PixelBase + (y * area.RowStride);
-
- for (int x = 0; x < width; x++)
- {
- *destination = *(source + 0);
- *(destination + 1) = *(source + 1);
- *(destination + 2) = *(source + 2);
-
- source += 4;
- destination += 3;
- }
- }
- }
-
- ///
- protected override void CopyToZyxw(PixelArea area, int sourceX, int sourceY, int width, int height)
- {
- for (int y = 0; y < height; y++)
- {
- byte* source = this.GetRowPointer(sourceX, sourceY + y);
- byte* destination = area.PixelBase + (y * area.RowStride);
-
- for (int x = 0; x < width; x++)
- {
- *destination = *(source + 2);
- *(destination + 1) = *(source + 1);
- *(destination + 2) = *(source + 0);
- *(destination + 3) = *(source + 3);
-
- source += 4;
- destination += 4;
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs
new file mode 100644
index 000000000..1c541d28b
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs
@@ -0,0 +1,63 @@
+// ReSharper disable InconsistentNaming
+namespace ImageSharp.Benchmarks.Color.Bulk
+{
+ using BenchmarkDotNet.Attributes;
+
+ using Color = ImageSharp.Color;
+
+ public abstract class PackFromXyzw
+ where TColor : struct, IPixel
+ {
+ private PinnedBuffer destination;
+
+ private PinnedBuffer source;
+
+ [Params(16, 128, 1024)]
+ public int Count { get; set; }
+
+ [Setup]
+ public void Setup()
+ {
+ this.destination = new PinnedBuffer(this.Count);
+ this.source = new PinnedBuffer(this.Count * 4);
+ }
+
+ [Cleanup]
+ public void Cleanup()
+ {
+ this.destination.Dispose();
+ this.source.Dispose();
+ }
+
+ [Benchmark(Baseline = true)]
+ public void PerElement()
+ {
+ byte[] s = this.source.Array;
+ TColor[] d = this.destination.Array;
+
+ for (int i = 0; i < this.Count; i++)
+ {
+ int i4 = i * 4;
+ TColor c = default(TColor);
+ c.PackFromBytes(s[i4], s[i4 + 1], s[i4 + 2], s[i4 + 3]);
+ d[i] = c;
+ }
+ }
+
+ [Benchmark]
+ public void CommonBulk()
+ {
+ new BulkPixelOperations().PackFromXyzwBytes(this.source, this.destination, this.Count);
+ }
+
+ [Benchmark]
+ public void OptimizedBulk()
+ {
+ BulkPixelOperations.Instance.PackFromXyzwBytes(this.source, this.destination, this.Count);
+ }
+ }
+
+ public class PackFromXyzw_Color : PackFromXyzw
+ {
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs
deleted file mode 100644
index 694a26f3d..000000000
--- a/tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs
+++ /dev/null
@@ -1,129 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-
-namespace ImageSharp.Benchmarks.Color.Bulk
-{
- using System.Runtime.CompilerServices;
- using System.Runtime.InteropServices;
-
- using BenchmarkDotNet.Attributes;
-
- using Color = ImageSharp.Color;
-
- ///
- /// Benchmark to measure the effect of using virtual bulk-copy calls inside PixelAccessor methods
- ///
- public unsafe class PixelAccessorVirtualCopy
- {
- abstract class CopyExecutor
- {
- internal abstract void VirtualCopy(BufferPointer destination, BufferPointer source, int count);
- }
-
- class UnsafeCopyExecutor : CopyExecutor
- {
- [MethodImpl(MethodImplOptions.NoInlining)]
- internal override unsafe void VirtualCopy(BufferPointer destination, BufferPointer source, int count)
- {
- Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, (uint)count*4);
- }
- }
-
- private PixelAccessor pixelAccessor;
-
- private PixelArea area;
-
- private CopyExecutor executor;
-
- [Params(64, 256, 512)]
- public int Width { get; set; }
-
- public int Height { get; set; } = 256;
-
-
- [Setup]
- public void Setup()
- {
- this.pixelAccessor = new PixelAccessor(this.Width, this.Height);
- this.area = new PixelArea(this.Width / 2, this.Height, ComponentOrder.Xyzw);
- this.executor = new UnsafeCopyExecutor();
- }
-
- [Cleanup]
- public void Cleanup()
- {
- this.pixelAccessor.Dispose();
- this.area.Dispose();
- }
-
- [Benchmark(Baseline = true)]
- public void CopyRawUnsafeInlined()
- {
- uint byteCount = (uint)this.area.Width * 4;
-
- int targetX = this.Width / 4;
- int targetY = 0;
-
- for (int y = 0; y < this.Height; y++)
- {
- byte* source = this.area.PixelBase + (y * this.area.RowStride);
- byte* destination = this.GetRowPointer(targetX, targetY + y);
-
- Unsafe.CopyBlock(destination, source, byteCount);
- }
- }
-
- [Benchmark]
- public void CopyBufferPointerUnsafeInlined()
- {
- uint byteCount = (uint)this.area.Width * 4;
-
- int targetX = this.Width / 4;
- int targetY = 0;
-
- for (int y = 0; y < this.Height; y++)
- {
- BufferPointer source = this.GetAreaRow(y);
- BufferPointer destination = this.GetPixelAccessorRow(targetX, targetY + y);
- Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, byteCount);
- }
- }
-
- [Benchmark]
- public void CopyBufferPointerUnsafeVirtual()
- {
- int targetX = this.Width / 4;
- int targetY = 0;
-
- for (int y = 0; y < this.Height; y++)
- {
- BufferPointer source = this.GetAreaRow(y);
- BufferPointer destination = this.GetPixelAccessorRow(targetX, targetY + y);
- this.executor.VirtualCopy(destination, source, this.area.Width);
- }
- }
-
- private byte* GetRowPointer(int x, int y)
- {
- return (byte*)this.pixelAccessor.DataPointer + (((y * this.pixelAccessor.Width) + x) * Unsafe.SizeOf());
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private BufferPointer GetPixelAccessorRow(int x, int y)
- {
- return new BufferPointer(
- this.pixelAccessor.PixelBuffer,
- (void*)this.pixelAccessor.DataPointer,
- (y * this.pixelAccessor.Width) + x
- );
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private BufferPointer GetAreaRow(int y)
- {
- return new BufferPointer(this.area.Bytes, this.area.PixelBase, y * this.area.RowStride);
- }
- }
-}
diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs
new file mode 100644
index 000000000..b48eaa35a
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToVector4.cs
@@ -0,0 +1,61 @@
+// ReSharper disable InconsistentNaming
+namespace ImageSharp.Benchmarks.Color.Bulk
+{
+ using System.Numerics;
+
+ using BenchmarkDotNet.Attributes;
+
+ public abstract class ToVector4
+ where TColor : struct, IPixel
+ {
+ private PinnedBuffer source;
+
+ private PinnedBuffer destination;
+
+ [Params(64, 300, 1024)]
+ public int Count { get; set; }
+
+ [Setup]
+ public void Setup()
+ {
+ this.source = new PinnedBuffer(this.Count);
+ this.destination = new PinnedBuffer(this.Count);
+ }
+
+ [Cleanup]
+ public void Cleanup()
+ {
+ this.source.Dispose();
+ this.destination.Dispose();
+ }
+
+ [Benchmark(Baseline = true)]
+ public void PerElement()
+ {
+ TColor[] s = this.source.Array;
+ Vector4[] d = this.destination.Array;
+
+ for (int i = 0; i < this.Count; i++)
+ {
+ TColor c = s[i];
+ d[i] = c.ToVector4();
+ }
+ }
+
+ [Benchmark]
+ public void CommonBulk()
+ {
+ new BulkPixelOperations().ToVector4(this.source, this.destination, this.Count);
+ }
+
+ [Benchmark]
+ public void OptimizedBulk()
+ {
+ BulkPixelOperations.Instance.ToVector4(this.source, this.destination, this.Count);
+ }
+ }
+
+ public class ToVector4_Color : ToVector4
+ {
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs
new file mode 100644
index 000000000..bc59dba4e
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs
@@ -0,0 +1,61 @@
+// ReSharper disable InconsistentNaming
+namespace ImageSharp.Benchmarks.Color.Bulk
+{
+ using BenchmarkDotNet.Attributes;
+
+ using Color = ImageSharp.Color;
+
+ public abstract class ToXyz
+ where TColor : struct, IPixel
+ {
+ private PinnedBuffer source;
+
+ private PinnedBuffer destination;
+
+ [Params(16, 128, 1024)]
+ public int Count { get; set; }
+
+ [Setup]
+ public void Setup()
+ {
+ this.source = new PinnedBuffer(this.Count);
+ this.destination = new PinnedBuffer(this.Count * 3);
+ }
+
+ [Cleanup]
+ public void Cleanup()
+ {
+ this.source.Dispose();
+ this.destination.Dispose();
+ }
+
+ [Benchmark(Baseline = true)]
+ public void PerElement()
+ {
+ TColor[] s = this.source.Array;
+ byte[] d = this.destination.Array;
+
+ for (int i = 0; i < this.Count; i++)
+ {
+ TColor c = s[i];
+ c.ToXyzBytes(d, i * 4);
+ }
+ }
+
+ [Benchmark]
+ public void CommonBulk()
+ {
+ new BulkPixelOperations().ToXyzBytes(this.source, this.destination, this.Count);
+ }
+
+ [Benchmark]
+ public void OptimizedBulk()
+ {
+ BulkPixelOperations.Instance.ToXyzBytes(this.source, this.destination, this.Count);
+ }
+ }
+
+ public class ToXyz_Color : ToXyz
+ {
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs
new file mode 100644
index 000000000..a4ec6f6dc
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+// ReSharper disable InconsistentNaming
+
+namespace ImageSharp.Benchmarks.Color.Bulk
+{
+ using BenchmarkDotNet.Attributes;
+
+ using Color = ImageSharp.Color;
+
+ public abstract class ToXyzw
+ where TColor : struct, IPixel
+ {
+ private PinnedBuffer source;
+
+ private PinnedBuffer destination;
+
+ [Params(16, 128, 1024)]
+ public int Count { get; set; }
+
+ [Setup]
+ public void Setup()
+ {
+ this.source = new PinnedBuffer(this.Count);
+ this.destination = new PinnedBuffer(this.Count * 4);
+ }
+
+ [Cleanup]
+ public void Cleanup()
+ {
+ this.source.Dispose();
+ this.destination.Dispose();
+ }
+
+ [Benchmark(Baseline = true)]
+ public void PerElement()
+ {
+ TColor[] s = this.source.Array;
+ byte[] d = this.destination.Array;
+
+ for (int i = 0; i < this.Count; i++)
+ {
+ TColor c = s[i];
+ c.ToXyzwBytes(d, i * 4);
+ }
+ }
+
+ [Benchmark]
+ public void CommonBulk()
+ {
+ new BulkPixelOperations().ToXyzwBytes(this.source, this.destination, this.Count);
+ }
+
+ [Benchmark]
+ public void OptimizedBulk()
+ {
+ BulkPixelOperations.Instance.ToXyzwBytes(this.source, this.destination, this.Count);
+ }
+ }
+
+ public class ToXyzw_Color : ToXyzw
+ {
+ }
+
+ public class ToXyzw_Argb : ToXyzw
+ {
+ }
+}
diff --git a/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs b/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs
new file mode 100644
index 000000000..9aa836de5
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/General/ClearBuffer.cs
@@ -0,0 +1,43 @@
+// ReSharper disable InconsistentNaming
+namespace ImageSharp.Benchmarks.General
+{
+ using System;
+ using System.Runtime.CompilerServices;
+ using System.Runtime.InteropServices;
+
+ using BenchmarkDotNet.Attributes;
+
+ using Color = ImageSharp.Color;
+
+ public unsafe class ClearBuffer
+ {
+ private PinnedBuffer buffer;
+
+ [Params(32, 128, 512)]
+ public int Count { get; set; }
+
+ [Setup]
+ public void Setup()
+ {
+ this.buffer = new PinnedBuffer(this.Count);
+ }
+
+ [Cleanup]
+ public void Cleanup()
+ {
+ this.buffer.Dispose();
+ }
+
+ [Benchmark(Baseline = true)]
+ public void Array_Clear()
+ {
+ Array.Clear(this.buffer.Array, 0, this.Count);
+ }
+
+ [Benchmark]
+ public void Unsafe_InitBlock()
+ {
+ Unsafe.InitBlock((void*)this.buffer.Pointer, default(byte), (uint)this.Count*sizeof(uint));
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Benchmarks/Image/EncodePng.cs b/tests/ImageSharp.Benchmarks/Image/EncodePng.cs
index dd1882c80..0bb0e922c 100644
--- a/tests/ImageSharp.Benchmarks/Image/EncodePng.cs
+++ b/tests/ImageSharp.Benchmarks/Image/EncodePng.cs
@@ -10,6 +10,10 @@ namespace ImageSharp.Benchmarks.Image
using System.IO;
using BenchmarkDotNet.Attributes;
+
+ using ImageSharp.Formats;
+ using ImageSharp.Quantizers;
+
using CoreImage = ImageSharp.Image;
public class EncodePng : BenchmarkBase
@@ -19,12 +23,21 @@ namespace ImageSharp.Benchmarks.Image
private Image bmpDrawing;
private CoreImage bmpCore;
+ [Params(false)]
+ public bool LargeImage { get; set; }
+
+ [Params(false)]
+ public bool UseOctreeQuantizer { get; set; }
+
[Setup]
public void ReadImages()
{
if (this.bmpStream == null)
{
- this.bmpStream = File.OpenRead("../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp");
+ string path = this.LargeImage
+ ? "../ImageSharp.Tests/TestImages/Formats/Jpg/baseline/jpeg420exif.jpg"
+ : "../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp";
+ this.bmpStream = File.OpenRead(path);
this.bmpCore = new CoreImage(this.bmpStream);
this.bmpStream.Position = 0;
this.bmpDrawing = Image.FromStream(this.bmpStream);
@@ -53,7 +66,13 @@ namespace ImageSharp.Benchmarks.Image
{
using (MemoryStream memoryStream = new MemoryStream())
{
- this.bmpCore.SaveAsPng(memoryStream);
+ Quantizer quantizer = this.UseOctreeQuantizer
+ ? (Quantizer)
+ new OctreeQuantizer()
+ : new PaletteQuantizer();
+
+ PngEncoderOptions options = new PngEncoderOptions() { Quantizer = quantizer };
+ this.bmpCore.SaveAsPng(memoryStream, options);
}
}
}
diff --git a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj
index a1ae6bb0d..8837f8733 100644
--- a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj
+++ b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj
@@ -206,12 +206,12 @@
-
- Benchmarks\PixelAccessorVirtualCopy.cs
-
Tests\Colors\BulkPixelOperationsTests.cs
+
+ Tests\Common\BufferPointerTests.cs
+
Tests\Common\PinnedBufferTests.cs
@@ -272,6 +272,9 @@
Tests\TestImages.cs
+
+ Tests\TestUtilities\ApproximateFloatComparer.cs
+
Tests\TestUtilities\Attributes\ImageDataAttributeBase.cs
@@ -340,7 +343,9 @@
-
+
+
+
diff --git a/tests/ImageSharp.Sandbox46/Program.cs b/tests/ImageSharp.Sandbox46/Program.cs
index 4d6d15925..467663a53 100644
--- a/tests/ImageSharp.Sandbox46/Program.cs
+++ b/tests/ImageSharp.Sandbox46/Program.cs
@@ -7,9 +7,9 @@ namespace ImageSharp.Sandbox46
{
using System;
using System.Runtime.DesignerServices;
-
- using ImageSharp.Benchmarks.Color.Bulk;
+
using ImageSharp.Tests;
+ using ImageSharp.Tests.Colors;
using Xunit.Abstractions;
@@ -38,21 +38,18 @@ namespace ImageSharp.Sandbox46
public static void Main(string[] args)
{
// RunDecodeJpegProfilingTests();
- TestPixelAccessorCopyFromXyzw();
+
+ RunToVector4ProfilingTest();
+
Console.ReadLine();
}
- private static void TestPixelAccessorCopyFromXyzw()
+ private static void RunToVector4ProfilingTest()
{
- PixelAccessorVirtualCopy benchmark = new PixelAccessorVirtualCopy();
- benchmark.Width = 64;
- benchmark.Setup();
-
- benchmark.CopyRawUnsafeInlined();
-
- benchmark.Cleanup();
+ BulkPixelOperationsTests.Color tests = new BulkPixelOperationsTests.Color(new ConsoleOutput());
+ tests.Benchmark_ToVector4();
}
-
+
private static void RunDecodeJpegProfilingTests()
{
Console.WriteLine("RunDecodeJpegProfilingTests...");
diff --git a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs
index 80d5952a1..6621292ec 100644
--- a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs
+++ b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs
@@ -1,21 +1,71 @@
-namespace ImageSharp.Tests.Colors
+// ReSharper disable InconsistentNaming
+// ReSharper disable AccessToDisposedClosure
+namespace ImageSharp.Tests.Colors
{
using System;
using System.Numerics;
using Xunit;
+ using Xunit.Abstractions;
public class BulkPixelOperationsTests
{
public class Color : BulkPixelOperationsTests
{
+ public Color(ITestOutputHelper output)
+ : base(output)
+ {
+ }
+
// For 4.6 test runner MemberData does not work without redeclaring the public field in the derived test class:
public static new TheoryData ArraySizesData => new TheoryData { 7, 16, 1111 };
+
+ [Fact]
+ public void IsSpecialImplementation()
+ {
+ Assert.IsType(BulkPixelOperations.Instance);
+ }
+
+ [Fact]
+ public void ToVector4SimdAligned()
+ {
+ ImageSharp.Color[] source = CreatePixelTestData(64);
+ Vector4[] expected = CreateExpectedVector4Data(source);
+
+ TestOperation(
+ source,
+ expected,
+ (s, d) => ImageSharp.Color.BulkOperations.ToVector4SimdAligned(s, d, 64)
+ );
+ }
+
+ // [Fact] // Profiling benchmark - enable manually!
+ public void Benchmark_ToVector4()
+ {
+ int times = 200000;
+ int count = 1024;
+
+ using (PinnedBuffer source = new PinnedBuffer(count))
+ using (PinnedBuffer dest = new PinnedBuffer(count))
+ {
+ this.Measure(
+ times,
+ () =>
+ {
+ BulkPixelOperations.Instance.ToVector4(source, dest, count);
+ });
+ }
+ }
}
public class Argb : BulkPixelOperationsTests
{
// For 4.6 test runner MemberData does not work without redeclaring the public field in the derived test class:
+ public Argb(ITestOutputHelper output)
+ : base(output)
+ {
+ }
+
public static new TheoryData ArraySizesData => new TheoryData { 7, 16, 1111 };
}
@@ -28,46 +78,65 @@
}
}
- public abstract class BulkPixelOperationsTests
+ public abstract class BulkPixelOperationsTests : MeasureFixture
where TColor : struct, IPixel
{
+ protected BulkPixelOperationsTests(ITestOutputHelper output)
+ : base(output)
+ {
+ }
+
public static TheoryData ArraySizesData => new TheoryData { 7, 16, 1111 };
-
- [Theory]
- [MemberData(nameof(ArraySizesData))]
- public void PackFromVector4(int count)
+
+ private static BulkPixelOperations Operations => BulkPixelOperations.Instance;
+
+ internal static TColor[] CreateExpectedPixelData(Vector4[] source)
{
- Vector4[] source = CreateVector4TestData(count);
- TColor[] expected = new TColor[count];
+ TColor[] expected = new TColor[source.Length];
- for (int i = 0; i < count; i++)
+ for (int i = 0; i < expected.Length; i++)
{
expected[i].PackFromVector4(source[i]);
}
+ return expected;
+ }
+
+ [Theory]
+ [MemberData(nameof(ArraySizesData))]
+ public void PackFromVector4(int count)
+ {
+ Vector4[] source = CreateVector4TestData(count);
+ TColor[] expected = CreateExpectedPixelData(source);
TestOperation(
source,
expected,
- (ops, s, d) => ops.PackFromVector4(s, d, count)
+ (s, d) => Operations.PackFromVector4(s, d, count)
);
}
- [Theory]
- [MemberData(nameof(ArraySizesData))]
- public void PackToVector4(int count)
+ internal static Vector4[] CreateExpectedVector4Data(TColor[] source)
{
- TColor[] source = CreatePixelTestData(count);
- Vector4[] expected = new Vector4[count];
+ Vector4[] expected = new Vector4[source.Length];
- for (int i = 0; i < count; i++)
+ for (int i = 0; i < expected.Length; i++)
{
expected[i] = source[i].ToVector4();
}
+ return expected;
+ }
+
+ [Theory]
+ [MemberData(nameof(ArraySizesData))]
+ public void ToVector4(int count)
+ {
+ TColor[] source = CreatePixelTestData(count);
+ Vector4[] expected = CreateExpectedVector4Data(source);
TestOperation(
source,
expected,
- (ops, s, d) => ops.ToVector4(s, d, count)
+ (s, d) => Operations.ToVector4(s, d, count)
);
}
@@ -89,13 +158,13 @@
TestOperation(
source,
expected,
- (ops, s, d) => ops.PackFromXyzBytes(s, d, count)
+ (s, d) => Operations.PackFromXyzBytes(s, d, count)
);
}
[Theory]
[MemberData(nameof(ArraySizesData))]
- public void PackToXyzBytes(int count)
+ public void ToXyzBytes(int count)
{
TColor[] source = CreatePixelTestData(count);
byte[] expected = new byte[count * 3];
@@ -109,7 +178,7 @@
TestOperation(
source,
expected,
- (ops, s, d) => ops.ToXyzBytes(s, d, count)
+ (s, d) => Operations.ToXyzBytes(s, d, count)
);
}
@@ -130,13 +199,13 @@
TestOperation(
source,
expected,
- (ops, s, d) => ops.PackFromXyzwBytes(s, d, count)
+ (s, d) => Operations.PackFromXyzwBytes(s, d, count)
);
}
[Theory]
[MemberData(nameof(ArraySizesData))]
- public void PackToXyzwBytes(int count)
+ public void ToXyzwBytes(int count)
{
TColor[] source = CreatePixelTestData(count);
byte[] expected = new byte[count * 4];
@@ -150,7 +219,7 @@
TestOperation(
source,
expected,
- (ops, s, d) => ops.ToXyzwBytes(s, d, count)
+ (s, d) => Operations.ToXyzwBytes(s, d, count)
);
}
@@ -171,13 +240,13 @@
TestOperation(
source,
expected,
- (ops, s, d) => ops.PackFromZyxBytes(s, d, count)
+ (s, d) => Operations.PackFromZyxBytes(s, d, count)
);
}
[Theory]
[MemberData(nameof(ArraySizesData))]
- public void PackToZyxBytes(int count)
+ public void ToZyxBytes(int count)
{
TColor[] source = CreatePixelTestData(count);
byte[] expected = new byte[count * 3];
@@ -191,7 +260,7 @@
TestOperation(
source,
expected,
- (ops, s, d) => ops.ToZyxBytes(s, d, count)
+ (s, d) => Operations.ToZyxBytes(s, d, count)
);
}
@@ -212,13 +281,13 @@
TestOperation(
source,
expected,
- (ops, s, d) => ops.PackFromZyxwBytes(s, d, count)
+ (s, d) => Operations.PackFromZyxwBytes(s, d, count)
);
}
[Theory]
[MemberData(nameof(ArraySizesData))]
- public void PackToZyxwBytes(int count)
+ public void ToZyxwBytes(int count)
{
TColor[] source = CreatePixelTestData(count);
byte[] expected = new byte[count * 4];
@@ -232,7 +301,7 @@
TestOperation(
source,
expected,
- (ops, s, d) => ops.ToZyxwBytes(s, d, count)
+ (s, d) => Operations.ToZyxwBytes(s, d, count)
);
}
@@ -262,33 +331,51 @@
this.ExpectedDestBuffer.Dispose();
}
+ private const float Tolerance = 0.0001f;
+
public void Verify()
{
int count = this.ExpectedDestBuffer.Count;
- TDest[] expected = this.ExpectedDestBuffer.Array;
- TDest[] actual = this.ActualDestBuffer.Array;
- for (int i = 0; i < count; i++)
+
+ if (typeof(TDest) == typeof(Vector4))
+ {
+ Vector4[] expected = this.ExpectedDestBuffer.Array as Vector4[];
+ Vector4[] actual = this.ActualDestBuffer.Array as Vector4[];
+
+ for (int i = 0; i < count; i++)
+ {
+ // ReSharper disable PossibleNullReferenceException
+ Assert.Equal(expected[i], actual[i], new ApproximateFloatComparer(0.001f));
+ // ReSharper restore PossibleNullReferenceException
+ }
+ }
+ else
{
- Assert.Equal(expected[i], actual[i]);
+ TDest[] expected = this.ExpectedDestBuffer.Array;
+ TDest[] actual = this.ActualDestBuffer.Array;
+ for (int i = 0; i < count; i++)
+ {
+ Assert.Equal(expected[i], actual[i]);
+ }
}
}
}
- private static void TestOperation(
+ internal static void TestOperation(
TSource[] source,
TDest[] expected,
- Action, BufferPointer, BufferPointer> action)
+ Action, BufferPointer> action)
where TSource : struct
where TDest : struct
{
using (var buffers = new TestBuffers(source, expected))
{
- action(BulkPixelOperations.Instance, buffers.Source, buffers.ActualDest);
+ action(buffers.Source, buffers.ActualDest);
buffers.Verify();
}
}
- private static Vector4[] CreateVector4TestData(int length)
+ internal static Vector4[] CreateVector4TestData(int length)
{
Vector4[] result = new Vector4[length];
Random rnd = new Random(42); // Deterministic random values
@@ -300,7 +387,7 @@
return result;
}
- private static TColor[] CreatePixelTestData(int length)
+ internal static TColor[] CreatePixelTestData(int length)
{
TColor[] result = new TColor[length];
@@ -315,7 +402,7 @@
return result;
}
- private static byte[] CreateByteTestData(int length)
+ internal static byte[] CreateByteTestData(int length)
{
byte[] result = new byte[length];
Random rnd = new Random(42); // Deterministic random values
@@ -326,8 +413,8 @@
}
return result;
}
-
- private static Vector4 GetVector(Random rnd)
+
+ internal static Vector4 GetVector(Random rnd)
{
return new Vector4(
(float)rnd.NextDouble(),
diff --git a/tests/ImageSharp.Tests/Common/BufferPointerTests.cs b/tests/ImageSharp.Tests/Common/BufferPointerTests.cs
index 4b5847d53..c82b63f11 100644
--- a/tests/ImageSharp.Tests/Common/BufferPointerTests.cs
+++ b/tests/ImageSharp.Tests/Common/BufferPointerTests.cs
@@ -15,16 +15,68 @@ namespace ImageSharp.Tests.Common
public double B;
+ public Foo(int a, double b)
+ {
+ this.A = a;
+ this.B = b;
+ }
+
internal static Foo[] CreateArray(int size)
{
Foo[] result = new Foo[size];
for (int i = 0; i < size; i++)
{
- result[i] = new Foo() { A = i, B = i };
+ result[i] = new Foo(i+1, i+1);
+ }
+ return result;
+ }
+ }
+
+ ///
+ /// sizeof(AlignedFoo) == sizeof(long)
+ ///
+ public struct AlignedFoo
+ {
+ public int A;
+
+ public int B;
+
+ static AlignedFoo()
+ {
+ Assert.Equal(sizeof(AlignedFoo), sizeof(long));
+ }
+
+ public AlignedFoo(int a, int b)
+ {
+ this.A = a;
+ this.B = b;
+ }
+
+ internal static AlignedFoo[] CreateArray(int size)
+ {
+ AlignedFoo[] result = new AlignedFoo[size];
+ for (int i = 0; i < size; i++)
+ {
+ result[i] = new AlignedFoo(i + 1, i + 1);
}
return result;
}
}
+
+ [Fact]
+ public void AsBytes()
+ {
+ Foo[] fooz = { new Foo(1, 2), new Foo(3, 4), new Foo(5, 6) };
+
+ using (PinnedBuffer colorBuf = new PinnedBuffer(fooz))
+ {
+ BufferPointer orig = colorBuf.Slice(1);
+ BufferPointer asBytes = (BufferPointer < byte > )orig;
+
+ Assert.Equal(asBytes.Offset, sizeof(Foo));
+ Assert.Equal(orig.PointerAtOffset, asBytes.PointerAtOffset);
+ }
+ }
[Fact]
public void ConstructWithoutOffset()
@@ -79,8 +131,58 @@ namespace ImageSharp.Tests.Common
}
}
+
+ [Theory]
+ [InlineData(4)]
+ [InlineData(1500)]
+ public void Clear(int count)
+ {
+ Foo[] array = Foo.CreateArray(count + 42);
+
+ int offset = 2;
+ fixed (Foo* p = array)
+ {
+ BufferPointer ap = new BufferPointer(array, p, offset);
+
+ // Act:
+ ap.Clear(count);
+
+ Assert.NotEqual(default(Foo), array[offset-1]);
+ Assert.Equal(default(Foo), array[offset]);
+ Assert.Equal(default(Foo), array[offset + count-1]);
+ Assert.NotEqual(default(Foo), array[offset + count]);
+ }
+ }
+
+
public class Copy
{
+ private static void AssertNotDefault(T[] data, int idx)
+ where T : struct
+ {
+ Assert.NotEqual(default(T), data[idx]);
+ }
+
+ private static byte[] CreateTestBytes(int count)
+ {
+ byte[] result = new byte[count];
+ for (int i = 0; i < result.Length; i++)
+ {
+ result[i] = (byte)((i % 200) + 1);
+ }
+ return result;
+ }
+
+ private static int[] CreateTestInts(int count)
+ {
+ int[] result = new int[count];
+ for (int i = 0; i < result.Length; i++)
+ {
+ result[i] = i + 1;
+ }
+ return result;
+ }
+
[Theory]
[InlineData(4)]
[InlineData(1500)]
@@ -92,35 +194,150 @@ namespace ImageSharp.Tests.Common
fixed (Foo* pSource = source)
fixed (Foo* pDest = dest)
{
- BufferPointer apSource = new BufferPointer(source, pSource);
- BufferPointer apDest = new BufferPointer(dest, pDest);
+ BufferPointer apSource = new BufferPointer(source, pSource, 1);
+ BufferPointer apDest = new BufferPointer(dest, pDest, 1);
- BufferPointer.Copy(apSource, apDest, count);
+ BufferPointer.Copy(apSource, apDest, count-1);
}
- Assert.Equal(source[0], dest[0]);
+ AssertNotDefault(source, 1);
+ AssertNotDefault(dest, 1);
+
+ Assert.NotEqual(source[0], dest[0]);
+ Assert.Equal(source[1], dest[1]);
+ Assert.Equal(source[2], dest[2]);
Assert.Equal(source[count-1], dest[count-1]);
Assert.NotEqual(source[count], dest[count]);
}
-
+
+ [Theory]
+ [InlineData(4)]
+ [InlineData(1500)]
+ public void GenericToOwnType_Aligned(int count)
+ {
+ AlignedFoo[] source = AlignedFoo.CreateArray(count + 2);
+ AlignedFoo[] dest = new AlignedFoo[count + 5];
+
+ fixed (AlignedFoo* pSource = source)
+ fixed (AlignedFoo* pDest = dest)
+ {
+ BufferPointer apSource = new BufferPointer(source, pSource, 1);
+ BufferPointer apDest = new BufferPointer(dest, pDest, 1);
+
+ BufferPointer.Copy(apSource, apDest, count - 1);
+ }
+
+ AssertNotDefault(source, 1);
+ AssertNotDefault(dest, 1);
+
+ Assert.NotEqual(source[0], dest[0]);
+ Assert.Equal(source[1], dest[1]);
+ Assert.Equal(source[2], dest[2]);
+ Assert.Equal(source[count - 1], dest[count - 1]);
+ Assert.NotEqual(source[count], dest[count]);
+ }
+
+ [Theory]
+ [InlineData(4)]
+ [InlineData(1500)]
+ public void IntToInt(int count)
+ {
+ int[] source = CreateTestInts(count+2);
+ int[] dest = new int[count + 5];
+
+ fixed (int* pSource = source)
+ fixed (int* pDest = dest)
+ {
+ BufferPointer apSource = new BufferPointer(source, pSource, 1);
+ BufferPointer apDest = new BufferPointer(dest, pDest, 1);
+
+ BufferPointer.Copy(apSource, apDest, count -1);
+ }
+
+ AssertNotDefault(source, 1);
+ AssertNotDefault(dest, 1);
+
+ Assert.NotEqual(source[0], dest[0]);
+ Assert.Equal(source[1], dest[1]);
+ Assert.Equal(source[2], dest[2]);
+ Assert.Equal(source[count - 1], dest[count - 1]);
+ Assert.NotEqual(source[count], dest[count]);
+ }
+
[Theory]
[InlineData(4)]
[InlineData(1500)]
public void GenericToBytes(int count)
{
int destCount = count * sizeof(Foo);
- Foo[] source = Foo.CreateArray(count + 2);
- byte[] dest = new byte[destCount + sizeof(Foo) + 1];
+ Foo[] source = Foo.CreateArray(count+2);
+ byte[] dest = new byte[destCount + sizeof(Foo)*2];
fixed (Foo* pSource = source)
fixed (byte* pDest = dest)
{
- BufferPointer apSource = new BufferPointer(source, pSource);
+ BufferPointer