diff --git a/src/ImageSharp/Image/PixelAccessor{TColor}.cs b/src/ImageSharp/Image/PixelAccessor{TColor}.cs
index 338f49182..b31ada10b 100644
--- a/src/ImageSharp/Image/PixelAccessor{TColor}.cs
+++ b/src/ImageSharp/Image/PixelAccessor{TColor}.cs
@@ -120,6 +120,11 @@ namespace ImageSharp
///
public bool PooledMemory { get; private set; }
+ ///
+ /// Gets the pixel buffer array.
+ ///
+ public TColor[] PixelBuffer => this.pixelBuffer;
+
///
/// Gets the pointer to the pixel buffer.
///
diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs
new file mode 100644
index 000000000..ed649792f
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs
@@ -0,0 +1,129 @@
+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(ArrayPointer destination, ArrayPointer source, int count);
+ }
+
+ class UnsafeCopyExecutor : CopyExecutor
+ {
+ [MethodImpl(MethodImplOptions.NoInlining)]
+ internal override unsafe void VirtualCopy(ArrayPointer destination, ArrayPointer 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)]
+ 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 CopyArrayPointerUnsafeInlined()
+ {
+ uint byteCount = (uint)this.area.Width * 4;
+
+ int targetX = this.Width / 4;
+ int targetY = 0;
+
+ for (int y = 0; y < this.Height; y++)
+ {
+ ArrayPointer source = this.GetAreaRow(y);
+ ArrayPointer destination = this.GetPixelAccessorRow(targetX, targetY + y);
+ Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, byteCount);
+ }
+ }
+
+ [Benchmark]
+ public void CopyArrayPointerUnsafeVirtual()
+ {
+ int targetX = this.Width / 4;
+ int targetY = 0;
+
+ for (int y = 0; y < this.Height; y++)
+ {
+ ArrayPointer source = this.GetAreaRow(y);
+ ArrayPointer 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 ArrayPointer GetPixelAccessorRow(int x, int y)
+ {
+ return new ArrayPointer(
+ this.pixelAccessor.PixelBuffer,
+ (void*)this.pixelAccessor.DataPointer,
+ (y * this.pixelAccessor.Width) + x
+ );
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private ArrayPointer GetAreaRow(int y)
+ {
+ return new ArrayPointer(this.area.Bytes, this.area.PixelBase, y * this.area.RowStride);
+ }
+ }
+}
diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs
index c066ac18c..a10417b90 100644
--- a/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs
+++ b/tests/ImageSharp.Benchmarks/Drawing/DrawBeziers.cs
@@ -28,7 +28,7 @@ namespace ImageSharp.Benchmarks
{
graphics.InterpolationMode = InterpolationMode.Default;
graphics.SmoothingMode = SmoothingMode.AntiAlias;
- var pen = new Pen(Color.HotPink, 10);
+ var pen = new Pen(System.Drawing.Color.HotPink, 10);
graphics.DrawBeziers(pen, new[] {
new PointF(10, 500),
new PointF(30, 10),
diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs
index 78f71b660..146def363 100644
--- a/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs
+++ b/tests/ImageSharp.Benchmarks/Drawing/DrawLines.cs
@@ -28,7 +28,7 @@ namespace ImageSharp.Benchmarks
{
graphics.InterpolationMode = InterpolationMode.Default;
graphics.SmoothingMode = SmoothingMode.AntiAlias;
- var pen = new Pen(Color.HotPink, 10);
+ var pen = new Pen(System.Drawing.Color.HotPink, 10);
graphics.DrawLines(pen, new[] {
new PointF(10, 10),
new PointF(550, 50),
diff --git a/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs b/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs
index 88618b912..e6c1ac0d6 100644
--- a/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs
+++ b/tests/ImageSharp.Benchmarks/Drawing/DrawPolygon.cs
@@ -27,7 +27,7 @@ namespace ImageSharp.Benchmarks
{
graphics.InterpolationMode = InterpolationMode.Default;
graphics.SmoothingMode = SmoothingMode.AntiAlias;
- var pen = new Pen(Color.HotPink, 10);
+ var pen = new Pen(System.Drawing.Color.HotPink, 10);
graphics.DrawPolygon(pen, new[] {
new PointF(10, 10),
new PointF(550, 50),
diff --git a/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs b/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs
index 718474f1f..589ac0cd4 100644
--- a/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs
+++ b/tests/ImageSharp.Benchmarks/Drawing/FillWithPattern.cs
@@ -25,7 +25,7 @@ namespace ImageSharp.Benchmarks
using (Graphics graphics = Graphics.FromImage(destination))
{
graphics.SmoothingMode = SmoothingMode.AntiAlias;
- var brush = new HatchBrush(HatchStyle.BackwardDiagonal, Color.HotPink);
+ var brush = new HatchBrush(HatchStyle.BackwardDiagonal, System.Drawing.Color.HotPink);
graphics.FillRectangle(brush, new Rectangle(0,0, 800,800)); // can't find a way to flood fill with a brush
}
using (MemoryStream ms = new MemoryStream())
diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs
new file mode 100644
index 000000000..dd20e85d5
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs
@@ -0,0 +1,54 @@
+namespace ImageSharp.Benchmarks.General.Vectorization
+{
+ using System.Numerics;
+
+ using BenchmarkDotNet.Attributes;
+
+ public class BitwiseOrUInt32
+ {
+ private uint[] input;
+
+ private uint[] result;
+
+ [Params(32)]
+ public int InputSize { get; set; }
+
+ private uint testValue;
+
+ [Setup]
+ public void Setup()
+ {
+ this.input = new uint[this.InputSize];
+ this.result = new uint[this.InputSize];
+ this.testValue = 42;
+
+ for (int i = 0; i < this.InputSize; i++)
+ {
+ this.input[i] = (uint) i;
+ }
+ }
+
+ [Benchmark(Baseline = true)]
+ public void Standard()
+ {
+ uint v = this.testValue;
+ for (int i = 0; i < this.input.Length; i++)
+ {
+ this.result[i] = this.input[i] | v;
+ }
+ }
+
+ [Benchmark]
+ public void Simd()
+ {
+ Vector v = new Vector(this.testValue);
+
+ for (int i = 0; i < this.input.Length; i+=Vector.Count)
+ {
+ Vector a = new Vector(this.input, i);
+ a = Vector.BitwiseOr(a, v);
+ a.CopyTo(this.result, i);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs
new file mode 100644
index 000000000..61582b7dc
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs
@@ -0,0 +1,54 @@
+namespace ImageSharp.Benchmarks.General.Vectorization
+{
+ using System.Numerics;
+
+ using BenchmarkDotNet.Attributes;
+
+ public class DivFloat
+ {
+ private float[] input;
+
+ private float[] result;
+
+ [Params(32)]
+ public int InputSize { get; set; }
+
+ private float testValue;
+
+ [Setup]
+ public void Setup()
+ {
+ this.input = new float[this.InputSize];
+ this.result = new float[this.InputSize];
+ this.testValue = 42;
+
+ for (int i = 0; i < this.InputSize; i++)
+ {
+ this.input[i] = (uint)i;
+ }
+ }
+
+ [Benchmark(Baseline = true)]
+ public void Standard()
+ {
+ float v = this.testValue;
+ for (int i = 0; i < this.input.Length; i++)
+ {
+ this.result[i] = this.input[i] / v;
+ }
+ }
+
+ [Benchmark]
+ public void Simd()
+ {
+ Vector v = new Vector(this.testValue);
+
+ for (int i = 0; i < this.input.Length; i += Vector.Count)
+ {
+ Vector a = new Vector(this.input, i);
+ a = a / v;
+ a.CopyTo(this.result, i);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs
new file mode 100644
index 000000000..75fa03c04
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs
@@ -0,0 +1,54 @@
+namespace ImageSharp.Benchmarks.General.Vectorization
+{
+ using System.Numerics;
+
+ using BenchmarkDotNet.Attributes;
+
+ public class DivUInt32
+ {
+ private uint[] input;
+
+ private uint[] result;
+
+ [Params(32)]
+ public int InputSize { get; set; }
+
+ private uint testValue;
+
+ [Setup]
+ public void Setup()
+ {
+ this.input = new uint[this.InputSize];
+ this.result = new uint[this.InputSize];
+ this.testValue = 42;
+
+ for (int i = 0; i < this.InputSize; i++)
+ {
+ this.input[i] = (uint)i;
+ }
+ }
+
+ [Benchmark(Baseline = true)]
+ public void Standard()
+ {
+ uint v = this.testValue;
+ for (int i = 0; i < this.input.Length; i++)
+ {
+ this.result[i] = this.input[i] / v;
+ }
+ }
+
+ [Benchmark]
+ public void Simd()
+ {
+ Vector v = new Vector(this.testValue);
+
+ for (int i = 0; i < this.input.Length; i += Vector.Count)
+ {
+ Vector a = new Vector(this.input, i);
+ a = a / v;
+ a.CopyTo(this.result, i);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs
new file mode 100644
index 000000000..151145e12
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs
@@ -0,0 +1,54 @@
+namespace ImageSharp.Benchmarks.General.Vectorization
+{
+ using System.Numerics;
+
+ using BenchmarkDotNet.Attributes;
+
+ public class MulFloat
+ {
+ private float[] input;
+
+ private float[] result;
+
+ [Params(32)]
+ public int InputSize { get; set; }
+
+ private float testValue;
+
+ [Setup]
+ public void Setup()
+ {
+ this.input = new float[this.InputSize];
+ this.result = new float[this.InputSize];
+ this.testValue = 42;
+
+ for (int i = 0; i < this.InputSize; i++)
+ {
+ this.input[i] = (uint)i;
+ }
+ }
+
+ [Benchmark(Baseline = true)]
+ public void Standard()
+ {
+ float v = this.testValue;
+ for (int i = 0; i < this.input.Length; i++)
+ {
+ this.result[i] = this.input[i] * v;
+ }
+ }
+
+ [Benchmark]
+ public void Simd()
+ {
+ Vector v = new Vector(this.testValue);
+
+ for (int i = 0; i < this.input.Length; i += Vector.Count)
+ {
+ Vector a = new Vector(this.input, i);
+ a = a * v;
+ a.CopyTo(this.result, i);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs
new file mode 100644
index 000000000..f7d6cf9b9
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs
@@ -0,0 +1,54 @@
+namespace ImageSharp.Benchmarks.General.Vectorization
+{
+ using System.Numerics;
+
+ using BenchmarkDotNet.Attributes;
+
+ public class MulUInt32
+ {
+ private uint[] input;
+
+ private uint[] result;
+
+ [Params(32)]
+ public int InputSize { get; set; }
+
+ private uint testValue;
+
+ [Setup]
+ public void Setup()
+ {
+ this.input = new uint[this.InputSize];
+ this.result = new uint[this.InputSize];
+ this.testValue = 42;
+
+ for (int i = 0; i < this.InputSize; i++)
+ {
+ this.input[i] = (uint)i;
+ }
+ }
+
+ [Benchmark(Baseline = true)]
+ public void Standard()
+ {
+ uint v = this.testValue;
+ for (int i = 0; i < this.input.Length; i++)
+ {
+ this.result[i] = this.input[i] * v;
+ }
+ }
+
+ [Benchmark]
+ public void Simd()
+ {
+ Vector v = new Vector(this.testValue);
+
+ for (int i = 0; i < this.input.Length; i += Vector.Count)
+ {
+ Vector a = new Vector(this.input, i);
+ a = a * v;
+ a.CopyTo(this.result, i);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs b/tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs
new file mode 100644
index 000000000..b0ca181cd
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs
@@ -0,0 +1,62 @@
+namespace ImageSharp.Benchmarks.General.Vectorization
+{
+ using System.Numerics;
+ using System.Runtime.InteropServices;
+
+ using BenchmarkDotNet.Attributes;
+
+ public class ReinterpretUInt32AsFloat
+ {
+ private uint[] input;
+
+ private float[] result;
+
+ [Params(32)]
+ public int InputSize { get; set; }
+
+ [StructLayout(LayoutKind.Explicit)]
+ struct UIntFloatUnion
+ {
+ [FieldOffset(0)]
+ public float f;
+
+ [FieldOffset(0)]
+ public uint i;
+ }
+
+
+ [Setup]
+ public void Setup()
+ {
+ this.input = new uint[this.InputSize];
+ this.result = new float[this.InputSize];
+
+ for (int i = 0; i < this.InputSize; i++)
+ {
+ this.input[i] = (uint)i;
+ }
+ }
+
+ [Benchmark(Baseline = true)]
+ public void Standard()
+ {
+ UIntFloatUnion u = default(UIntFloatUnion);
+ for (int i = 0; i < this.input.Length; i++)
+ {
+ u.i = this.input[i];
+ this.result[i] = u.f;
+ }
+ }
+
+ [Benchmark]
+ public void Simd()
+ {
+ for (int i = 0; i < this.input.Length; i += Vector.Count)
+ {
+ Vector a = new Vector(this.input, i);
+ Vector b = Vector.AsVectorSingle(a);
+ b.CopyTo(this.result, i);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj
index 2444db031..d1b059f44 100644
--- a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj
+++ b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj
@@ -109,6 +109,10 @@
..\..\packages\System.Reflection.Metadata.1.3.0\lib\portable-net45+win8\System.Reflection.Metadata.dll
True
+
+ ..\..\packages\System.Runtime.CompilerServices.Unsafe.4.3.0\lib\netstandard1.0\System.Runtime.CompilerServices.Unsafe.dll
+ True
+
..\..\packages\System.Security.Cryptography.Algorithms.4.2.0\lib\net461\System.Security.Cryptography.Algorithms.dll
True
@@ -202,6 +206,9 @@
+
+ Benchmarks\PixelAccessorVirtualCopy.cs
+
Tests\Drawing\PolygonTests.cs
@@ -327,9 +334,7 @@
-
-
-
+
diff --git a/tests/ImageSharp.Sandbox46/Program.cs b/tests/ImageSharp.Sandbox46/Program.cs
index 48219902b..f289ac2db 100644
--- a/tests/ImageSharp.Sandbox46/Program.cs
+++ b/tests/ImageSharp.Sandbox46/Program.cs
@@ -8,6 +8,7 @@ namespace ImageSharp.Sandbox46
using System;
using System.Runtime.DesignerServices;
+ using ImageSharp.Benchmarks.Color.Bulk;
using ImageSharp.Tests;
using Xunit.Abstractions;
@@ -36,7 +37,23 @@ namespace ImageSharp.Sandbox46
///
public static void Main(string[] args)
{
- RunDecodeJpegProfilingTests();
+ //RunDecodeJpegProfilingTests();
+ TestPixelAccessorCopyFromXyzw();
+ Console.ReadLine();
+ }
+
+ private static void TestPixelAccessorCopyFromXyzw()
+ {
+ PixelAccessorVirtualCopy benchmark = new PixelAccessorVirtualCopy();
+ benchmark.Width = 64;
+ benchmark.Setup();
+
+ benchmark.CopyRawUnsafeInlined();
+ benchmark.CopyArrayPointerUnsafe();
+ benchmark.CopyArrayPointerVirtualUnsafe();
+ benchmark.CopyArrayPointerVirtualMarshal();
+
+ benchmark.Cleanup();
}
private static void RunDecodeJpegProfilingTests()
diff --git a/tests/ImageSharp.Sandbox46/app.config b/tests/ImageSharp.Sandbox46/app.config
index 5a049c66e..3328297a5 100644
--- a/tests/ImageSharp.Sandbox46/app.config
+++ b/tests/ImageSharp.Sandbox46/app.config
@@ -10,6 +10,10 @@
+
+
+
+
\ No newline at end of file
diff --git a/tests/ImageSharp.Sandbox46/packages.config b/tests/ImageSharp.Sandbox46/packages.config
index 65ad74fa6..426f5f1b5 100644
--- a/tests/ImageSharp.Sandbox46/packages.config
+++ b/tests/ImageSharp.Sandbox46/packages.config
@@ -29,6 +29,7 @@
+