From e8285ef6660a64dd6d945849761d470a9b932fb5 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 20 Feb 2017 01:17:31 +0100 Subject: [PATCH 01/15] Benchmarks: PixelAccessorVirtualCopy, vectorization --- src/ImageSharp/Image/PixelAccessor{TColor}.cs | 5 + .../Color/Bulk/PixelAccessorVirtualCopy.cs | 129 ++++++++++++++++++ .../Drawing/DrawBeziers.cs | 2 +- .../Drawing/DrawLines.cs | 2 +- .../Drawing/DrawPolygon.cs | 2 +- .../Drawing/FillWithPattern.cs | 2 +- .../General/Vectorization/BitwiseOrUint32.cs | 54 ++++++++ .../General/Vectorization/DivFloat.cs | 54 ++++++++ .../General/Vectorization/DivUInt32.cs | 54 ++++++++ .../General/Vectorization/MulFloat.cs | 54 ++++++++ .../General/Vectorization/MulUInt32.cs | 54 ++++++++ .../Vectorization/ReinterpretUInt32AsFloat.cs | 62 +++++++++ .../ImageSharp.Sandbox46.csproj | 11 +- tests/ImageSharp.Sandbox46/Program.cs | 19 ++- tests/ImageSharp.Sandbox46/app.config | 4 + tests/ImageSharp.Sandbox46/packages.config | 1 + 16 files changed, 501 insertions(+), 8 deletions(-) create mode 100644 tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs create mode 100644 tests/ImageSharp.Benchmarks/General/Vectorization/BitwiseOrUint32.cs create mode 100644 tests/ImageSharp.Benchmarks/General/Vectorization/DivFloat.cs create mode 100644 tests/ImageSharp.Benchmarks/General/Vectorization/DivUInt32.cs create mode 100644 tests/ImageSharp.Benchmarks/General/Vectorization/MulFloat.cs create mode 100644 tests/ImageSharp.Benchmarks/General/Vectorization/MulUInt32.cs create mode 100644 tests/ImageSharp.Benchmarks/General/Vectorization/ReinterpretUInt32AsFloat.cs 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 @@ + From 6475c52ced12a48cdbddb6a979a97cf831e90ff6 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 20 Feb 2017 01:25:11 +0100 Subject: [PATCH 02/15] additional PixelAccessorVirtualCopy Param --- .../Color/Bulk/PixelAccessorVirtualCopy.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs index ed649792f..9222d6bac 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs @@ -37,7 +37,7 @@ namespace ImageSharp.Benchmarks.Color.Bulk private CopyExecutor executor; - [Params(64, 256)] + [Params(64, 256, 512)] public int Width { get; set; } public int Height { get; set; } = 256; From a52f6bf1b5065db0d88680c8e6c8a5ace1e6a037 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 2 Mar 2017 01:50:24 +0100 Subject: [PATCH 03/15] BulkPixelOperations skeleton --- src/ImageSharp/Colors/Color.cs | 3 + src/ImageSharp/Colors/PackedPixel/Alpha8.cs | 3 + src/ImageSharp/Colors/PackedPixel/Argb.cs | 3 + src/ImageSharp/Colors/PackedPixel/Bgr565.cs | 3 + src/ImageSharp/Colors/PackedPixel/Bgra4444.cs | 3 + src/ImageSharp/Colors/PackedPixel/Bgra5551.cs | 3 + .../Colors/PackedPixel/BulkPixelOperations.cs | 56 ++++++++++ src/ImageSharp/Colors/PackedPixel/Byte4.cs | 3 + .../Colors/PackedPixel/HalfSingle.cs | 3 + .../Colors/PackedPixel/HalfVector2.cs | 3 + .../Colors/PackedPixel/HalfVector4.cs | 3 + src/ImageSharp/Colors/PackedPixel/IPixel.cs | 4 + .../Colors/PackedPixel/NormalizedByte2.cs | 3 + .../Colors/PackedPixel/NormalizedByte4.cs | 3 + .../Colors/PackedPixel/NormalizedShort2.cs | 3 + .../Colors/PackedPixel/NormalizedShort4.cs | 3 + src/ImageSharp/Colors/PackedPixel/Rg32.cs | 3 + .../Colors/PackedPixel/Rgba1010102.cs | 3 + src/ImageSharp/Colors/PackedPixel/Rgba64.cs | 3 + src/ImageSharp/Colors/PackedPixel/Short2.cs | 3 + src/ImageSharp/Colors/PackedPixel/Short4.cs | 3 + src/ImageSharp/Common/Memory/ArrayPointer.cs | 50 +++++++++ .../General/ArrayCopy.cs | 25 ++++- .../Colors/BulkPixelOperationsTests.cs | 104 ++++++++++++++++++ .../Common/ArrayPointerTests.cs | 94 +++++++++++++++- 25 files changed, 381 insertions(+), 9 deletions(-) create mode 100644 src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs create mode 100644 src/ImageSharp/Common/Memory/ArrayPointer.cs create mode 100644 tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs diff --git a/src/ImageSharp/Colors/Color.cs b/src/ImageSharp/Colors/Color.cs index 469774b34..8a869935c 100644 --- a/src/ImageSharp/Colors/Color.cs +++ b/src/ImageSharp/Colors/Color.cs @@ -112,6 +112,9 @@ namespace ImageSharp this.packedValue = packed; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Gets or sets the red component. /// diff --git a/src/ImageSharp/Colors/PackedPixel/Alpha8.cs b/src/ImageSharp/Colors/PackedPixel/Alpha8.cs index 485725d71..1181eb9e4 100644 --- a/src/ImageSharp/Colors/PackedPixel/Alpha8.cs +++ b/src/ImageSharp/Colors/PackedPixel/Alpha8.cs @@ -26,6 +26,9 @@ namespace ImageSharp /// public byte PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/Argb.cs b/src/ImageSharp/Colors/PackedPixel/Argb.cs index bef986fb9..1b97d2337 100644 --- a/src/ImageSharp/Colors/PackedPixel/Argb.cs +++ b/src/ImageSharp/Colors/PackedPixel/Argb.cs @@ -109,6 +109,9 @@ namespace ImageSharp /// public uint PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Gets or sets the red component. /// diff --git a/src/ImageSharp/Colors/PackedPixel/Bgr565.cs b/src/ImageSharp/Colors/PackedPixel/Bgr565.cs index ebe8d2533..41b2bdc2e 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgr565.cs +++ b/src/ImageSharp/Colors/PackedPixel/Bgr565.cs @@ -39,6 +39,9 @@ namespace ImageSharp /// public ushort PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs b/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs index ccd6ab1f3..99659a36b 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs +++ b/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs @@ -38,6 +38,9 @@ namespace ImageSharp /// public ushort PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs b/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs index a7a2e899a..86864fd48 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs +++ b/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs @@ -40,6 +40,9 @@ namespace ImageSharp /// public ushort PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs new file mode 100644 index 000000000..c914b3921 --- /dev/null +++ b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs @@ -0,0 +1,56 @@ +namespace ImageSharp +{ + using System.Numerics; + + public unsafe class BulkPixelOperations + where TColor : struct, IPixel + { + public static BulkPixelOperations Instance { get; } = default(TColor).BulkOperations; + + internal virtual void PackFromVector4( + ArrayPointer sourceVectors, + ArrayPointer destColors, + int count) + { + } + + internal virtual void PackToVector4( + ArrayPointer sourceColors, + ArrayPointer destVectors, + int count) + { + } + + internal virtual void PackToXyzBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) + { + } + + internal virtual void PackFromXyzBytes(ArrayPointer sourceBytes, ArrayPointer destColors, int count) + { + } + + internal virtual void PackToXyzwBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) + { + } + + internal virtual void PackFromXyzwBytes(ArrayPointer sourceBytes, ArrayPointer destColors, int count) + { + } + + internal virtual void PackToZyxBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) + { + } + + internal virtual void PackFromZyxBytes(ArrayPointer sourceBytes, ArrayPointer destColors, int count) + { + } + + internal virtual void PackToZyxwBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) + { + } + + internal virtual void PackFromZyxwBytes(ArrayPointer sourceBytes, ArrayPointer destColors, int count) + { + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Colors/PackedPixel/Byte4.cs b/src/ImageSharp/Colors/PackedPixel/Byte4.cs index 9d5eb9be8..5712027d9 100644 --- a/src/ImageSharp/Colors/PackedPixel/Byte4.cs +++ b/src/ImageSharp/Colors/PackedPixel/Byte4.cs @@ -41,6 +41,9 @@ namespace ImageSharp /// public uint PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs b/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs index acfa639b7..0bc82c3a6 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs @@ -36,6 +36,9 @@ namespace ImageSharp /// public ushort PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs b/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs index e02c226dd..778f86e0f 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs @@ -45,6 +45,9 @@ namespace ImageSharp /// public uint PackedValue { get; set; } + + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); /// /// Compares two objects for equality. diff --git a/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs b/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs index 7c7f640e4..c24553d3f 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs @@ -49,6 +49,9 @@ namespace ImageSharp /// public ulong PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/IPixel.cs b/src/ImageSharp/Colors/PackedPixel/IPixel.cs index 1c3e20a7e..c17fe86cc 100644 --- a/src/ImageSharp/Colors/PackedPixel/IPixel.cs +++ b/src/ImageSharp/Colors/PackedPixel/IPixel.cs @@ -15,6 +15,10 @@ namespace ImageSharp public interface IPixel : IPixel, IEquatable where TSelf : struct, IPixel { + /// + /// Gets the instance for this pixel type. + /// + BulkPixelOperations BulkOperations { get; } } /// diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs index 116a68172..d425806c2 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs @@ -51,6 +51,9 @@ namespace ImageSharp /// public ushort PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs index 7aaa30c52..cba3f0e86 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs @@ -52,6 +52,9 @@ namespace ImageSharp /// public uint PackedValue { get; set; } + + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); /// /// Compares two objects for equality. diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs index 2f4ef89d6..4bc7f9427 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs @@ -51,6 +51,9 @@ namespace ImageSharp /// public uint PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs index 60c5c9805..c848b7236 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs @@ -53,6 +53,9 @@ namespace ImageSharp /// public ulong PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/Rg32.cs b/src/ImageSharp/Colors/PackedPixel/Rg32.cs index 9e5e5a711..9eb2247c9 100644 --- a/src/ImageSharp/Colors/PackedPixel/Rg32.cs +++ b/src/ImageSharp/Colors/PackedPixel/Rg32.cs @@ -36,6 +36,9 @@ namespace ImageSharp /// public uint PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs b/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs index 95a8d3b97..4f99feb6e 100644 --- a/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs +++ b/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs @@ -39,6 +39,9 @@ namespace ImageSharp /// public uint PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/Rgba64.cs b/src/ImageSharp/Colors/PackedPixel/Rgba64.cs index 679a55c4e..a23e57ec3 100644 --- a/src/ImageSharp/Colors/PackedPixel/Rgba64.cs +++ b/src/ImageSharp/Colors/PackedPixel/Rgba64.cs @@ -38,6 +38,9 @@ namespace ImageSharp /// public ulong PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/Short2.cs b/src/ImageSharp/Colors/PackedPixel/Short2.cs index 1c1cb28c3..f26e82578 100644 --- a/src/ImageSharp/Colors/PackedPixel/Short2.cs +++ b/src/ImageSharp/Colors/PackedPixel/Short2.cs @@ -51,6 +51,9 @@ namespace ImageSharp /// public uint PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Colors/PackedPixel/Short4.cs b/src/ImageSharp/Colors/PackedPixel/Short4.cs index 2c11a1f8b..6dc7545e1 100644 --- a/src/ImageSharp/Colors/PackedPixel/Short4.cs +++ b/src/ImageSharp/Colors/PackedPixel/Short4.cs @@ -53,6 +53,9 @@ namespace ImageSharp /// public ulong PackedValue { get; set; } + /// + public BulkPixelOperations BulkOperations => new BulkPixelOperations(); + /// /// Compares two objects for equality. /// diff --git a/src/ImageSharp/Common/Memory/ArrayPointer.cs b/src/ImageSharp/Common/Memory/ArrayPointer.cs new file mode 100644 index 000000000..c864d31fd --- /dev/null +++ b/src/ImageSharp/Common/Memory/ArrayPointer.cs @@ -0,0 +1,50 @@ +namespace ImageSharp +{ + using System.Runtime.CompilerServices; + + /// + /// Utility methods to + /// + internal static class ArrayPointer + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void Copy(ArrayPointer source, ArrayPointer destination, int count) + where T : struct + { + Unsafe.CopyBlock((void*)source.PointerAtOffset, (void*)destination.PointerAtOffset, USizeOf(count)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void Copy(ArrayPointer source, ArrayPointer destination, int countInSource) + where T : struct + { + Unsafe.CopyBlock((void*)source.PointerAtOffset, (void*)destination.PointerAtOffset, USizeOf(countInSource)); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void Copy(ArrayPointer source, ArrayPointer destination, int countInDest) + where T : struct + { + Unsafe.CopyBlock((void*)source.PointerAtOffset, (void*)destination.PointerAtOffset, USizeOf(countInDest)); + } + + /// + /// Gets the size of `count` elements in bytes. + /// + /// The count of the elements + /// The size in bytes as int + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int SizeOf(int count) + where T : struct => Unsafe.SizeOf() * count; + + /// + /// Gets the size of `count` elements in bytes as UInt32 + /// + /// The count of the elements + /// The size in bytes as UInt32 + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static uint USizeOf(int count) + where T : struct + => (uint)SizeOf(count); + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs b/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs index dddd83e42..72fd6dc24 100644 --- a/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs +++ b/tests/ImageSharp.Benchmarks/General/ArrayCopy.cs @@ -2,22 +2,23 @@ // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // - namespace ImageSharp.Benchmarks.General { using System; using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; using BenchmarkDotNet.Attributes; + [Config(typeof(Config.Short))] public class ArrayCopy { - [Params(100, 1000, 10000)] + [Params(10, 100, 1000, 10000)] public int Count { get; set; } - private byte[] source; + byte[] source; - private byte[] destination; + byte[] destination; [Setup] public void SetUp() @@ -42,6 +43,12 @@ namespace ImageSharp.Benchmarks.General } } + [Benchmark(Description = "Copy using Buffer.BlockCopy()")] + public void CopyUsingBufferBlockCopy() + { + Buffer.BlockCopy(this.source, 0, this.destination, 0, this.Count); + } + [Benchmark(Description = "Copy using Buffer.MemoryCopy")] public unsafe void CopyUsingBufferMemoryCopy() { @@ -51,5 +58,15 @@ namespace ImageSharp.Benchmarks.General Buffer.MemoryCopy(pinnedSource, pinnedDestination, this.Count, this.Count); } } + + + [Benchmark(Description = "Copy using Marshal.Copy")] + public unsafe void CopyUsingMarshalCopy() + { + fixed (byte* pinnedDestination = this.destination) + { + Marshal.Copy(this.source, 0, (IntPtr)pinnedDestination, this.Count); + } + } } } diff --git a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs new file mode 100644 index 000000000..413bd9451 --- /dev/null +++ b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs @@ -0,0 +1,104 @@ +namespace ImageSharp.Tests.Colors +{ + using System; + + using Xunit; + + public class BulkPixelOperationsTests + { + public class TypeParam + { + } + + + [Theory] + [InlineData(default(TypeParam))] + [InlineData(default(TypeParam))] + public virtual void PackFromVector4(TypeParam dummy) + where TColor : struct, IPixel + { + throw new NotImplementedException(); + } + + [Theory] + [InlineData(default(TypeParam))] + [InlineData(default(TypeParam))] + public virtual void PackToVector4(TypeParam dummy) + where TColor : struct, IPixel + { + throw new NotImplementedException(); + } + + [Theory] + [InlineData(default(TypeParam))] + [InlineData(default(TypeParam))] + public virtual void PackToXyzBytes(TypeParam dummy) + where TColor : struct, IPixel + { + throw new NotImplementedException(); + } + + [Theory] + [InlineData(default(TypeParam))] + [InlineData(default(TypeParam))] + public virtual void PackFromXyzBytes(TypeParam dummy) + where TColor : struct, IPixel + { + throw new NotImplementedException(); + } + + [Theory] + [InlineData(default(TypeParam))] + [InlineData(default(TypeParam))] + public virtual void PackToXyzwBytes(TypeParam dummy) + where TColor : struct, IPixel + { + throw new NotImplementedException(); + } + + [Theory] + [InlineData(default(TypeParam))] + [InlineData(default(TypeParam))] + public virtual void PackFromXyzwBytes(TypeParam dummy) + where TColor : struct, IPixel + { + throw new NotImplementedException(); + } + + [Theory] + [InlineData(default(TypeParam))] + [InlineData(default(TypeParam))] + public virtual void PackToZyxBytes(TypeParam dummy) + where TColor : struct, IPixel + { + throw new NotImplementedException(); + } + + [Theory] + [InlineData(default(TypeParam))] + [InlineData(default(TypeParam))] + public virtual void PackFromZyxBytes(TypeParam dummy) + where TColor : struct, IPixel + { + throw new NotImplementedException(); + } + + [Theory] + [InlineData(default(TypeParam))] + [InlineData(default(TypeParam))] + public virtual void PackToZyxwBytes(TypeParam dummy) + where TColor : struct, IPixel + { + throw new NotImplementedException(); + } + + [Theory] + [InlineData(default(TypeParam))] + [InlineData(default(TypeParam))] + public virtual void PackFromZyxwBytes(TypeParam dummy) + where TColor : struct, IPixel + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Common/ArrayPointerTests.cs b/tests/ImageSharp.Tests/Common/ArrayPointerTests.cs index 076e2512c..916a10947 100644 --- a/tests/ImageSharp.Tests/Common/ArrayPointerTests.cs +++ b/tests/ImageSharp.Tests/Common/ArrayPointerTests.cs @@ -3,6 +3,7 @@ namespace ImageSharp.Tests.Common { using System; + using System.Runtime.CompilerServices; using Xunit; @@ -10,18 +11,16 @@ namespace ImageSharp.Tests.Common { public struct Foo { -#pragma warning disable CS0414 - private int a; + public int A; - private double b; -#pragma warning restore CS0414 + public double 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() { A = i, B = i }; } return result; } @@ -79,5 +78,90 @@ namespace ImageSharp.Tests.Common Assert.Equal((IntPtr)(p + totalOffset), ap.PointerAtOffset); } } + + public class Copy + { + [Theory] + [InlineData(4)] + [InlineData(1500)] + public void GenericToOwnType(int count) + { + Foo[] source = Foo.CreateArray(count + 2); + Foo[] dest = new Foo[count + 5]; + + fixed (Foo* pSource = source) + fixed (Foo* pDest = dest) + { + ArrayPointer apSource = new ArrayPointer(source, pSource); + ArrayPointer apDest = new ArrayPointer(dest, pDest); + + ArrayPointer.Copy(apSource, apDest, count); + } + + Assert.Equal(source[0], dest[0]); + 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]; + + fixed (Foo* pSource = source) + fixed (byte* pDest = dest) + { + ArrayPointer apSource = new ArrayPointer(source, pSource); + ArrayPointer apDest = new ArrayPointer(dest, pDest); + + ArrayPointer.Copy(apSource, apDest, count); + } + + Assert.True(ElementsAreEqual(source, dest, 0)); + Assert.True(ElementsAreEqual(source, dest, count - 1)); + Assert.False(ElementsAreEqual(source, dest, count)); + } + + [Theory] + [InlineData(4)] + [InlineData(1500)] + public void BytesToGeneric(int count) + { + int destCount = count * sizeof(Foo); + byte[] source = new byte[destCount + sizeof(Foo) + 1]; + Foo[] dest = Foo.CreateArray(count + 2); + + fixed(byte* pSource = source) + fixed (Foo* pDest = dest) + { + ArrayPointer apSource = new ArrayPointer(source, pSource); + ArrayPointer apDest = new ArrayPointer(dest, pDest); + + ArrayPointer.Copy(apSource, apDest, count); + } + + Assert.True(ElementsAreEqual(dest, source, 0)); + Assert.True(ElementsAreEqual(dest, source, count - 1)); + Assert.False(ElementsAreEqual(dest, source, count)); + } + + private static bool ElementsAreEqual(Foo[] array, byte[] rawArray, int index) + { + fixed (Foo* pArray = array) + fixed (byte* pRaw = rawArray) + { + Foo* pCasted = (Foo*)pRaw; + + Foo val1 = pArray[index]; + Foo val2 = pCasted[index]; + + return val1.Equals(val2); + } + } + } } } \ No newline at end of file From 78f997cd06195dabb27f31f3575b801f209287e6 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Thu, 2 Mar 2017 03:10:00 +0100 Subject: [PATCH 04/15] PinnedBuffer, better tests --- src/ImageSharp/Common/Memory/ArrayPointer.cs | 7 ++ src/ImageSharp/Common/Memory/PinnedBuffer.cs | 109 +++++++++++++++++ src/ImageSharp/Image/PixelPool{TColor}.cs | 1 + .../Colors/BulkPixelOperationsTests.cs | 114 +++++++++++------- .../Common/PinnedBufferTests.cs | 69 +++++++++++ 5 files changed, 257 insertions(+), 43 deletions(-) create mode 100644 src/ImageSharp/Common/Memory/PinnedBuffer.cs create mode 100644 tests/ImageSharp.Tests/Common/PinnedBufferTests.cs diff --git a/src/ImageSharp/Common/Memory/ArrayPointer.cs b/src/ImageSharp/Common/Memory/ArrayPointer.cs index c864d31fd..56cae35a4 100644 --- a/src/ImageSharp/Common/Memory/ArrayPointer.cs +++ b/src/ImageSharp/Common/Memory/ArrayPointer.cs @@ -7,6 +7,13 @@ namespace ImageSharp /// internal static class ArrayPointer { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe ArrayPointer GetArrayPointer(this PinnedBuffer buffer) + where T : struct + { + return new ArrayPointer(buffer.Array, (void*)buffer.Pointer); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void Copy(ArrayPointer source, ArrayPointer destination, int count) where T : struct diff --git a/src/ImageSharp/Common/Memory/PinnedBuffer.cs b/src/ImageSharp/Common/Memory/PinnedBuffer.cs new file mode 100644 index 000000000..c6e0c7c6f --- /dev/null +++ b/src/ImageSharp/Common/Memory/PinnedBuffer.cs @@ -0,0 +1,109 @@ +namespace ImageSharp +{ + using System; + using System.Buffers; + using System.Runtime.InteropServices; + + /// + /// Manages a pinned buffer of 'T' as a Disposable resource. + /// The backing array is either pooled or comes from the outside. + /// TODO: Should replace the pinning/dispose logic in several classes like or ! + /// + /// The value type. + internal class PinnedBuffer : IDisposable + where T : struct + { + private GCHandle handle; + + private bool isBufferRented; + + private bool isDisposed; + + /// + /// TODO: Consider reusing functionality of + /// + private static readonly ArrayPool ArrayPool = ArrayPool.Create(); + + /// + /// Initializes a new instance of the class. + /// + /// The desired count of elements. (Minimum size for ) + public PinnedBuffer(int count) + { + this.Count = count; + this.Array = ArrayPool.Rent(count); + this.isBufferRented = true; + this.Pin(); + } + + /// + /// Initializes a new instance of the class. + /// + /// The array to pin. + public PinnedBuffer(T[] array) + { + this.Count = array.Length; + this.Array = array; + this.Pin(); + } + + /// + /// The count of "relevant" elements. Usually be smaller than 'Array.Length' when is pooled. + /// + public int Count { get; private set; } + + /// + /// The (pinned) array of elements. + /// + public T[] Array { get; private set; } + + /// + /// Pointer to the pinned . + /// + public IntPtr Pointer { get; private set; } + + /// + /// Disposes the instance by unpinning the array, and returning the pooled buffer when necessary. + /// + public void Dispose() + { + if (this.isDisposed) + { + return; + } + this.isDisposed = true; + this.UnPin(); + + if (this.isBufferRented) + { + ArrayPool.Return(this.Array, true); + } + + this.Array = null; + this.Count = 0; + + GC.SuppressFinalize(this); + } + + private void Pin() + { + this.handle = GCHandle.Alloc(this.Array, GCHandleType.Pinned); + this.Pointer = this.handle.AddrOfPinnedObject(); + } + + private void UnPin() + { + if (this.Pointer == IntPtr.Zero || !this.handle.IsAllocated) + { + return; + } + this.handle.Free(); + this.Pointer = IntPtr.Zero; + } + + ~PinnedBuffer() + { + this.UnPin(); + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image/PixelPool{TColor}.cs b/src/ImageSharp/Image/PixelPool{TColor}.cs index 8193600da..ea6dad6b1 100644 --- a/src/ImageSharp/Image/PixelPool{TColor}.cs +++ b/src/ImageSharp/Image/PixelPool{TColor}.cs @@ -8,6 +8,7 @@ namespace ImageSharp using System; using System.Buffers; + // TODO: Consider refactoring this into a more general ClearPool, so we can use it in PinnedBuffer! /// /// Provides a resource pool that enables reusing instances of type . /// diff --git a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs index 413bd9451..f2081b943 100644 --- a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs @@ -1,104 +1,132 @@ namespace ImageSharp.Tests.Colors { using System; + using System.Numerics; using Xunit; - - public class BulkPixelOperationsTests + + public abstract class BulkPixelOperationsTests + where TColor : struct, IPixel { - public class TypeParam + public class ColorPixels : BulkPixelOperationsTests { } + public class ArgbPixels : BulkPixelOperationsTests + { + } + public static TheoryData ArraySizesData = new TheoryData { 7, 16, 1111 }; + [Theory] - [InlineData(default(TypeParam))] - [InlineData(default(TypeParam))] - public virtual void PackFromVector4(TypeParam dummy) - where TColor : struct, IPixel + [MemberData(nameof(ArraySizesData))] + public virtual void PackFromVector4(int count) { throw new NotImplementedException(); } [Theory] - [InlineData(default(TypeParam))] - [InlineData(default(TypeParam))] - public virtual void PackToVector4(TypeParam dummy) - where TColor : struct, IPixel + [MemberData(nameof(ArraySizesData))] + public virtual void PackToVector4(int count) { throw new NotImplementedException(); } [Theory] - [InlineData(default(TypeParam))] - [InlineData(default(TypeParam))] - public virtual void PackToXyzBytes(TypeParam dummy) - where TColor : struct, IPixel + [MemberData(nameof(ArraySizesData))] + public virtual void PackToXyzBytes(int count) { throw new NotImplementedException(); } [Theory] - [InlineData(default(TypeParam))] - [InlineData(default(TypeParam))] - public virtual void PackFromXyzBytes(TypeParam dummy) - where TColor : struct, IPixel + [MemberData(nameof(ArraySizesData))] + public virtual void PackFromXyzBytes(int count) { throw new NotImplementedException(); } [Theory] - [InlineData(default(TypeParam))] - [InlineData(default(TypeParam))] - public virtual void PackToXyzwBytes(TypeParam dummy) - where TColor : struct, IPixel + [MemberData(nameof(ArraySizesData))] + public virtual void PackToXyzwBytes(int count) { throw new NotImplementedException(); } [Theory] - [InlineData(default(TypeParam))] - [InlineData(default(TypeParam))] - public virtual void PackFromXyzwBytes(TypeParam dummy) - where TColor : struct, IPixel + [MemberData(nameof(ArraySizesData))] + public virtual void PackFromXyzwBytes(int count) { throw new NotImplementedException(); } [Theory] - [InlineData(default(TypeParam))] - [InlineData(default(TypeParam))] - public virtual void PackToZyxBytes(TypeParam dummy) - where TColor : struct, IPixel + [MemberData(nameof(ArraySizesData))] + public virtual void PackToZyxBytes(int count) { throw new NotImplementedException(); } [Theory] - [InlineData(default(TypeParam))] - [InlineData(default(TypeParam))] - public virtual void PackFromZyxBytes(TypeParam dummy) - where TColor : struct, IPixel + [MemberData(nameof(ArraySizesData))] + public virtual void PackFromZyxBytes(int count) { throw new NotImplementedException(); } [Theory] - [InlineData(default(TypeParam))] - [InlineData(default(TypeParam))] - public virtual void PackToZyxwBytes(TypeParam dummy) - where TColor : struct, IPixel + [MemberData(nameof(ArraySizesData))] + public virtual void PackToZyxwBytes(int count) { throw new NotImplementedException(); } [Theory] - [InlineData(default(TypeParam))] - [InlineData(default(TypeParam))] - public virtual void PackFromZyxwBytes(TypeParam dummy) - where TColor : struct, IPixel + [MemberData(nameof(ArraySizesData))] + public virtual void PackFromZyxwBytes(int count) { throw new NotImplementedException(); } + + public class TestBuffers + { + internal static PinnedBuffer Vector4(int length) + { + Vector4[] result = new Vector4[length]; + Random rnd = new Random(42); // Deterministic random values + + for (int i = 0; i < result.Length; i++) + { + result[i] = GetVector(rnd); + } + + return new PinnedBuffer(result); + } + + internal static PinnedBuffer Pixel(int length) + { + TColor[] result = new TColor[length]; + + Random rnd = new Random(42); // Deterministic random values + + for (int i = 0; i < result.Length; i++) + { + Vector4 v = GetVector(rnd); + result[i].PackFromVector4(v); + } + + return new PinnedBuffer(result); + } + + private static Vector4 GetVector(Random rnd) + { + return new Vector4( + (float)rnd.NextDouble(), + (float)rnd.NextDouble(), + (float)rnd.NextDouble(), + (float)rnd.NextDouble() + ); + } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs b/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs new file mode 100644 index 000000000..e0783f716 --- /dev/null +++ b/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs @@ -0,0 +1,69 @@ +namespace ImageSharp.Tests.Common +{ + using System; + using System.Runtime.CompilerServices; + using System.Runtime.InteropServices; + + using Xunit; + + public unsafe class PinnedBufferTests + { + public struct Foo + { + public int A; + + public double B; + } + + [Theory] + [InlineData(42)] + [InlineData(1111)] + public void ConstructWithOwnArray(int count) + { + using (PinnedBuffer buffer = new PinnedBuffer(count)) + { + Assert.NotNull(buffer.Array); + Assert.Equal(count, buffer.Count); + Assert.True(buffer.Array.Length >= count); + + VerifyPointer(buffer); + } + } + + [Theory] + [InlineData(42)] + [InlineData(1111)] + public void ConstructWithExistingArray(int count) + { + Foo[] array = new Foo[count]; + using (PinnedBuffer buffer = new PinnedBuffer(array)) + { + Assert.Equal(array, buffer.Array); + Assert.Equal(count, buffer.Count); + + VerifyPointer(buffer); + } + } + + [Fact] + public void GetArrayPointer() + { + Foo[] a = { new Foo() { A = 1, B = 2 }, new Foo() { A = 3, B = 4 } }; + + using (PinnedBuffer buffer = new PinnedBuffer(a)) + { + var arrayPtr = buffer.GetArrayPointer(); + + Assert.Equal(a, arrayPtr.Array); + Assert.Equal(0, arrayPtr.Offset); + Assert.Equal(buffer.Pointer, arrayPtr.PointerAtOffset); + } + } + + private static void VerifyPointer(PinnedBuffer buffer) + { + IntPtr ptr = (IntPtr)Unsafe.AsPointer(ref buffer.Array[0]); + Assert.Equal(ptr, buffer.Pointer); + } + } +} \ No newline at end of file From 61a2b2520d3c16cfe155a8a8e2211c0b9fa9bf2b Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 3 Mar 2017 01:58:56 +0100 Subject: [PATCH 05/15] better tests --- .../Colors/BulkPixelOperationsTests.cs | 197 ++++++++++++++---- 1 file changed, 156 insertions(+), 41 deletions(-) diff --git a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs index f2081b943..441b9daca 100644 --- a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs @@ -4,9 +4,8 @@ using System.Numerics; using Xunit; - - public abstract class BulkPixelOperationsTests - where TColor : struct, IPixel + + public abstract class BulkPixelOperationsTests { public class ColorPixels : BulkPixelOperationsTests { @@ -15,118 +14,234 @@ public class ArgbPixels : BulkPixelOperationsTests { } + } + public abstract class BulkPixelOperationsTests + where TColor : struct, IPixel + { public static TheoryData ArraySizesData = new TheoryData { 7, 16, 1111 }; [Theory] [MemberData(nameof(ArraySizesData))] - public virtual void PackFromVector4(int count) + public void PackFromVector4(int count) { - throw new NotImplementedException(); + Vector4[] source = CreateVector4TestData(count); + TColor[] expected = new TColor[count]; + + for (int i = 0; i < count; i++) + { + expected[i].PackFromVector4(source[i]); + } + + TestOperation( + source, + expected, + (ops, s, d) => ops.PackFromVector4(s, d, count) + ); } [Theory] [MemberData(nameof(ArraySizesData))] - public virtual void PackToVector4(int count) + public void PackToVector4(int count) { - throw new NotImplementedException(); + TColor[] source = CreatePixelTestData(count); + Vector4[] expected = new Vector4[count]; + + for (int i = 0; i < count; i++) + { + expected[i] = source[i].ToVector4(); + } + + TestOperation( + source, + expected, + (ops, s, d) => ops.PackToVector4(s, d, count) + ); } + [Theory] [MemberData(nameof(ArraySizesData))] - public virtual void PackToXyzBytes(int count) + public void PackFromXyzBytes(int count) { - throw new NotImplementedException(); + byte[] source = CreateByteTestData(count * 3); + TColor[] expected = new TColor[count]; + + for (int i = 0; i < count; i++) + { + int i3 = i * 3; + + expected[i].PackFromBytes(source[i3 + 0], source[i3 + 1], source[i3 + 2], 255); + } + + TestOperation( + source, + expected, + (ops, s, d) => ops.PackFromXyzBytes(s, d, count) + ); } [Theory] [MemberData(nameof(ArraySizesData))] - public virtual void PackFromXyzBytes(int count) + public void PackToXyzBytes(int count) { - throw new NotImplementedException(); + TColor[] source = CreatePixelTestData(count); + byte[] expected = new byte[count * 3]; + + for (int i = 0; i < count; i++) + { + int i3 = i * 3; + source[i].ToXyzBytes(expected, i3); + } + + TestOperation( + source, + expected, + (ops, s, d) => ops.PackToXyzBytes(s, d, count) + ); } + [Theory] [MemberData(nameof(ArraySizesData))] - public virtual void PackToXyzwBytes(int count) + public void PackToXyzwBytes(int count) { throw new NotImplementedException(); } [Theory] [MemberData(nameof(ArraySizesData))] - public virtual void PackFromXyzwBytes(int count) + public void PackFromXyzwBytes(int count) { throw new NotImplementedException(); } [Theory] [MemberData(nameof(ArraySizesData))] - public virtual void PackToZyxBytes(int count) + public void PackToZyxBytes(int count) { throw new NotImplementedException(); } [Theory] [MemberData(nameof(ArraySizesData))] - public virtual void PackFromZyxBytes(int count) + public void PackFromZyxBytes(int count) { throw new NotImplementedException(); } [Theory] [MemberData(nameof(ArraySizesData))] - public virtual void PackToZyxwBytes(int count) + public void PackToZyxwBytes(int count) { throw new NotImplementedException(); } [Theory] [MemberData(nameof(ArraySizesData))] - public virtual void PackFromZyxwBytes(int count) + public void PackFromZyxwBytes(int count) { throw new NotImplementedException(); } - - public class TestBuffers + + private class TestBuffers : IDisposable + where TSource : struct + where TDest : struct { - internal static PinnedBuffer Vector4(int length) + public PinnedBuffer SourceBuffer { get; } + public PinnedBuffer ActualDestBuffer { get; } + public PinnedBuffer ExpectedDestBuffer { get; } + + public ArrayPointer Source => this.SourceBuffer.GetArrayPointer(); + public ArrayPointer ActualDest => this.ActualDestBuffer.GetArrayPointer(); + + public TestBuffers(TSource[] source, TDest[] expectedDest) + { + this.SourceBuffer = new PinnedBuffer(source); + this.ExpectedDestBuffer = new PinnedBuffer(expectedDest); + this.ActualDestBuffer = new PinnedBuffer(expectedDest.Length); + } + + public void Dispose() { - Vector4[] result = new Vector4[length]; - Random rnd = new Random(42); // Deterministic random values + this.SourceBuffer.Dispose(); + this.ActualDestBuffer.Dispose(); + this.ExpectedDestBuffer.Dispose(); + } - for (int i = 0; i < result.Length; i++) + 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++) { - result[i] = GetVector(rnd); + Assert.Equal(expected[i], actual[i]); } + } + } - return new PinnedBuffer(result); + private static void TestOperation( + TSource[] source, + TDest[] expected, + Action, ArrayPointer, ArrayPointer> action) + where TSource : struct + where TDest : struct + { + using (var buffers = new TestBuffers(source, expected)) + { + action(BulkPixelOperations.Instance, buffers.Source, buffers.ActualDest); + buffers.Verify(); } + } - internal static PinnedBuffer Pixel(int length) + private static Vector4[] CreateVector4TestData(int length) + { + Vector4[] result = new Vector4[length]; + Random rnd = new Random(42); // Deterministic random values + + for (int i = 0; i < result.Length; i++) { - TColor[] result = new TColor[length]; + result[i] = GetVector(rnd); + } + return result; + } - Random rnd = new Random(42); // Deterministic random values + private static TColor[] CreatePixelTestData(int length) + { + TColor[] result = new TColor[length]; - for (int i = 0; i < result.Length; i++) - { - Vector4 v = GetVector(rnd); - result[i].PackFromVector4(v); - } + Random rnd = new Random(42); // Deterministic random values - return new PinnedBuffer(result); + for (int i = 0; i < result.Length; i++) + { + Vector4 v = GetVector(rnd); + result[i].PackFromVector4(v); } - private static Vector4 GetVector(Random rnd) + return result; + } + + private static byte[] CreateByteTestData(int length) + { + byte[] result = new byte[length]; + Random rnd = new Random(42); // Deterministic random values + + for (int i = 0; i < result.Length; i++) { - return new Vector4( - (float)rnd.NextDouble(), - (float)rnd.NextDouble(), - (float)rnd.NextDouble(), - (float)rnd.NextDouble() - ); + result[i] = (byte)rnd.Next(255); } + return result; + } + + private static Vector4 GetVector(Random rnd) + { + return new Vector4( + (float)rnd.NextDouble(), + (float)rnd.NextDouble(), + (float)rnd.NextDouble(), + (float)rnd.NextDouble() + ); } } } \ No newline at end of file From 93f169372b89f1b516db41680127aff1d41d0898 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 3 Mar 2017 02:46:26 +0100 Subject: [PATCH 06/15] started implementing operations --- .../Colors/PackedPixel/BulkPixelOperations.cs | 58 ++++++++++++++++++- .../Common/Memory/ArrayPointer{T}.cs | 21 +++++++ .../Colors/BulkPixelOperationsTests.cs | 6 +- .../Formats/Jpg/JpegProfilingBenchmarks.cs | 8 +-- 4 files changed, 84 insertions(+), 9 deletions(-) diff --git a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs index c914b3921..a0dceaded 100644 --- a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs +++ b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs @@ -1,17 +1,34 @@ namespace ImageSharp { + using System; using System.Numerics; + using System.Runtime.CompilerServices; public unsafe class BulkPixelOperations where TColor : struct, IPixel { public static BulkPixelOperations Instance { get; } = default(TColor).BulkOperations; + + private static readonly int ColorSize = Unsafe.SizeOf(); internal virtual void PackFromVector4( ArrayPointer sourceVectors, ArrayPointer destColors, int count) { + Vector4* sp = (Vector4*)sourceVectors.PointerAtOffset; + byte* dp = (byte*)destColors; + + for (int i = 0; i < count; i++) + { + Vector4 v = Unsafe.Read(sp); + TColor c = default(TColor); + c.PackFromVector4(v); + Unsafe.Write(dp, c); + + sp++; + dp += ColorSize; + } } internal virtual void PackToVector4( @@ -19,16 +36,51 @@ ArrayPointer destVectors, int count) { + byte* sp = (byte*)sourceColors; + Vector4* dp = (Vector4*)destVectors.PointerAtOffset; + + for (int i = 0; i < count; i++) + { + TColor c = Unsafe.Read(sp); + *dp = c.ToVector4(); + sp += ColorSize; + dp++; + } } - internal virtual void PackToXyzBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) + internal virtual void PackFromXyzBytes( + ArrayPointer sourceBytes, + ArrayPointer destColors, + int count) { + byte* sp = (byte*)sourceBytes; + byte* dp = (byte*)destColors.PointerAtOffset; + + for (int i = 0; i < count; i++) + { + TColor c = default(TColor); + c.PackFromBytes(sp[0], sp[1], sp[2], 255); + Unsafe.Write(dp, c); + sp += 3; + dp += ColorSize; + } } - internal virtual void PackFromXyzBytes(ArrayPointer sourceBytes, ArrayPointer destColors, int count) + internal virtual void PackToXyzBytes( + ArrayPointer sourceColors, + ArrayPointer destBytes, int count) { - } + byte* sp = (byte*)sourceColors; + + byte[] dest = destBytes.Array; + for (int i = destBytes.Offset; i < destBytes.Offset + count*3; i+=3) + { + TColor c = Unsafe.Read(sp); + c.ToXyzBytes(dest, i); + } + } + internal virtual void PackToXyzwBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) { } diff --git a/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs b/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs index 1ea7706d4..8f99327ba 100644 --- a/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs +++ b/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs @@ -73,6 +73,7 @@ namespace ImageSharp /// /// The offset in number of elements /// The offseted (sliced) ArrayPointer + [MethodImpl(MethodImplOptions.AggressiveInlining)] public ArrayPointer Slice(int offset) { ArrayPointer result = default(ArrayPointer); @@ -81,5 +82,25 @@ namespace ImageSharp result.PointerAtOffset = this.PointerAtOffset + (Unsafe.SizeOf() * offset); return result; } + + /// + /// Convertes instance to a raw 'void*' pointer + /// + /// The to convert + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator void*(ArrayPointer arrayPointer) + { + return (void*)arrayPointer.PointerAtOffset; + } + + /// + /// Convertes instance to a raw 'byte*' pointer + /// + /// The to convert + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static explicit operator byte* (ArrayPointer arrayPointer) + { + return (byte*)arrayPointer.PointerAtOffset; + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs index 441b9daca..472582310 100644 --- a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs @@ -7,12 +7,14 @@ public abstract class BulkPixelOperationsTests { - public class ColorPixels : BulkPixelOperationsTests + public class Color : BulkPixelOperationsTests { + public static TheoryData ArraySizesData = new TheoryData { 7, 16, 1111 }; } - public class ArgbPixels : BulkPixelOperationsTests + public class Argb : BulkPixelOperationsTests { + public static TheoryData ArraySizesData = new TheoryData { 7, 16, 1111 }; } } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs index 50e678bf0..12deda577 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs @@ -38,10 +38,10 @@ namespace ImageSharp.Tests { const int ExecutionCount = 30; - if (!Vector.IsHardwareAccelerated) - { - throw new Exception("Vector.IsHardwareAccelerated == false! ('prefer32 bit' enabled?)"); - } + //if (!Vector.IsHardwareAccelerated) + //{ + // throw new Exception("Vector.IsHardwareAccelerated == false! ('prefer32 bit' enabled?)"); + //} string path = TestFile.GetPath(fileName); byte[] bytes = File.ReadAllBytes(path); From 5ae01f1fa8f3b70a9c5f4fa123cfbd3c72fd7c9a Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Fri, 3 Mar 2017 03:05:06 +0100 Subject: [PATCH 07/15] fixed PackToXyzBytes --- src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs | 1 + tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj | 3 +++ tests/ImageSharp.Sandbox46/Program.cs | 3 --- .../Formats/Jpg/JpegProfilingBenchmarks.cs | 8 ++++---- 4 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs index a0dceaded..c1f6001af 100644 --- a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs +++ b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs @@ -78,6 +78,7 @@ { TColor c = Unsafe.Read(sp); c.ToXyzBytes(dest, i); + sp += ColorSize; } } diff --git a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj index d1b059f44..ad436793d 100644 --- a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj +++ b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj @@ -209,6 +209,9 @@ Benchmarks\PixelAccessorVirtualCopy.cs + + Tests\Colors\BulkPixelOperationsTests.cs + Tests\Drawing\PolygonTests.cs diff --git a/tests/ImageSharp.Sandbox46/Program.cs b/tests/ImageSharp.Sandbox46/Program.cs index f289ac2db..3afd18094 100644 --- a/tests/ImageSharp.Sandbox46/Program.cs +++ b/tests/ImageSharp.Sandbox46/Program.cs @@ -49,9 +49,6 @@ namespace ImageSharp.Sandbox46 benchmark.Setup(); benchmark.CopyRawUnsafeInlined(); - benchmark.CopyArrayPointerUnsafe(); - benchmark.CopyArrayPointerVirtualUnsafe(); - benchmark.CopyArrayPointerVirtualMarshal(); benchmark.Cleanup(); } diff --git a/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs b/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs index 12deda577..50e678bf0 100644 --- a/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs +++ b/tests/ImageSharp.Tests/Formats/Jpg/JpegProfilingBenchmarks.cs @@ -38,10 +38,10 @@ namespace ImageSharp.Tests { const int ExecutionCount = 30; - //if (!Vector.IsHardwareAccelerated) - //{ - // throw new Exception("Vector.IsHardwareAccelerated == false! ('prefer32 bit' enabled?)"); - //} + if (!Vector.IsHardwareAccelerated) + { + throw new Exception("Vector.IsHardwareAccelerated == false! ('prefer32 bit' enabled?)"); + } string path = TestFile.GetPath(fileName); byte[] bytes = File.ReadAllBytes(path); From 1d6dae9462b4f75a8f36b9beeae7a6542944d299 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 Mar 2017 00:47:40 +0100 Subject: [PATCH 08/15] default BulkPixelOperations passing --- .../Colors/PackedPixel/BulkPixelOperations.cs | 75 ++++++++++-- .../Colors/BulkPixelOperationsTests.cs | 109 +++++++++++++++--- 2 files changed, 162 insertions(+), 22 deletions(-) diff --git a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs index c1f6001af..ffa28fc13 100644 --- a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs +++ b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs @@ -71,7 +71,6 @@ ArrayPointer destBytes, int count) { byte* sp = (byte*)sourceColors; - byte[] dest = destBytes.Array; for (int i = destBytes.Offset; i < destBytes.Offset + count*3; i+=3) @@ -81,29 +80,89 @@ sp += ColorSize; } } - - internal virtual void PackToXyzwBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) - { - } internal virtual void PackFromXyzwBytes(ArrayPointer sourceBytes, ArrayPointer destColors, int count) { + byte* sp = (byte*)sourceBytes; + byte* dp = (byte*)destColors.PointerAtOffset; + + for (int i = 0; i < count; i++) + { + TColor c = default(TColor); + c.PackFromBytes(sp[0], sp[1], sp[2], sp[3]); + Unsafe.Write(dp, c); + sp += 4; + dp += ColorSize; + } } - - internal virtual void PackToZyxBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) + + internal virtual void PackToXyzwBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) { + byte* sp = (byte*)sourceColors; + byte[] dest = destBytes.Array; + + for (int i = destBytes.Offset; i < destBytes.Offset + count * 4; i += 4) + { + TColor c = Unsafe.Read(sp); + c.ToXyzwBytes(dest, i); + sp += ColorSize; + } } internal virtual void PackFromZyxBytes(ArrayPointer sourceBytes, ArrayPointer destColors, int count) { + byte* sp = (byte*)sourceBytes; + byte* dp = (byte*)destColors.PointerAtOffset; + + for (int i = 0; i < count; i++) + { + TColor c = default(TColor); + c.PackFromBytes(sp[2], sp[1], sp[0], 255); + Unsafe.Write(dp, c); + sp += 3; + dp += ColorSize; + } } - internal virtual void PackToZyxwBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) + internal virtual void PackToZyxBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) { + byte* sp = (byte*)sourceColors; + byte[] dest = destBytes.Array; + + for (int i = destBytes.Offset; i < destBytes.Offset + count * 3; i += 3) + { + TColor c = Unsafe.Read(sp); + c.ToZyxBytes(dest, i); + sp += ColorSize; + } } internal virtual void PackFromZyxwBytes(ArrayPointer sourceBytes, ArrayPointer destColors, int count) { + byte* sp = (byte*)sourceBytes; + byte* dp = (byte*)destColors.PointerAtOffset; + + for (int i = 0; i < count; i++) + { + TColor c = default(TColor); + c.PackFromBytes(sp[2], sp[1], sp[0], sp[3]); + Unsafe.Write(dp, c); + sp += 4; + dp += ColorSize; + } + } + + internal virtual void PackToZyxwBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) + { + byte* sp = (byte*)sourceColors; + byte[] dest = destBytes.Array; + + for (int i = destBytes.Offset; i < destBytes.Offset + count * 4; i += 4) + { + TColor c = Unsafe.Read(sp); + c.ToZyxwBytes(dest, i); + sp += ColorSize; + } } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs index 472582310..3682aa78a 100644 --- a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs @@ -21,7 +21,7 @@ public abstract class BulkPixelOperationsTests where TColor : struct, IPixel { - public static TheoryData ArraySizesData = new TheoryData { 7, 16, 1111 }; + protected static TheoryData ArraySizesData = new TheoryData { 7, 16, 1111 }; [Theory] [MemberData(nameof(ArraySizesData))] @@ -103,48 +103,129 @@ ); } - [Theory] [MemberData(nameof(ArraySizesData))] - public void PackToXyzwBytes(int count) + public void PackFromXyzwBytes(int count) { - throw new NotImplementedException(); + byte[] source = CreateByteTestData(count * 4); + TColor[] expected = new TColor[count]; + + for (int i = 0; i < count; i++) + { + int i4 = i * 4; + + expected[i].PackFromBytes(source[i4 + 0], source[i4 + 1], source[i4 + 2], source[i4 + 3]); + } + + TestOperation( + source, + expected, + (ops, s, d) => ops.PackFromXyzwBytes(s, d, count) + ); } [Theory] [MemberData(nameof(ArraySizesData))] - public void PackFromXyzwBytes(int count) + public void PackToXyzwBytes(int count) { - throw new NotImplementedException(); + TColor[] source = CreatePixelTestData(count); + byte[] expected = new byte[count * 4]; + + for (int i = 0; i < count; i++) + { + int i4 = i * 4; + source[i].ToXyzwBytes(expected, i4); + } + + TestOperation( + source, + expected, + (ops, s, d) => ops.PackToXyzwBytes(s, d, count) + ); } [Theory] [MemberData(nameof(ArraySizesData))] - public void PackToZyxBytes(int count) + public void PackFromZyxBytes(int count) { - throw new NotImplementedException(); + byte[] source = CreateByteTestData(count * 3); + TColor[] expected = new TColor[count]; + + for (int i = 0; i < count; i++) + { + int i3 = i * 3; + + expected[i].PackFromBytes(source[i3 + 2], source[i3 + 1], source[i3 + 0], 255); + } + + TestOperation( + source, + expected, + (ops, s, d) => ops.PackFromZyxBytes(s, d, count) + ); } [Theory] [MemberData(nameof(ArraySizesData))] - public void PackFromZyxBytes(int count) + public void PackToZyxBytes(int count) { - throw new NotImplementedException(); + TColor[] source = CreatePixelTestData(count); + byte[] expected = new byte[count * 3]; + + for (int i = 0; i < count; i++) + { + int i3 = i * 3; + source[i].ToZyxBytes(expected, i3); + } + + TestOperation( + source, + expected, + (ops, s, d) => ops.PackToZyxBytes(s, d, count) + ); } [Theory] [MemberData(nameof(ArraySizesData))] - public void PackToZyxwBytes(int count) + public void PackFromZyxwBytes(int count) { - throw new NotImplementedException(); + byte[] source = CreateByteTestData(count * 4); + TColor[] expected = new TColor[count]; + + for (int i = 0; i < count; i++) + { + int i4 = i * 4; + + expected[i].PackFromBytes(source[i4 + 2], source[i4 + 1], source[i4 + 0], source[i4 + 3]); + } + + TestOperation( + source, + expected, + (ops, s, d) => ops.PackFromZyxwBytes(s, d, count) + ); } [Theory] [MemberData(nameof(ArraySizesData))] - public void PackFromZyxwBytes(int count) + public void PackToZyxwBytes(int count) { - throw new NotImplementedException(); + TColor[] source = CreatePixelTestData(count); + byte[] expected = new byte[count * 4]; + + for (int i = 0; i < count; i++) + { + int i4 = i * 4; + source[i].ToZyxwBytes(expected, i4); + } + + TestOperation( + source, + expected, + (ops, s, d) => ops.PackToZyxwBytes(s, d, count) + ); } + private class TestBuffers : IDisposable where TSource : struct From 26b1715bf8854c5d15a3cce98116980075039684 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 Mar 2017 17:56:30 +0100 Subject: [PATCH 09/15] PixelAccessor and PixelArea using PinnedBuffer --- .../Colors/PackedPixel/BulkPixelOperations.cs | 168 ------------ .../BulkPixelOperations{TColor}.cs | 256 ++++++++++++++++++ .../Common/Memory/ArrayPointer{T}.cs | 32 +-- src/ImageSharp/Common/Memory/PinnedBuffer.cs | 89 ++++-- .../Common/Memory/PixelDataPool{T}.cs | 60 ++++ src/ImageSharp/Image/ImageBase{TColor}.cs | 10 +- src/ImageSharp/Image/PixelAccessor{TColor}.cs | 139 ++++------ src/ImageSharp/Image/PixelArea{TColor}.cs | 108 ++------ src/ImageSharp/Image/PixelPool{TColor}.cs | 43 --- .../ImageSharp.Sandbox46.csproj | 6 + .../Common/PinnedBufferTests.cs | 25 ++ .../ImageSharp.Tests/Image/PixelPoolTests.cs | 49 +++- 12 files changed, 552 insertions(+), 433 deletions(-) delete mode 100644 src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs create mode 100644 src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs create mode 100644 src/ImageSharp/Common/Memory/PixelDataPool{T}.cs delete mode 100644 src/ImageSharp/Image/PixelPool{TColor}.cs diff --git a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs deleted file mode 100644 index ffa28fc13..000000000 --- a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations.cs +++ /dev/null @@ -1,168 +0,0 @@ -namespace ImageSharp -{ - using System; - using System.Numerics; - using System.Runtime.CompilerServices; - - public unsafe class BulkPixelOperations - where TColor : struct, IPixel - { - public static BulkPixelOperations Instance { get; } = default(TColor).BulkOperations; - - private static readonly int ColorSize = Unsafe.SizeOf(); - - internal virtual void PackFromVector4( - ArrayPointer sourceVectors, - ArrayPointer destColors, - int count) - { - Vector4* sp = (Vector4*)sourceVectors.PointerAtOffset; - byte* dp = (byte*)destColors; - - for (int i = 0; i < count; i++) - { - Vector4 v = Unsafe.Read(sp); - TColor c = default(TColor); - c.PackFromVector4(v); - Unsafe.Write(dp, c); - - sp++; - dp += ColorSize; - } - } - - internal virtual void PackToVector4( - ArrayPointer sourceColors, - ArrayPointer destVectors, - int count) - { - byte* sp = (byte*)sourceColors; - Vector4* dp = (Vector4*)destVectors.PointerAtOffset; - - for (int i = 0; i < count; i++) - { - TColor c = Unsafe.Read(sp); - *dp = c.ToVector4(); - sp += ColorSize; - dp++; - } - } - - internal virtual void PackFromXyzBytes( - ArrayPointer sourceBytes, - ArrayPointer destColors, - int count) - { - byte* sp = (byte*)sourceBytes; - byte* dp = (byte*)destColors.PointerAtOffset; - - for (int i = 0; i < count; i++) - { - TColor c = default(TColor); - c.PackFromBytes(sp[0], sp[1], sp[2], 255); - Unsafe.Write(dp, c); - sp += 3; - dp += ColorSize; - } - } - - internal virtual void PackToXyzBytes( - ArrayPointer sourceColors, - ArrayPointer destBytes, int count) - { - byte* sp = (byte*)sourceColors; - byte[] dest = destBytes.Array; - - for (int i = destBytes.Offset; i < destBytes.Offset + count*3; i+=3) - { - TColor c = Unsafe.Read(sp); - c.ToXyzBytes(dest, i); - sp += ColorSize; - } - } - - internal virtual void PackFromXyzwBytes(ArrayPointer sourceBytes, ArrayPointer destColors, int count) - { - byte* sp = (byte*)sourceBytes; - byte* dp = (byte*)destColors.PointerAtOffset; - - for (int i = 0; i < count; i++) - { - TColor c = default(TColor); - c.PackFromBytes(sp[0], sp[1], sp[2], sp[3]); - Unsafe.Write(dp, c); - sp += 4; - dp += ColorSize; - } - } - - internal virtual void PackToXyzwBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) - { - byte* sp = (byte*)sourceColors; - byte[] dest = destBytes.Array; - - for (int i = destBytes.Offset; i < destBytes.Offset + count * 4; i += 4) - { - TColor c = Unsafe.Read(sp); - c.ToXyzwBytes(dest, i); - sp += ColorSize; - } - } - - internal virtual void PackFromZyxBytes(ArrayPointer sourceBytes, ArrayPointer destColors, int count) - { - byte* sp = (byte*)sourceBytes; - byte* dp = (byte*)destColors.PointerAtOffset; - - for (int i = 0; i < count; i++) - { - TColor c = default(TColor); - c.PackFromBytes(sp[2], sp[1], sp[0], 255); - Unsafe.Write(dp, c); - sp += 3; - dp += ColorSize; - } - } - - internal virtual void PackToZyxBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) - { - byte* sp = (byte*)sourceColors; - byte[] dest = destBytes.Array; - - for (int i = destBytes.Offset; i < destBytes.Offset + count * 3; i += 3) - { - TColor c = Unsafe.Read(sp); - c.ToZyxBytes(dest, i); - sp += ColorSize; - } - } - - internal virtual void PackFromZyxwBytes(ArrayPointer sourceBytes, ArrayPointer destColors, int count) - { - byte* sp = (byte*)sourceBytes; - byte* dp = (byte*)destColors.PointerAtOffset; - - for (int i = 0; i < count; i++) - { - TColor c = default(TColor); - c.PackFromBytes(sp[2], sp[1], sp[0], sp[3]); - Unsafe.Write(dp, c); - sp += 4; - dp += ColorSize; - } - } - - internal virtual void PackToZyxwBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) - { - byte* sp = (byte*)sourceColors; - byte[] dest = destBytes.Array; - - for (int i = destBytes.Offset; i < destBytes.Offset + count * 4; i += 4) - { - TColor c = Unsafe.Read(sp); - c.ToZyxwBytes(dest, i); - sp += ColorSize; - } - } - } -} \ No newline at end of file diff --git a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs new file mode 100644 index 000000000..557d59a16 --- /dev/null +++ b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs @@ -0,0 +1,256 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System.Numerics; + using System.Runtime.CompilerServices; + + /// + /// A stateless class implementing Strategy Pattern for batched pixel-data conversion operations + /// for pixel buffers of type . + /// + /// The pixel format. + public unsafe class BulkPixelOperations + where TColor : struct, IPixel + { + /// + /// The size of in bytes + /// + private static readonly int ColorSize = Unsafe.SizeOf(); + + /// + /// Gets the global instance for the pixel type + /// + public static BulkPixelOperations Instance { get; } = default(TColor).BulkOperations; + + /// + /// Bulk version of + /// + /// The to the source vectors. + /// The to the destination colors. + /// The number of pixels to convert. + internal virtual void PackFromVector4( + ArrayPointer sourceVectors, + ArrayPointer destColors, + int count) + { + Vector4* sp = (Vector4*)sourceVectors.PointerAtOffset; + byte* dp = (byte*)destColors; + + for (int i = 0; i < count; i++) + { + Vector4 v = Unsafe.Read(sp); + TColor c = default(TColor); + c.PackFromVector4(v); + Unsafe.Write(dp, c); + + sp++; + dp += ColorSize; + } + } + + /// + /// Bulk version of . + /// + /// The to the source colors. + /// The to the destination vectors. + /// The number of pixels to convert. + internal virtual void PackToVector4( + ArrayPointer sourceColors, + ArrayPointer destVectors, + int count) + { + byte* sp = (byte*)sourceColors; + Vector4* dp = (Vector4*)destVectors.PointerAtOffset; + + for (int i = 0; i < count; i++) + { + TColor c = Unsafe.Read(sp); + *dp = c.ToVector4(); + sp += ColorSize; + dp++; + } + } + + /// + /// Bulk version of that converts data in . + /// + /// + /// + /// + internal virtual void PackFromXyzBytes( + ArrayPointer sourceBytes, + ArrayPointer destColors, + int count) + { + byte* sp = (byte*)sourceBytes; + byte* dp = (byte*)destColors.PointerAtOffset; + + for (int i = 0; i < count; i++) + { + TColor c = default(TColor); + c.PackFromBytes(sp[0], sp[1], sp[2], 255); + Unsafe.Write(dp, c); + sp += 3; + dp += ColorSize; + } + } + + /// + /// Bulk version of . + /// + /// + /// + /// + internal virtual void PackToXyzBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) + { + byte* sp = (byte*)sourceColors; + byte[] dest = destBytes.Array; + + for (int i = destBytes.Offset; i < destBytes.Offset + count * 3; i += 3) + { + TColor c = Unsafe.Read(sp); + c.ToXyzBytes(dest, i); + sp += ColorSize; + } + } + + /// + /// Bulk version of that converts data in . + /// + /// + /// + /// + internal virtual void PackFromXyzwBytes( + ArrayPointer sourceBytes, + ArrayPointer destColors, + int count) + { + byte* sp = (byte*)sourceBytes; + byte* dp = (byte*)destColors.PointerAtOffset; + + for (int i = 0; i < count; i++) + { + TColor c = default(TColor); + c.PackFromBytes(sp[0], sp[1], sp[2], sp[3]); + Unsafe.Write(dp, c); + sp += 4; + dp += ColorSize; + } + } + + /// + /// Bulk version of . + /// + /// + /// + /// + internal virtual void PackToXyzwBytes( + ArrayPointer sourceColors, + ArrayPointer destBytes, + int count) + { + byte* sp = (byte*)sourceColors; + byte[] dest = destBytes.Array; + + for (int i = destBytes.Offset; i < destBytes.Offset + count * 4; i += 4) + { + TColor c = Unsafe.Read(sp); + c.ToXyzwBytes(dest, i); + sp += ColorSize; + } + } + + /// + /// Bulk version of that converts data in . + /// + /// + /// + /// + internal virtual void PackFromZyxBytes( + ArrayPointer sourceBytes, + ArrayPointer destColors, + int count) + { + byte* sp = (byte*)sourceBytes; + byte* dp = (byte*)destColors.PointerAtOffset; + + for (int i = 0; i < count; i++) + { + TColor c = default(TColor); + c.PackFromBytes(sp[2], sp[1], sp[0], 255); + Unsafe.Write(dp, c); + sp += 3; + dp += ColorSize; + } + } + + /// + /// Bulk version of . + /// + /// + /// + /// + internal virtual void PackToZyxBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) + { + byte* sp = (byte*)sourceColors; + byte[] dest = destBytes.Array; + + for (int i = destBytes.Offset; i < destBytes.Offset + count * 3; i += 3) + { + TColor c = Unsafe.Read(sp); + c.ToZyxBytes(dest, i); + sp += ColorSize; + } + } + + /// + /// Bulk version of that converts data in . + /// + /// + /// + /// + internal virtual void PackFromZyxwBytes( + ArrayPointer sourceBytes, + ArrayPointer destColors, + int count) + { + byte* sp = (byte*)sourceBytes; + byte* dp = (byte*)destColors.PointerAtOffset; + + for (int i = 0; i < count; i++) + { + TColor c = default(TColor); + c.PackFromBytes(sp[2], sp[1], sp[0], sp[3]); + Unsafe.Write(dp, c); + sp += 4; + dp += ColorSize; + } + } + + /// + /// Bulk version of . + /// + /// + /// + /// + internal virtual void PackToZyxwBytes( + ArrayPointer sourceColors, + ArrayPointer destBytes, + int count) + { + byte* sp = (byte*)sourceColors; + byte[] dest = destBytes.Array; + + for (int i = destBytes.Offset; i < destBytes.Offset + count * 4; i += 4) + { + TColor c = Unsafe.Read(sp); + c.ToZyxwBytes(dest, i); + sp += ColorSize; + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs b/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs index 8f99327ba..e0b728095 100644 --- a/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs +++ b/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs @@ -68,27 +68,12 @@ namespace ImageSharp /// public IntPtr PointerAtOffset { get; private set; } - /// - /// Forms a slice out of the given ArrayPointer, beginning at 'offset'. - /// - /// The offset in number of elements - /// The offseted (sliced) ArrayPointer - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ArrayPointer Slice(int offset) - { - ArrayPointer result = default(ArrayPointer); - result.Array = this.Array; - result.Offset = this.Offset + offset; - result.PointerAtOffset = this.PointerAtOffset + (Unsafe.SizeOf() * offset); - return result; - } - /// /// Convertes instance to a raw 'void*' pointer /// /// The to convert [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator void*(ArrayPointer arrayPointer) + public static explicit operator void* (ArrayPointer arrayPointer) { return (void*)arrayPointer.PointerAtOffset; } @@ -102,5 +87,20 @@ namespace ImageSharp { return (byte*)arrayPointer.PointerAtOffset; } + + /// + /// Forms a slice out of the given ArrayPointer, beginning at 'offset'. + /// + /// The offset in number of elements + /// The offseted (sliced) ArrayPointer + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ArrayPointer Slice(int offset) + { + ArrayPointer result = default(ArrayPointer); + result.Array = this.Array; + result.Offset = this.Offset + offset; + result.PointerAtOffset = this.PointerAtOffset + (Unsafe.SizeOf() * offset); + return result; + } } } \ No newline at end of file diff --git a/src/ImageSharp/Common/Memory/PinnedBuffer.cs b/src/ImageSharp/Common/Memory/PinnedBuffer.cs index c6e0c7c6f..201d93b56 100644 --- a/src/ImageSharp/Common/Memory/PinnedBuffer.cs +++ b/src/ImageSharp/Common/Memory/PinnedBuffer.cs @@ -5,25 +5,23 @@ namespace ImageSharp using System.Runtime.InteropServices; /// - /// Manages a pinned buffer of 'T' as a Disposable resource. + /// Manages a pinned buffer of value type data 'T' as a Disposable resource. /// The backing array is either pooled or comes from the outside. - /// TODO: Should replace the pinning/dispose logic in several classes like or ! /// /// The value type. internal class PinnedBuffer : IDisposable where T : struct { + /// + /// A handle that allows to access the managed as an unmanaged memory by pinning. + /// private GCHandle handle; - private bool isBufferRented; - - private bool isDisposed; - /// - /// TODO: Consider reusing functionality of + /// A value indicating wether this instance should return the array to the pool. /// - private static readonly ArrayPool ArrayPool = ArrayPool.Create(); - + private bool isPoolingOwner; + /// /// Initializes a new instance of the class. /// @@ -31,8 +29,8 @@ namespace ImageSharp public PinnedBuffer(int count) { this.Count = count; - this.Array = ArrayPool.Rent(count); - this.isBufferRented = true; + this.Array = PixelDataPool.Rent(count); + this.isPoolingOwner = true; this.Pin(); } @@ -48,7 +46,34 @@ namespace ImageSharp } /// - /// The count of "relevant" elements. Usually be smaller than 'Array.Length' when is pooled. + /// Initializes a new instance of the class. + /// + /// The count of "relevant" elements in 'array'. + /// The array to pin. + public PinnedBuffer(int count, T[] array) + { + if (array.Length < count) + { + throw new ArgumentException("Can't initialize a PinnedBuffer with array.Length < count", nameof(array)); + } + this.Count = count; + this.Array = array; + this.Pin(); + } + + ~PinnedBuffer() + { + this.UnPin(); + } + + /// + /// Gets a value indicating whether this instance is disposed, or has lost ownership of . + /// + public bool IsDisposedOrLostArrayOwnership { get; private set; } + + + /// + /// Gets the count of "relevant" elements. Usually be smaller than 'Array.Length' when is pooled. /// public int Count { get; private set; } @@ -58,7 +83,7 @@ namespace ImageSharp public T[] Array { get; private set; } /// - /// Pointer to the pinned . + /// Gets a pointer to the pinned . /// public IntPtr Pointer { get; private set; } @@ -67,16 +92,16 @@ namespace ImageSharp /// public void Dispose() { - if (this.isDisposed) + if (this.IsDisposedOrLostArrayOwnership) { return; } - this.isDisposed = true; + this.IsDisposedOrLostArrayOwnership = true; this.UnPin(); - if (this.isBufferRented) + if (this.isPoolingOwner) { - ArrayPool.Return(this.Array, true); + PixelDataPool.Return(this.Array); } this.Array = null; @@ -85,12 +110,37 @@ namespace ImageSharp GC.SuppressFinalize(this); } + /// + /// Unpins and makes the object "quasi-disposed" so the array is no longer owned by this object. + /// If is rented, it's the callers responsibility to return it to it's pool. (Most likely ) + /// + /// The unpinned + public T[] UnPinAndTakeArrayOwnership() + { + if (this.IsDisposedOrLostArrayOwnership) + { + throw new InvalidOperationException("UnPinAndTakeArrayOwnership() is invalid: either PinnedBuffer is disposed or UnPinAndTakeArrayOwnership() has been called multiple times!"); + } + + this.IsDisposedOrLostArrayOwnership = true; + this.UnPin(); + T[] array = this.Array; + this.Array = null; + return array; + } + + /// + /// Pins . + /// private void Pin() { this.handle = GCHandle.Alloc(this.Array, GCHandleType.Pinned); this.Pointer = this.handle.AddrOfPinnedObject(); } + /// + /// Unpins . + /// private void UnPin() { if (this.Pointer == IntPtr.Zero || !this.handle.IsAllocated) @@ -100,10 +150,5 @@ namespace ImageSharp this.handle.Free(); this.Pointer = IntPtr.Zero; } - - ~PinnedBuffer() - { - this.UnPin(); - } } } \ No newline at end of file diff --git a/src/ImageSharp/Common/Memory/PixelDataPool{T}.cs b/src/ImageSharp/Common/Memory/PixelDataPool{T}.cs new file mode 100644 index 000000000..f6f6a1042 --- /dev/null +++ b/src/ImageSharp/Common/Memory/PixelDataPool{T}.cs @@ -0,0 +1,60 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +namespace ImageSharp +{ + using System; + using System.Buffers; + + /// + /// Provides a resource pool that enables reusing instances of value type arrays . + /// will always return arrays initialized with 'default(T)' + /// + /// The value type. + public static class PixelDataPool + where T : struct + { + /// + /// The used to pool data. + /// + private static readonly ArrayPool ArrayPool = ArrayPool.Create(CalculateMaxArrayLength(), 50); + + /// + /// Rents the pixel array from the pool. + /// + /// The minimum length of the array to return. + /// The + public static T[] Rent(int minimumLength) + { + return ArrayPool.Rent(minimumLength); + } + + /// + /// Returns the rented pixel array back to the pool. + /// + /// The array to return to the buffer pool. + public static void Return(T[] array) + { + ArrayPool.Return(array, true); + } + + /// + /// Heuristically calculates a reasonable maxArrayLength value for the backing . + /// + /// The maxArrayLength value + internal static int CalculateMaxArrayLength() + { + if (typeof(IPixel).IsAssignableFrom(typeof(T))) + { + const int MaximumExpectedImageSize = 16384; + return MaximumExpectedImageSize * MaximumExpectedImageSize; + } + else + { + return int.MaxValue; + } + } + } +} \ No newline at end of file diff --git a/src/ImageSharp/Image/ImageBase{TColor}.cs b/src/ImageSharp/Image/ImageBase{TColor}.cs index e4b4485c7..9bd760805 100644 --- a/src/ImageSharp/Image/ImageBase{TColor}.cs +++ b/src/ImageSharp/Image/ImageBase{TColor}.cs @@ -162,13 +162,15 @@ namespace ImageSharp internal void SwapPixelsBuffers(PixelAccessor pixelSource) { Guard.NotNull(pixelSource, nameof(pixelSource)); - Guard.IsTrue(pixelSource.PooledMemory, nameof(pixelSource.PooledMemory), "pixelSource must be using pooled memory"); + + // TODO: This check was useful. We can introduce a bool PixelAccessor.IsBoundToImage to re-introduce it. + // Guard.IsTrue(pixelSource.PooledMemory, nameof(pixelSource.PooledMemory), "pixelSource must be using pooled memory"); int newWidth = pixelSource.Width; int newHeight = pixelSource.Height; // Push my memory into the accessor (which in turn unpins the old puffer ready for the images use) - TColor[] newPixels = pixelSource.ReturnCurrentPixelsAndReplaceThemInternally(this.Width, this.Height, this.pixelBuffer, true); + TColor[] newPixels = pixelSource.ReturnCurrentPixelsAndReplaceThemInternally(this.Width, this.Height, this.pixelBuffer); this.Width = newWidth; this.Height = newHeight; this.pixelBuffer = newPixels; @@ -222,7 +224,7 @@ namespace ImageSharp /// private void RentPixels() { - this.pixelBuffer = PixelPool.RentPixels(this.Width * this.Height); + this.pixelBuffer = PixelDataPool.Rent(this.Width * this.Height); } /// @@ -230,7 +232,7 @@ namespace ImageSharp /// private void ReturnPixels() { - PixelPool.ReturnPixels(this.pixelBuffer); + PixelDataPool.Return(this.pixelBuffer); this.pixelBuffer = null; } } diff --git a/src/ImageSharp/Image/PixelAccessor{TColor}.cs b/src/ImageSharp/Image/PixelAccessor{TColor}.cs index b31ada10b..3a3f0f5c7 100644 --- a/src/ImageSharp/Image/PixelAccessor{TColor}.cs +++ b/src/ImageSharp/Image/PixelAccessor{TColor}.cs @@ -18,21 +18,11 @@ namespace ImageSharp public unsafe class PixelAccessor : IDisposable where TColor : struct, IPixel { - /// - /// The pointer to the pixel buffer. - /// - private IntPtr dataPointer; - /// /// The position of the first pixel in the image. /// private byte* pixelsBase; - - /// - /// Provides a way to access the pixels from unmanaged memory. - /// - private GCHandle pixelsHandle; - + /// /// A value indicating whether this instance of the given entity has been disposed. /// @@ -45,9 +35,9 @@ namespace ImageSharp private bool isDisposed; /// - /// The pixel buffer + /// The containing the pixel data. /// - private TColor[] pixelBuffer; + private PinnedBuffer pixelBuffer; /// /// Initializes a new instance of the class. @@ -59,7 +49,7 @@ namespace ImageSharp Guard.MustBeGreaterThan(image.Width, 0, "image width"); Guard.MustBeGreaterThan(image.Height, 0, "image height"); - this.SetPixelBufferUnsafe(image.Width, image.Height, image.Pixels, false); + this.SetPixelBufferUnsafe(image.Width, image.Height, image.Pixels); this.ParallelOptions = image.Configuration.ParallelOptions; } @@ -70,7 +60,7 @@ namespace ImageSharp /// The height of the image represented by the pixel buffer. /// The pixel buffer. public PixelAccessor(int width, int height, TColor[] pixels) - : this(width, height, pixels, false) + : this(width, height, new PinnedBuffer(width * height, pixels)) { } @@ -80,7 +70,7 @@ namespace ImageSharp /// The width of the image represented by the pixel buffer. /// The height of the image represented by the pixel buffer. public PixelAccessor(int width, int height) - : this(width, height, PixelPool.RentPixels(width * height), true) + : this(width, height, new PinnedBuffer(width * height)) { } @@ -90,19 +80,18 @@ namespace ImageSharp /// The width of the image represented by the pixel buffer. /// The height of the image represented by the pixel buffer. /// The pixel buffer. - /// if set to true then the is from the thus should be returned once disposed. - private PixelAccessor(int width, int height, TColor[] pixels, bool pooledMemory) + private PixelAccessor(int width, int height, PinnedBuffer pixels) { Guard.NotNull(pixels, nameof(pixels)); Guard.MustBeGreaterThan(width, 0, nameof(width)); Guard.MustBeGreaterThan(height, 0, nameof(height)); - if (!(pixels.Length >= width * height)) - { - throw new ArgumentException($"Pixel array must have the length of at least {width * height}."); - } + //if (!(pixels.Length >= width * height)) + //{ + // throw new ArgumentException($"Pixel array must have the length of at least {width * height}."); + //} - this.SetPixelBufferUnsafe(width, height, pixels, pooledMemory); + this.SetPixelBufferUnsafe(width, height, pixels); this.ParallelOptions = Configuration.Default.ParallelOptions; } @@ -114,21 +103,16 @@ namespace ImageSharp { this.Dispose(); } - - /// - /// Gets a value indicating whether the current pixel buffer is from a pooled source. - /// - public bool PooledMemory { get; private set; } - + /// /// Gets the pixel buffer array. /// - public TColor[] PixelBuffer => this.pixelBuffer; + public TColor[] PixelBuffer => this.pixelBuffer.Array; /// /// Gets the pointer to the pixel buffer. /// - public IntPtr DataPointer => this.dataPointer; + public IntPtr DataPointer => this.pixelBuffer.Pointer; /// /// Gets the size of a single pixel in the number of bytes. @@ -246,24 +230,18 @@ namespace ImageSharp { return; } - - this.UnPinPixels(); - + // Note disposing is done. this.isDisposed = true; + this.pixelBuffer.Dispose(); + // This object will be cleaned up by the Dispose method. // Therefore, you should call GC.SuppressFinalize to // take this object off the finalization queue // and prevent finalization code for this object // from executing a second time. GC.SuppressFinalize(this); - - if (this.PooledMemory) - { - PixelPool.ReturnPixels(this.pixelBuffer); - this.pixelBuffer = null; - } } /// @@ -280,13 +258,12 @@ namespace ImageSharp /// The width. /// The height. /// The pixels. - /// If set to true this indicates that the pixel buffer is from a pooled source. /// Returns the old pixel data thats has gust been replaced. - /// If is true then caller is responsible for ensuring is called. - internal TColor[] ReturnCurrentPixelsAndReplaceThemInternally(int width, int height, TColor[] pixels, bool pooledMemory) + /// If is true then caller is responsible for ensuring is called. + internal TColor[] ReturnCurrentPixelsAndReplaceThemInternally(int width, int height, TColor[] pixels) { - TColor[] oldPixels = this.pixelBuffer; - this.SetPixelBufferUnsafe(width, height, pixels, pooledMemory); + TColor[] oldPixels = this.pixelBuffer.UnPinAndTakeArrayOwnership(); + this.SetPixelBufferUnsafe(width, height, pixels); return oldPixels; } @@ -514,53 +491,57 @@ namespace ImageSharp 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)); + } + /// /// Sets the pixel buffer in an unsafe manor this should not be used unless you know what its doing!!! /// /// The width. /// The height. - /// The pixels. - /// If set to true this indicates that the pixel buffer is from a pooled source. - private void SetPixelBufferUnsafe(int width, int height, TColor[] pixels, bool pooledMemory) + /// The pixel buffer + private void SetPixelBufferUnsafe(int width, int height, PinnedBuffer pixels) { this.pixelBuffer = pixels; - this.PooledMemory = pooledMemory; + this.pixelsBase = (byte*)pixels.Pointer; + this.Width = width; this.Height = height; - this.PinPixels(); this.PixelSize = Unsafe.SizeOf(); this.RowStride = this.Width * this.PixelSize; } - /// - /// Pins the pixels data. - /// - private void PinPixels() - { - // unpin any old pixels just incase - this.UnPinPixels(); - - this.pixelsHandle = GCHandle.Alloc(this.pixelBuffer, GCHandleType.Pinned); - this.dataPointer = this.pixelsHandle.AddrOfPinnedObject(); - this.pixelsBase = (byte*)this.dataPointer.ToPointer(); - } - - /// - /// Unpins pixels data. - /// - private void UnPinPixels() - { - if (this.pixelsBase != null) - { - if (this.pixelsHandle.IsAllocated) - { - this.pixelsHandle.Free(); - } - - this.dataPointer = IntPtr.Zero; - this.pixelsBase = null; - } - } + ///// + ///// Pins the pixels data. + ///// + //private void PinPixels() + //{ + // // unpin any old pixels just incase + // this.UnPinPixels(); + + // this.pixelsHandle = GCHandle.Alloc(this.pixelBuffer, GCHandleType.Pinned); + // this.dataPointer = this.pixelsHandle.AddrOfPinnedObject(); + // this.pixelsBase = (byte*)this.dataPointer.ToPointer(); + //} + + ///// + ///// Unpins pixels data. + ///// + //private void UnPinPixels() + //{ + // if (this.pixelsBase != null) + // { + // if (this.pixelsHandle.IsAllocated) + // { + // this.pixelsHandle.Free(); + // } + + // this.dataPointer = IntPtr.Zero; + // this.pixelsBase = null; + // } + //} /// /// Copy an area of pixels to the image. diff --git a/src/ImageSharp/Image/PixelArea{TColor}.cs b/src/ImageSharp/Image/PixelArea{TColor}.cs index 77b648ca5..25840167e 100644 --- a/src/ImageSharp/Image/PixelArea{TColor}.cs +++ b/src/ImageSharp/Image/PixelArea{TColor}.cs @@ -18,21 +18,6 @@ namespace ImageSharp public sealed unsafe class PixelArea : IDisposable where TColor : struct, IPixel { - /// - /// True if was rented from by the constructor - /// - private readonly bool isBufferRented; - - /// - /// Provides a way to access the pixels from unmanaged memory. - /// - private readonly GCHandle pixelsHandle; - - /// - /// The pointer to the pixel buffer. - /// - private IntPtr dataPointer; - /// /// A value indicating whether this instance of the given entity has been disposed. /// @@ -44,6 +29,11 @@ namespace ImageSharp /// private bool isDisposed; + /// + /// The underlying buffer containing the raw pixel data. + /// + private PinnedBuffer byteBuffer; + /// /// Initializes a new instance of the class. /// @@ -76,14 +66,11 @@ namespace ImageSharp this.Height = height; this.ComponentOrder = componentOrder; this.RowStride = width * GetComponentCount(componentOrder); - this.Bytes = bytes; - this.Length = bytes.Length; - this.isBufferRented = false; - this.pixelsHandle = GCHandle.Alloc(this.Bytes, GCHandleType.Pinned); + this.Length = bytes.Length; // TODO: Is this the right value for Length? - // TODO: Why is Resharper warning us about an impure method call? - this.dataPointer = this.pixelsHandle.AddrOfPinnedObject(); - this.PixelBase = (byte*)this.dataPointer.ToPointer(); + this.byteBuffer = new PinnedBuffer(bytes); + + this.PixelBase = (byte*)this.byteBuffer.Pointer; } /// @@ -132,27 +119,15 @@ namespace ImageSharp this.ComponentOrder = componentOrder; this.RowStride = (width * GetComponentCount(componentOrder)) + padding; this.Length = this.RowStride * height; - this.Bytes = BytesPool.Rent(this.Length); - this.isBufferRented = true; - this.pixelsHandle = GCHandle.Alloc(this.Bytes, GCHandleType.Pinned); - // TODO: Why is Resharper warning us about an impure method call? - this.dataPointer = this.pixelsHandle.AddrOfPinnedObject(); - this.PixelBase = (byte*)this.dataPointer.ToPointer(); + this.byteBuffer = new PinnedBuffer(this.Length); + this.PixelBase = (byte*)this.byteBuffer.Pointer; } - - /// - /// Finalizes an instance of the class. - /// - ~PixelArea() - { - this.Dispose(false); - } - + /// /// Gets the data in bytes. /// - public byte[] Bytes { get; } + public byte[] Bytes => this.byteBuffer.Array; /// /// Gets the length of the buffer. @@ -167,7 +142,7 @@ namespace ImageSharp /// /// Gets the pointer to the pixel buffer. /// - public IntPtr DataPointer => this.dataPointer; + public IntPtr DataPointer => this.byteBuffer.Pointer; /// /// Gets the height. @@ -188,26 +163,19 @@ namespace ImageSharp /// Gets the width. /// public int Width { get; } - - /// - /// Gets the pool used to rent bytes, when it's not coming from an external source. - /// - // TODO: Use own pool? - private static ArrayPool BytesPool => ArrayPool.Shared; - + /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// public void Dispose() { - this.Dispose(true); + if (this.isDisposed) + { + return; + } - // This object will be cleaned up by the Dispose method. - // Therefore, you should call GC.SuppressFinalize to - // take this object off the finalization queue - // and prevent finalization code for this object - // from executing a second time. - GC.SuppressFinalize(this); + this.byteBuffer.Dispose(); + this.isDisposed = true; } /// @@ -281,38 +249,6 @@ namespace ImageSharp nameof(bytes), $"Invalid byte array length. Length {bytes.Length}; Should be {requiredLength}."); } - } - - /// - /// Disposes the object and frees resources for the Garbage Collector. - /// - /// If true, the object gets disposed. - private void Dispose(bool disposing) - { - if (this.isDisposed) - { - return; - } - - if (this.PixelBase == null) - { - return; - } - - if (this.pixelsHandle.IsAllocated) - { - this.pixelsHandle.Free(); - } - - if (disposing && this.isBufferRented) - { - BytesPool.Return(this.Bytes); - } - - this.dataPointer = IntPtr.Zero; - this.PixelBase = null; - - this.isDisposed = true; - } + } } } \ No newline at end of file diff --git a/src/ImageSharp/Image/PixelPool{TColor}.cs b/src/ImageSharp/Image/PixelPool{TColor}.cs deleted file mode 100644 index ea6dad6b1..000000000 --- a/src/ImageSharp/Image/PixelPool{TColor}.cs +++ /dev/null @@ -1,43 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System; - using System.Buffers; - - // TODO: Consider refactoring this into a more general ClearPool, so we can use it in PinnedBuffer! - /// - /// Provides a resource pool that enables reusing instances of type . - /// - /// The pixel format. - public static class PixelPool - where TColor : struct, IPixel - { - /// - /// The used to pool data. TODO: Choose sensible default size and count - /// - private static readonly ArrayPool ArrayPool = ArrayPool.Create(int.MaxValue, 50); - - /// - /// Rents the pixel array from the pool. - /// - /// The minimum length of the array to return. - /// The - public static TColor[] RentPixels(int minimumLength) - { - return ArrayPool.Rent(minimumLength); - } - - /// - /// Returns the rented pixel array back to the pool. - /// - /// The array to return to the buffer pool. - public static void ReturnPixels(TColor[] array) - { - ArrayPool.Return(array, true); - } - } -} \ No newline at end of file diff --git a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj index ad436793d..094eedb18 100644 --- a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj +++ b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj @@ -212,6 +212,9 @@ Tests\Colors\BulkPixelOperationsTests.cs + + Tests\Common\PinnedBufferTests.cs + Tests\Drawing\PolygonTests.cs @@ -248,6 +251,9 @@ Tests\Formats\Jpg\YCbCrImageTests.cs + + Tests\Image\PixelPoolTests.cs + Tests\MetaData\ImagePropertyTests.cs diff --git a/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs b/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs index e0783f716..c5eb2a510 100644 --- a/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs +++ b/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs @@ -22,6 +22,7 @@ { using (PinnedBuffer buffer = new PinnedBuffer(count)) { + Assert.False(buffer.IsDisposedOrLostArrayOwnership); Assert.NotNull(buffer.Array); Assert.Equal(count, buffer.Count); Assert.True(buffer.Array.Length >= count); @@ -38,6 +39,7 @@ Foo[] array = new Foo[count]; using (PinnedBuffer buffer = new PinnedBuffer(array)) { + Assert.False(buffer.IsDisposedOrLostArrayOwnership); Assert.Equal(array, buffer.Array); Assert.Equal(count, buffer.Count); @@ -45,6 +47,15 @@ } } + [Fact] + public void Dispose() + { + PinnedBuffer buffer = new PinnedBuffer(42); + buffer.Dispose(); + + Assert.True(buffer.IsDisposedOrLostArrayOwnership); + } + [Fact] public void GetArrayPointer() { @@ -60,6 +71,20 @@ } } + [Fact] + public void UnPinAndTakeArrayOwnership() + { + Foo[] data = null; + using (PinnedBuffer buffer = new PinnedBuffer(42)) + { + data = buffer.UnPinAndTakeArrayOwnership(); + Assert.True(buffer.IsDisposedOrLostArrayOwnership); + } + + Assert.NotNull(data); + Assert.True(data.Length >= 42); + } + private static void VerifyPointer(PinnedBuffer buffer) { IntPtr ptr = (IntPtr)Unsafe.AsPointer(ref buffer.Array[0]); diff --git a/tests/ImageSharp.Tests/Image/PixelPoolTests.cs b/tests/ImageSharp.Tests/Image/PixelPoolTests.cs index 0b762cf7c..001785d60 100644 --- a/tests/ImageSharp.Tests/Image/PixelPoolTests.cs +++ b/tests/ImageSharp.Tests/Image/PixelPoolTests.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -10,54 +10,54 @@ namespace ImageSharp.Tests using Xunit; /// - /// Tests the class. + /// Tests the class. /// - public class PixelPoolTests + public class PixelDataPoolTests { [Fact] - public void PixelPoolRentsMinimumSize() + public void PixelDataPoolRentsMinimumSize() { - Color[] pixels = PixelPool.RentPixels(1024); + Color[] pixels = PixelDataPool.Rent(1024); Assert.True(pixels.Length >= 1024); } [Fact] - public void PixelPoolRentsEmptyArray() + public void PixelDataPoolRentsEmptyArray() { for (int i = 16; i < 1024; i += 16) { - Color[] pixels = PixelPool.RentPixels(i); + Color[] pixels = PixelDataPool.Rent(i); Assert.True(pixels.All(p => p == default(Color))); - PixelPool.ReturnPixels(pixels); + PixelDataPool.Return(pixels); } for (int i = 16; i < 1024; i += 16) { - Color[] pixels = PixelPool.RentPixels(i); + Color[] pixels = PixelDataPool.Rent(i); Assert.True(pixels.All(p => p == default(Color))); - PixelPool.ReturnPixels(pixels); + PixelDataPool.Return(pixels); } } [Fact] - public void PixelPoolDoesNotThrowWhenReturningNonPooled() + public void PixelDataPoolDoesNotThrowWhenReturningNonPooled() { Color[] pixels = new Color[1024]; - PixelPool.ReturnPixels(pixels); + PixelDataPool.Return(pixels); Assert.True(pixels.Length >= 1024); } [Fact] - public void PixelPoolCleansRentedArray() + public void PixelDataPoolCleansRentedArray() { - Color[] pixels = PixelPool.RentPixels(256); + Color[] pixels = PixelDataPool.Rent(256); for (int i = 0; i < pixels.Length; i++) { @@ -66,9 +66,28 @@ namespace ImageSharp.Tests Assert.True(pixels.All(p => p == Color.Azure)); - PixelPool.ReturnPixels(pixels); + PixelDataPool.Return(pixels); Assert.True(pixels.All(p => p == default(Color))); } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void CalculateMaxArrayLength(bool isRawData) + { + int max = isRawData ? PixelDataPool.CalculateMaxArrayLength() + : PixelDataPool.CalculateMaxArrayLength(); + + Assert.Equal(max < int.MaxValue, !isRawData); + } + + [Fact] + public void RentNonIPixelData() + { + byte[] data = PixelDataPool.Rent(16384); + + Assert.True(data.Length >= 16384); + } } } \ No newline at end of file From 6132d8fa4cfe879f1679bfe544547863e746bde8 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 Mar 2017 18:18:09 +0100 Subject: [PATCH 10/15] cleanup & stylecop --- .../BulkPixelOperations{TColor}.cs | 56 +++++++++---------- .../Colors/PackedPixel/HalfVector2.cs | 2 +- .../Colors/PackedPixel/NormalizedByte4.cs | 2 +- src/ImageSharp/Common/Memory/ArrayPointer.cs | 34 +++++++++++ .../Common/Memory/ArrayPointer{T}.cs | 4 +- .../{PinnedBuffer.cs => PinnedBuffer{T}.cs} | 16 +++++- .../Common/Memory/PixelDataPool{T}.cs | 2 +- src/ImageSharp/Image/ImageBase{TColor}.cs | 4 +- src/ImageSharp/Image/PixelAccessor{TColor}.cs | 41 +------------- src/ImageSharp/Image/PixelArea{TColor}.cs | 7 +-- .../Colors/BulkPixelOperationsTests.cs | 5 +- 11 files changed, 92 insertions(+), 81 deletions(-) rename src/ImageSharp/Common/Memory/{PinnedBuffer.cs => PinnedBuffer{T}.cs} (93%) diff --git a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs index 557d59a16..31f872e42 100644 --- a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs +++ b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs @@ -78,9 +78,9 @@ namespace ImageSharp /// /// Bulk version of that converts data in . /// - /// - /// - /// + /// The to the source bytes. + /// The to the destination colors. + /// The number of pixels to convert. internal virtual void PackFromXyzBytes( ArrayPointer sourceBytes, ArrayPointer destColors, @@ -102,15 +102,15 @@ namespace ImageSharp /// /// Bulk version of . /// - /// - /// - /// + /// The to the source colors. + /// The to the destination bytes. + /// The number of pixels to convert. internal virtual void PackToXyzBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) { byte* sp = (byte*)sourceColors; byte[] dest = destBytes.Array; - for (int i = destBytes.Offset; i < destBytes.Offset + count * 3; i += 3) + for (int i = destBytes.Offset; i < destBytes.Offset + (count * 3); i += 3) { TColor c = Unsafe.Read(sp); c.ToXyzBytes(dest, i); @@ -121,9 +121,9 @@ namespace ImageSharp /// /// Bulk version of that converts data in . /// - /// - /// - /// + /// The to the source bytes. + /// The to the destination colors. + /// The number of pixels to convert. internal virtual void PackFromXyzwBytes( ArrayPointer sourceBytes, ArrayPointer destColors, @@ -145,9 +145,9 @@ namespace ImageSharp /// /// Bulk version of . /// - /// - /// - /// + /// The to the source colors. + /// The to the destination bytes. + /// The number of pixels to convert. internal virtual void PackToXyzwBytes( ArrayPointer sourceColors, ArrayPointer destBytes, @@ -156,7 +156,7 @@ namespace ImageSharp byte* sp = (byte*)sourceColors; byte[] dest = destBytes.Array; - for (int i = destBytes.Offset; i < destBytes.Offset + count * 4; i += 4) + for (int i = destBytes.Offset; i < destBytes.Offset + (count * 4); i += 4) { TColor c = Unsafe.Read(sp); c.ToXyzwBytes(dest, i); @@ -167,9 +167,9 @@ namespace ImageSharp /// /// Bulk version of that converts data in . /// - /// - /// - /// + /// The to the source bytes. + /// The to the destination colors. + /// The number of pixels to convert. internal virtual void PackFromZyxBytes( ArrayPointer sourceBytes, ArrayPointer destColors, @@ -191,15 +191,15 @@ namespace ImageSharp /// /// Bulk version of . /// - /// - /// - /// + /// The to the source colors. + /// The to the destination bytes. + /// The number of pixels to convert. internal virtual void PackToZyxBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) { byte* sp = (byte*)sourceColors; byte[] dest = destBytes.Array; - for (int i = destBytes.Offset; i < destBytes.Offset + count * 3; i += 3) + for (int i = destBytes.Offset; i < destBytes.Offset + (count * 3); i += 3) { TColor c = Unsafe.Read(sp); c.ToZyxBytes(dest, i); @@ -210,9 +210,9 @@ namespace ImageSharp /// /// Bulk version of that converts data in . /// - /// - /// - /// + /// The to the source bytes. + /// The to the destination colors. + /// The number of pixels to convert. internal virtual void PackFromZyxwBytes( ArrayPointer sourceBytes, ArrayPointer destColors, @@ -234,9 +234,9 @@ namespace ImageSharp /// /// Bulk version of . /// - /// - /// - /// + /// The to the source colors. + /// The to the destination bytes. + /// The number of pixels to convert. internal virtual void PackToZyxwBytes( ArrayPointer sourceColors, ArrayPointer destBytes, @@ -245,7 +245,7 @@ namespace ImageSharp byte* sp = (byte*)sourceColors; byte[] dest = destBytes.Array; - for (int i = destBytes.Offset; i < destBytes.Offset + count * 4; i += 4) + for (int i = destBytes.Offset; i < destBytes.Offset + (count * 4); i += 4) { TColor c = Unsafe.Read(sp); c.ToZyxwBytes(dest, i); diff --git a/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs b/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs index 778f86e0f..68cb42735 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs @@ -45,7 +45,7 @@ namespace ImageSharp /// public uint PackedValue { get; set; } - + /// public BulkPixelOperations BulkOperations => new BulkPixelOperations(); diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs index cba3f0e86..4511060e4 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs @@ -52,7 +52,7 @@ namespace ImageSharp /// public uint PackedValue { get; set; } - + /// public BulkPixelOperations BulkOperations => new BulkPixelOperations(); diff --git a/src/ImageSharp/Common/Memory/ArrayPointer.cs b/src/ImageSharp/Common/Memory/ArrayPointer.cs index 56cae35a4..342eaa6c8 100644 --- a/src/ImageSharp/Common/Memory/ArrayPointer.cs +++ b/src/ImageSharp/Common/Memory/ArrayPointer.cs @@ -1,3 +1,8 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + namespace ImageSharp { using System.Runtime.CompilerServices; @@ -7,6 +12,12 @@ namespace ImageSharp /// internal static class ArrayPointer { + /// + /// Gets an to the beginning of the raw data in 'buffer'. + /// + /// The element type + /// The input + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe ArrayPointer GetArrayPointer(this PinnedBuffer buffer) where T : struct @@ -14,6 +25,13 @@ namespace ImageSharp return new ArrayPointer(buffer.Array, (void*)buffer.Pointer); } + /// + /// Copy 'count' number of elements of the same type from 'source' to 'dest' + /// + /// The element type. + /// The input + /// The destination . + /// The number of elements to copy [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void Copy(ArrayPointer source, ArrayPointer destination, int count) where T : struct @@ -21,6 +39,13 @@ namespace ImageSharp Unsafe.CopyBlock((void*)source.PointerAtOffset, (void*)destination.PointerAtOffset, USizeOf(count)); } + /// + /// Copy 'countInSource' elements of from 'source' into the raw byte buffer 'destination'. + /// + /// The element type. + /// The source buffer of elements to copy from. + /// The destination buffer. + /// The number of elements to copy from 'source' [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void Copy(ArrayPointer source, ArrayPointer destination, int countInSource) where T : struct @@ -28,6 +53,13 @@ namespace ImageSharp Unsafe.CopyBlock((void*)source.PointerAtOffset, (void*)destination.PointerAtOffset, USizeOf(countInSource)); } + /// + /// Copy 'countInDest' number of elements into 'dest' from a raw byte buffer defined by 'source'. + /// + /// The element type. + /// The raw source buffer to copy from"/> + /// The destination buffer"/> + /// The number of elements to copy. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static unsafe void Copy(ArrayPointer source, ArrayPointer destination, int countInDest) where T : struct @@ -38,6 +70,7 @@ namespace ImageSharp /// /// Gets the size of `count` elements in bytes. /// + /// The element type. /// The count of the elements /// The size in bytes as int [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -47,6 +80,7 @@ namespace ImageSharp /// /// Gets the size of `count` elements in bytes as UInt32 /// + /// The element type. /// The count of the elements /// The size in bytes as UInt32 [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs b/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs index e0b728095..9cbbaf094 100644 --- a/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs +++ b/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs @@ -73,7 +73,7 @@ namespace ImageSharp /// /// The to convert [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator void* (ArrayPointer arrayPointer) + public static explicit operator void*(ArrayPointer arrayPointer) { return (void*)arrayPointer.PointerAtOffset; } @@ -83,7 +83,7 @@ namespace ImageSharp /// /// The to convert [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator byte* (ArrayPointer arrayPointer) + public static explicit operator byte*(ArrayPointer arrayPointer) { return (byte*)arrayPointer.PointerAtOffset; } diff --git a/src/ImageSharp/Common/Memory/PinnedBuffer.cs b/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs similarity index 93% rename from src/ImageSharp/Common/Memory/PinnedBuffer.cs rename to src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs index 201d93b56..d95d67557 100644 --- a/src/ImageSharp/Common/Memory/PinnedBuffer.cs +++ b/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs @@ -1,3 +1,8 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + namespace ImageSharp { using System; @@ -21,7 +26,7 @@ namespace ImageSharp /// A value indicating wether this instance should return the array to the pool. /// private bool isPoolingOwner; - + /// /// Initializes a new instance of the class. /// @@ -56,11 +61,15 @@ namespace ImageSharp { throw new ArgumentException("Can't initialize a PinnedBuffer with array.Length < count", nameof(array)); } + this.Count = count; this.Array = array; this.Pin(); } + /// + /// Finalizes an instance of the class. + /// ~PinnedBuffer() { this.UnPin(); @@ -71,14 +80,13 @@ namespace ImageSharp /// public bool IsDisposedOrLostArrayOwnership { get; private set; } - /// /// Gets the count of "relevant" elements. Usually be smaller than 'Array.Length' when is pooled. /// public int Count { get; private set; } /// - /// The (pinned) array of elements. + /// Gets the backing pinned array. /// public T[] Array { get; private set; } @@ -96,6 +104,7 @@ namespace ImageSharp { return; } + this.IsDisposedOrLostArrayOwnership = true; this.UnPin(); @@ -147,6 +156,7 @@ namespace ImageSharp { return; } + this.handle.Free(); this.Pointer = IntPtr.Zero; } diff --git a/src/ImageSharp/Common/Memory/PixelDataPool{T}.cs b/src/ImageSharp/Common/Memory/PixelDataPool{T}.cs index f6f6a1042..74f0c1e8e 100644 --- a/src/ImageSharp/Common/Memory/PixelDataPool{T}.cs +++ b/src/ImageSharp/Common/Memory/PixelDataPool{T}.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // diff --git a/src/ImageSharp/Image/ImageBase{TColor}.cs b/src/ImageSharp/Image/ImageBase{TColor}.cs index 9bd760805..c2110eca4 100644 --- a/src/ImageSharp/Image/ImageBase{TColor}.cs +++ b/src/ImageSharp/Image/ImageBase{TColor}.cs @@ -163,9 +163,9 @@ namespace ImageSharp { Guard.NotNull(pixelSource, nameof(pixelSource)); - // TODO: This check was useful. We can introduce a bool PixelAccessor.IsBoundToImage to re-introduce it. - // Guard.IsTrue(pixelSource.PooledMemory, nameof(pixelSource.PooledMemory), "pixelSource must be using pooled memory"); + // TODO: Was this check really useful? If yes, we can define a bool PixelAccessor.IsBoundToImage to re-introduce it: + // Guard.IsTrue(pixelSource.PooledMemory, nameof(pixelSource.PooledMemory), "pixelSource must be using pooled memory"); int newWidth = pixelSource.Width; int newHeight = pixelSource.Height; diff --git a/src/ImageSharp/Image/PixelAccessor{TColor}.cs b/src/ImageSharp/Image/PixelAccessor{TColor}.cs index 3a3f0f5c7..9201270d9 100644 --- a/src/ImageSharp/Image/PixelAccessor{TColor}.cs +++ b/src/ImageSharp/Image/PixelAccessor{TColor}.cs @@ -22,7 +22,7 @@ namespace ImageSharp /// The position of the first pixel in the image. /// private byte* pixelsBase; - + /// /// A value indicating whether this instance of the given entity has been disposed. /// @@ -86,11 +86,6 @@ namespace ImageSharp Guard.MustBeGreaterThan(width, 0, nameof(width)); Guard.MustBeGreaterThan(height, 0, nameof(height)); - //if (!(pixels.Length >= width * height)) - //{ - // throw new ArgumentException($"Pixel array must have the length of at least {width * height}."); - //} - this.SetPixelBufferUnsafe(width, height, pixels); this.ParallelOptions = Configuration.Default.ParallelOptions; @@ -103,7 +98,7 @@ namespace ImageSharp { this.Dispose(); } - + /// /// Gets the pixel buffer array. /// @@ -230,7 +225,7 @@ namespace ImageSharp { return; } - + // Note disposing is done. this.isDisposed = true; @@ -513,36 +508,6 @@ namespace ImageSharp this.RowStride = this.Width * this.PixelSize; } - ///// - ///// Pins the pixels data. - ///// - //private void PinPixels() - //{ - // // unpin any old pixels just incase - // this.UnPinPixels(); - - // this.pixelsHandle = GCHandle.Alloc(this.pixelBuffer, GCHandleType.Pinned); - // this.dataPointer = this.pixelsHandle.AddrOfPinnedObject(); - // this.pixelsBase = (byte*)this.dataPointer.ToPointer(); - //} - - ///// - ///// Unpins pixels data. - ///// - //private void UnPinPixels() - //{ - // if (this.pixelsBase != null) - // { - // if (this.pixelsHandle.IsAllocated) - // { - // this.pixelsHandle.Free(); - // } - - // this.dataPointer = IntPtr.Zero; - // this.pixelsBase = null; - // } - //} - /// /// Copy an area of pixels to the image. /// diff --git a/src/ImageSharp/Image/PixelArea{TColor}.cs b/src/ImageSharp/Image/PixelArea{TColor}.cs index 25840167e..c54de12d6 100644 --- a/src/ImageSharp/Image/PixelArea{TColor}.cs +++ b/src/ImageSharp/Image/PixelArea{TColor}.cs @@ -69,7 +69,6 @@ namespace ImageSharp this.Length = bytes.Length; // TODO: Is this the right value for Length? this.byteBuffer = new PinnedBuffer(bytes); - this.PixelBase = (byte*)this.byteBuffer.Pointer; } @@ -123,7 +122,7 @@ namespace ImageSharp this.byteBuffer = new PinnedBuffer(this.Length); this.PixelBase = (byte*)this.byteBuffer.Pointer; } - + /// /// Gets the data in bytes. /// @@ -163,7 +162,7 @@ namespace ImageSharp /// Gets the width. /// public int Width { get; } - + /// /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// @@ -249,6 +248,6 @@ namespace ImageSharp nameof(bytes), $"Invalid byte array length. Length {bytes.Length}; Should be {requiredLength}."); } - } + } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs index 3682aa78a..4e1083033 100644 --- a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs @@ -9,11 +9,13 @@ { public class Color : BulkPixelOperationsTests { + // MemberData does not work without redeclaring the public field in the derived test class: public static TheoryData ArraySizesData = new TheoryData { 7, 16, 1111 }; } public class Argb : BulkPixelOperationsTests { + // MemberData does not work without redeclaring the public field in the derived test class: public static TheoryData ArraySizesData = new TheoryData { 7, 16, 1111 }; } } @@ -21,7 +23,8 @@ public abstract class BulkPixelOperationsTests where TColor : struct, IPixel { - protected static TheoryData ArraySizesData = new TheoryData { 7, 16, 1111 }; + // The field is private so it can be safely redeclared in inherited non-abstract test classes. + private static TheoryData ArraySizesData = new TheoryData { 7, 16, 1111 }; [Theory] [MemberData(nameof(ArraySizesData))] From 8a08319ce0d8b146410a85eff45d7c64aecbef3e Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 Mar 2017 18:32:06 +0100 Subject: [PATCH 11/15] build and testrunning fix --- src/ImageSharp/Common/Memory/PixelDataPool{T}.cs | 3 ++- .../Colors/BulkPixelOperationsTests.cs | 11 +++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/ImageSharp/Common/Memory/PixelDataPool{T}.cs b/src/ImageSharp/Common/Memory/PixelDataPool{T}.cs index 74f0c1e8e..a97d17fdb 100644 --- a/src/ImageSharp/Common/Memory/PixelDataPool{T}.cs +++ b/src/ImageSharp/Common/Memory/PixelDataPool{T}.cs @@ -46,7 +46,8 @@ namespace ImageSharp /// The maxArrayLength value internal static int CalculateMaxArrayLength() { - if (typeof(IPixel).IsAssignableFrom(typeof(T))) + // ReSharper disable once SuspiciousTypeConversion.Global + if (default(T) is IPixel) { const int MaximumExpectedImageSize = 16384; return MaximumExpectedImageSize * MaximumExpectedImageSize; diff --git a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs index 4e1083033..0e69a54f5 100644 --- a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs @@ -9,22 +9,21 @@ { public class Color : BulkPixelOperationsTests { - // MemberData does not work without redeclaring the public field in the derived test class: - public static TheoryData ArraySizesData = new TheoryData { 7, 16, 1111 }; + // 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 }; } public class Argb : BulkPixelOperationsTests { - // MemberData does not work without redeclaring the public field in the derived test class: - public static TheoryData ArraySizesData = new TheoryData { 7, 16, 1111 }; + // 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 }; } } public abstract class BulkPixelOperationsTests where TColor : struct, IPixel { - // The field is private so it can be safely redeclared in inherited non-abstract test classes. - private static TheoryData ArraySizesData = new TheoryData { 7, 16, 1111 }; + public static TheoryData ArraySizesData => new TheoryData { 7, 16, 1111 }; [Theory] [MemberData(nameof(ArraySizesData))] From 4e54632d631417fd49b395401b8d96f95640f694 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 Mar 2017 19:25:31 +0100 Subject: [PATCH 12/15] BulkPixelOperationsTests.GetGlobalInstance() to make CodeCov happy --- .../Colors/BulkPixelOperationsTests.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs index 0e69a54f5..c179b01a1 100644 --- a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs @@ -5,7 +5,7 @@ using Xunit; - public abstract class BulkPixelOperationsTests + public class BulkPixelOperationsTests { public class Color : BulkPixelOperationsTests { @@ -18,6 +18,14 @@ // 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 }; } + + [Theory] + [WithBlankImages(1, 1, PixelTypes.All)] + public void GetGlobalInstance(TestImageProvider dummy) + where TColor:struct, IPixel + { + Assert.NotNull(BulkPixelOperations.Instance); + } } public abstract class BulkPixelOperationsTests From 4b11becfff2d487d122824ccb89a9a1acf44bccd Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Sun, 5 Mar 2017 21:18:31 +0100 Subject: [PATCH 13/15] IPixel.BulkOperations ---> IPixel.CreateBulkOperations() --- src/ImageSharp/Colors/Color.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/Alpha8.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/Argb.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/Bgr565.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/Bgra4444.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/Bgra5551.cs | 6 +++--- .../Colors/PackedPixel/BulkPixelOperations{TColor}.cs | 2 +- src/ImageSharp/Colors/PackedPixel/Byte4.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/HalfSingle.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/HalfVector2.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/HalfVector4.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/IPixel.cs | 6 ++++-- src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/Rg32.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/Rgba64.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/Short2.cs | 6 +++--- src/ImageSharp/Colors/PackedPixel/Short4.cs | 6 +++--- 21 files changed, 62 insertions(+), 60 deletions(-) diff --git a/src/ImageSharp/Colors/Color.cs b/src/ImageSharp/Colors/Color.cs index 8a869935c..1ad6f9a64 100644 --- a/src/ImageSharp/Colors/Color.cs +++ b/src/ImageSharp/Colors/Color.cs @@ -112,9 +112,6 @@ namespace ImageSharp this.packedValue = packed; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Gets or sets the red component. /// @@ -248,6 +245,9 @@ namespace ImageSharp return ColorBuilder.FromHex(hex); } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromBytes(byte x, byte y, byte z, byte w) diff --git a/src/ImageSharp/Colors/PackedPixel/Alpha8.cs b/src/ImageSharp/Colors/PackedPixel/Alpha8.cs index 1181eb9e4..9a340544c 100644 --- a/src/ImageSharp/Colors/PackedPixel/Alpha8.cs +++ b/src/ImageSharp/Colors/PackedPixel/Alpha8.cs @@ -26,9 +26,6 @@ namespace ImageSharp /// public byte PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -61,6 +58,9 @@ namespace ImageSharp return left.PackedValue != right.PackedValue; } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) diff --git a/src/ImageSharp/Colors/PackedPixel/Argb.cs b/src/ImageSharp/Colors/PackedPixel/Argb.cs index 1b97d2337..70fd7de8a 100644 --- a/src/ImageSharp/Colors/PackedPixel/Argb.cs +++ b/src/ImageSharp/Colors/PackedPixel/Argb.cs @@ -109,9 +109,6 @@ namespace ImageSharp /// public uint PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Gets or sets the red component. /// @@ -223,6 +220,9 @@ namespace ImageSharp this.PackedValue = Pack(ref vector); } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() diff --git a/src/ImageSharp/Colors/PackedPixel/Bgr565.cs b/src/ImageSharp/Colors/PackedPixel/Bgr565.cs index 41b2bdc2e..77d943478 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgr565.cs +++ b/src/ImageSharp/Colors/PackedPixel/Bgr565.cs @@ -39,9 +39,6 @@ namespace ImageSharp /// public ushort PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -70,6 +67,9 @@ namespace ImageSharp return left.PackedValue != right.PackedValue; } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// /// Expands the packed representation into a . /// The vector components are typically expanded in least to greatest significance order. diff --git a/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs b/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs index 99659a36b..1346a54ef 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs +++ b/src/ImageSharp/Colors/PackedPixel/Bgra4444.cs @@ -38,9 +38,6 @@ namespace ImageSharp /// public ushort PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -69,6 +66,9 @@ namespace ImageSharp return left.PackedValue != right.PackedValue; } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() diff --git a/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs b/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs index 86864fd48..7989804cf 100644 --- a/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs +++ b/src/ImageSharp/Colors/PackedPixel/Bgra5551.cs @@ -40,9 +40,6 @@ namespace ImageSharp /// public ushort PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -71,6 +68,9 @@ namespace ImageSharp return left.PackedValue != right.PackedValue; } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() diff --git a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs index 31f872e42..986994af7 100644 --- a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs +++ b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs @@ -24,7 +24,7 @@ namespace ImageSharp /// /// Gets the global instance for the pixel type /// - public static BulkPixelOperations Instance { get; } = default(TColor).BulkOperations; + public static BulkPixelOperations Instance { get; } = default(TColor).CreateBulkOperations(); /// /// Bulk version of diff --git a/src/ImageSharp/Colors/PackedPixel/Byte4.cs b/src/ImageSharp/Colors/PackedPixel/Byte4.cs index 5712027d9..11ec5eaf4 100644 --- a/src/ImageSharp/Colors/PackedPixel/Byte4.cs +++ b/src/ImageSharp/Colors/PackedPixel/Byte4.cs @@ -41,9 +41,6 @@ namespace ImageSharp /// public uint PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -72,6 +69,9 @@ namespace ImageSharp return left.PackedValue != right.PackedValue; } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) diff --git a/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs b/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs index 0bc82c3a6..4c785a863 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfSingle.cs @@ -36,9 +36,6 @@ namespace ImageSharp /// public ushort PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -75,6 +72,9 @@ namespace ImageSharp return left.PackedValue != right.PackedValue; } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// /// Expands the packed representation into a . /// diff --git a/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs b/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs index 68cb42735..d06ab6ba0 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfVector2.cs @@ -46,9 +46,6 @@ namespace ImageSharp /// public uint PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -85,6 +82,9 @@ namespace ImageSharp return !left.Equals(right); } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// /// Expands the packed representation into a . /// diff --git a/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs b/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs index c24553d3f..a5fa796e1 100644 --- a/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs +++ b/src/ImageSharp/Colors/PackedPixel/HalfVector4.cs @@ -49,9 +49,6 @@ namespace ImageSharp /// public ulong PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -88,6 +85,9 @@ namespace ImageSharp return !left.Equals(right); } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) diff --git a/src/ImageSharp/Colors/PackedPixel/IPixel.cs b/src/ImageSharp/Colors/PackedPixel/IPixel.cs index c17fe86cc..67e013a42 100644 --- a/src/ImageSharp/Colors/PackedPixel/IPixel.cs +++ b/src/ImageSharp/Colors/PackedPixel/IPixel.cs @@ -16,9 +16,11 @@ namespace ImageSharp where TSelf : struct, IPixel { /// - /// Gets the instance for this pixel type. + /// Creates a instance for this pixel type. + /// This method is not intended to be consumed directly. Use instead. /// - BulkPixelOperations BulkOperations { get; } + /// The instance. + BulkPixelOperations CreateBulkOperations(); } /// diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs index d425806c2..56be64a86 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedByte2.cs @@ -51,9 +51,6 @@ namespace ImageSharp /// public ushort PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -90,6 +87,9 @@ namespace ImageSharp return left.PackedValue != right.PackedValue; } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// /// Expands the packed representation into a . /// The vector components are typically expanded in least to greatest significance order. diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs index 4511060e4..a1f9b8d84 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedByte4.cs @@ -53,9 +53,6 @@ namespace ImageSharp /// public uint PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -92,6 +89,9 @@ namespace ImageSharp return left.PackedValue != right.PackedValue; } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs index 4bc7f9427..b34c1e88b 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedShort2.cs @@ -51,9 +51,6 @@ namespace ImageSharp /// public uint PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -90,6 +87,9 @@ namespace ImageSharp return !left.Equals(right); } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) diff --git a/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs b/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs index c848b7236..f33ac25a6 100644 --- a/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs +++ b/src/ImageSharp/Colors/PackedPixel/NormalizedShort4.cs @@ -53,9 +53,6 @@ namespace ImageSharp /// public ulong PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -92,6 +89,9 @@ namespace ImageSharp return !left.Equals(right); } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) diff --git a/src/ImageSharp/Colors/PackedPixel/Rg32.cs b/src/ImageSharp/Colors/PackedPixel/Rg32.cs index 9eb2247c9..f8486f7f2 100644 --- a/src/ImageSharp/Colors/PackedPixel/Rg32.cs +++ b/src/ImageSharp/Colors/PackedPixel/Rg32.cs @@ -36,9 +36,6 @@ namespace ImageSharp /// public uint PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -75,6 +72,9 @@ namespace ImageSharp return left.PackedValue != right.PackedValue; } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// /// Expands the packed representation into a . /// The vector components are typically expanded in least to greatest significance order. diff --git a/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs b/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs index 4f99feb6e..56f304070 100644 --- a/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs +++ b/src/ImageSharp/Colors/PackedPixel/Rgba1010102.cs @@ -39,9 +39,6 @@ namespace ImageSharp /// public uint PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -78,6 +75,9 @@ namespace ImageSharp return left.PackedValue != right.PackedValue; } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() diff --git a/src/ImageSharp/Colors/PackedPixel/Rgba64.cs b/src/ImageSharp/Colors/PackedPixel/Rgba64.cs index a23e57ec3..816401d4e 100644 --- a/src/ImageSharp/Colors/PackedPixel/Rgba64.cs +++ b/src/ImageSharp/Colors/PackedPixel/Rgba64.cs @@ -38,9 +38,6 @@ namespace ImageSharp /// public ulong PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -77,6 +74,9 @@ namespace ImageSharp return left.PackedValue != right.PackedValue; } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public Vector4 ToVector4() diff --git a/src/ImageSharp/Colors/PackedPixel/Short2.cs b/src/ImageSharp/Colors/PackedPixel/Short2.cs index f26e82578..802df7c1d 100644 --- a/src/ImageSharp/Colors/PackedPixel/Short2.cs +++ b/src/ImageSharp/Colors/PackedPixel/Short2.cs @@ -51,9 +51,6 @@ namespace ImageSharp /// public uint PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -90,6 +87,9 @@ namespace ImageSharp return left.PackedValue != right.PackedValue; } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) diff --git a/src/ImageSharp/Colors/PackedPixel/Short4.cs b/src/ImageSharp/Colors/PackedPixel/Short4.cs index 6dc7545e1..2517ef7a8 100644 --- a/src/ImageSharp/Colors/PackedPixel/Short4.cs +++ b/src/ImageSharp/Colors/PackedPixel/Short4.cs @@ -53,9 +53,6 @@ namespace ImageSharp /// public ulong PackedValue { get; set; } - /// - public BulkPixelOperations BulkOperations => new BulkPixelOperations(); - /// /// Compares two objects for equality. /// @@ -92,6 +89,9 @@ namespace ImageSharp return left.PackedValue != right.PackedValue; } + /// + public BulkPixelOperations CreateBulkOperations() => new BulkPixelOperations(); + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public void PackFromVector4(Vector4 vector) From a8ab5d4b89591bf865b9aac3136006c6083ca8e2 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 6 Mar 2017 01:15:07 +0100 Subject: [PATCH 14/15] Renamed BulkPixelOperation methods, removed ArrayExtensions and the unused dangerous PixelAccessor ctr. --- .../BulkPixelOperations{TColor}.cs | 10 +++---- .../Common/Extensions/ArrayExtensions.cs | 29 ------------------- .../Common/Memory/PinnedBuffer{T}.cs | 2 +- src/ImageSharp/Image/ImageBase{TColor}.cs | 3 -- src/ImageSharp/Image/PixelAccessor{TColor}.cs | 11 ------- .../ImageSharp.Sandbox46.csproj | 3 -- tests/ImageSharp.Sandbox46/Program.cs | 2 +- .../Colors/BulkPixelOperationsTests.cs | 10 +++---- .../PixelDataPoolTests.cs} | 0 9 files changed, 12 insertions(+), 58 deletions(-) delete mode 100644 src/ImageSharp/Common/Extensions/ArrayExtensions.cs rename tests/ImageSharp.Tests/{Image/PixelPoolTests.cs => Common/PixelDataPoolTests.cs} (100%) diff --git a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs index 986994af7..a84c3edf3 100644 --- a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs +++ b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs @@ -58,7 +58,7 @@ namespace ImageSharp /// The to the source colors. /// The to the destination vectors. /// The number of pixels to convert. - internal virtual void PackToVector4( + internal virtual void ToVector4( ArrayPointer sourceColors, ArrayPointer destVectors, int count) @@ -105,7 +105,7 @@ namespace ImageSharp /// The to the source colors. /// The to the destination bytes. /// The number of pixels to convert. - internal virtual void PackToXyzBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) + internal virtual void ToXyzBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) { byte* sp = (byte*)sourceColors; byte[] dest = destBytes.Array; @@ -148,7 +148,7 @@ namespace ImageSharp /// The to the source colors. /// The to the destination bytes. /// The number of pixels to convert. - internal virtual void PackToXyzwBytes( + internal virtual void ToXyzwBytes( ArrayPointer sourceColors, ArrayPointer destBytes, int count) @@ -194,7 +194,7 @@ namespace ImageSharp /// The to the source colors. /// The to the destination bytes. /// The number of pixels to convert. - internal virtual void PackToZyxBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) + internal virtual void ToZyxBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) { byte* sp = (byte*)sourceColors; byte[] dest = destBytes.Array; @@ -237,7 +237,7 @@ namespace ImageSharp /// The to the source colors. /// The to the destination bytes. /// The number of pixels to convert. - internal virtual void PackToZyxwBytes( + internal virtual void ToZyxwBytes( ArrayPointer sourceColors, ArrayPointer destBytes, int count) diff --git a/src/ImageSharp/Common/Extensions/ArrayExtensions.cs b/src/ImageSharp/Common/Extensions/ArrayExtensions.cs deleted file mode 100644 index cce442c52..000000000 --- a/src/ImageSharp/Common/Extensions/ArrayExtensions.cs +++ /dev/null @@ -1,29 +0,0 @@ -// -// Copyright (c) James Jackson-South and contributors. -// Licensed under the Apache License, Version 2.0. -// - -namespace ImageSharp -{ - using System; - - /// - /// Extension methods for arrays. - /// - public static class ArrayExtensions - { - /// - /// Locks the pixel buffer providing access to the pixels. - /// - /// The pixel format. - /// The pixel buffer. - /// Gets the width of the image represented by the pixel buffer. - /// The height of the image represented by the pixel buffer. - /// The - public static PixelAccessor Lock(this TColor[] pixels, int width, int height) - where TColor : struct, IPixel - { - return new PixelAccessor(width, height, pixels); - } - } -} \ 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 d95d67557..04217a012 100644 --- a/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs +++ b/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs @@ -23,7 +23,7 @@ namespace ImageSharp private GCHandle handle; /// - /// A value indicating wether this instance should return the array to the pool. + /// A value indicating whether this instance should return the array to the pool. /// private bool isPoolingOwner; diff --git a/src/ImageSharp/Image/ImageBase{TColor}.cs b/src/ImageSharp/Image/ImageBase{TColor}.cs index c2110eca4..2badc008a 100644 --- a/src/ImageSharp/Image/ImageBase{TColor}.cs +++ b/src/ImageSharp/Image/ImageBase{TColor}.cs @@ -163,9 +163,6 @@ namespace ImageSharp { Guard.NotNull(pixelSource, nameof(pixelSource)); - // TODO: Was this check really useful? If yes, we can define a bool PixelAccessor.IsBoundToImage to re-introduce it: - - // Guard.IsTrue(pixelSource.PooledMemory, nameof(pixelSource.PooledMemory), "pixelSource must be using pooled memory"); int newWidth = pixelSource.Width; int newHeight = pixelSource.Height; diff --git a/src/ImageSharp/Image/PixelAccessor{TColor}.cs b/src/ImageSharp/Image/PixelAccessor{TColor}.cs index 9201270d9..e104b8ae7 100644 --- a/src/ImageSharp/Image/PixelAccessor{TColor}.cs +++ b/src/ImageSharp/Image/PixelAccessor{TColor}.cs @@ -53,17 +53,6 @@ namespace ImageSharp this.ParallelOptions = image.Configuration.ParallelOptions; } - /// - /// Initializes a new instance of the class. - /// - /// The width of the image represented by the pixel buffer. - /// The height of the image represented by the pixel buffer. - /// The pixel buffer. - public PixelAccessor(int width, int height, TColor[] pixels) - : this(width, height, new PinnedBuffer(width * height, pixels)) - { - } - /// /// Initializes a new instance of the class. /// diff --git a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj index 094eedb18..1d7899486 100644 --- a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj +++ b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj @@ -251,9 +251,6 @@ Tests\Formats\Jpg\YCbCrImageTests.cs - - Tests\Image\PixelPoolTests.cs - Tests\MetaData\ImagePropertyTests.cs diff --git a/tests/ImageSharp.Sandbox46/Program.cs b/tests/ImageSharp.Sandbox46/Program.cs index 3afd18094..4d6d15925 100644 --- a/tests/ImageSharp.Sandbox46/Program.cs +++ b/tests/ImageSharp.Sandbox46/Program.cs @@ -37,7 +37,7 @@ namespace ImageSharp.Sandbox46 /// public static void Main(string[] args) { - //RunDecodeJpegProfilingTests(); + // RunDecodeJpegProfilingTests(); TestPixelAccessorCopyFromXyzw(); Console.ReadLine(); } diff --git a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs index c179b01a1..1c22411d0 100644 --- a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs @@ -67,7 +67,7 @@ TestOperation( source, expected, - (ops, s, d) => ops.PackToVector4(s, d, count) + (ops, s, d) => ops.ToVector4(s, d, count) ); } @@ -109,7 +109,7 @@ TestOperation( source, expected, - (ops, s, d) => ops.PackToXyzBytes(s, d, count) + (ops, s, d) => ops.ToXyzBytes(s, d, count) ); } @@ -150,7 +150,7 @@ TestOperation( source, expected, - (ops, s, d) => ops.PackToXyzwBytes(s, d, count) + (ops, s, d) => ops.ToXyzwBytes(s, d, count) ); } @@ -191,7 +191,7 @@ TestOperation( source, expected, - (ops, s, d) => ops.PackToZyxBytes(s, d, count) + (ops, s, d) => ops.ToZyxBytes(s, d, count) ); } @@ -232,7 +232,7 @@ TestOperation( source, expected, - (ops, s, d) => ops.PackToZyxwBytes(s, d, count) + (ops, s, d) => ops.ToZyxwBytes(s, d, count) ); } diff --git a/tests/ImageSharp.Tests/Image/PixelPoolTests.cs b/tests/ImageSharp.Tests/Common/PixelDataPoolTests.cs similarity index 100% rename from tests/ImageSharp.Tests/Image/PixelPoolTests.cs rename to tests/ImageSharp.Tests/Common/PixelDataPoolTests.cs From 099697cf0a31e4ddc81666a579ef7c32634edf62 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Mon, 6 Mar 2017 21:47:45 +0100 Subject: [PATCH 15/15] renamed ArrayPointer to BufferPointer --- .../BulkPixelOperations{TColor}.cs | 76 +++++++++---------- .../{ArrayPointer.cs => BufferPointer.cs} | 24 +++--- ...ArrayPointer{T}.cs => BufferPointer{T}.cs} | 40 +++++----- .../Color/Bulk/PixelAccessorVirtualCopy.cs | 24 +++--- .../Colors/BulkPixelOperationsTests.cs | 6 +- ...yPointerTests.cs => BufferPointerTests.cs} | 26 +++---- .../Common/PinnedBufferTests.cs | 4 +- 7 files changed, 100 insertions(+), 100 deletions(-) rename src/ImageSharp/Common/Memory/{ArrayPointer.cs => BufferPointer.cs} (79%) rename src/ImageSharp/Common/Memory/{ArrayPointer{T}.cs => BufferPointer{T}.cs} (66%) rename tests/ImageSharp.Tests/Common/{ArrayPointerTests.cs => BufferPointerTests.cs} (81%) diff --git a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs index a84c3edf3..259b1c9b4 100644 --- a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs +++ b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs @@ -29,12 +29,12 @@ namespace ImageSharp /// /// Bulk version of /// - /// The to the source vectors. - /// The to the destination colors. + /// The to the source vectors. + /// The to the destination colors. /// The number of pixels to convert. internal virtual void PackFromVector4( - ArrayPointer sourceVectors, - ArrayPointer destColors, + BufferPointer sourceVectors, + BufferPointer destColors, int count) { Vector4* sp = (Vector4*)sourceVectors.PointerAtOffset; @@ -55,12 +55,12 @@ namespace ImageSharp /// /// Bulk version of . /// - /// The to the source colors. - /// The to the destination vectors. + /// The to the source colors. + /// The to the destination vectors. /// The number of pixels to convert. internal virtual void ToVector4( - ArrayPointer sourceColors, - ArrayPointer destVectors, + BufferPointer sourceColors, + BufferPointer destVectors, int count) { byte* sp = (byte*)sourceColors; @@ -78,12 +78,12 @@ namespace ImageSharp /// /// Bulk version of that converts data in . /// - /// The to the source bytes. - /// The to the destination colors. + /// The to the source bytes. + /// The to the destination colors. /// The number of pixels to convert. internal virtual void PackFromXyzBytes( - ArrayPointer sourceBytes, - ArrayPointer destColors, + BufferPointer sourceBytes, + BufferPointer destColors, int count) { byte* sp = (byte*)sourceBytes; @@ -102,10 +102,10 @@ namespace ImageSharp /// /// Bulk version of . /// - /// The to the source colors. - /// The to the destination bytes. + /// The to the source colors. + /// The to the destination bytes. /// The number of pixels to convert. - internal virtual void ToXyzBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) + internal virtual void ToXyzBytes(BufferPointer sourceColors, BufferPointer destBytes, int count) { byte* sp = (byte*)sourceColors; byte[] dest = destBytes.Array; @@ -121,12 +121,12 @@ namespace ImageSharp /// /// Bulk version of that converts data in . /// - /// The to the source bytes. - /// The to the destination colors. + /// The to the source bytes. + /// The to the destination colors. /// The number of pixels to convert. internal virtual void PackFromXyzwBytes( - ArrayPointer sourceBytes, - ArrayPointer destColors, + BufferPointer sourceBytes, + BufferPointer destColors, int count) { byte* sp = (byte*)sourceBytes; @@ -145,12 +145,12 @@ namespace ImageSharp /// /// Bulk version of . /// - /// The to the source colors. - /// The to the destination bytes. + /// The to the source colors. + /// The to the destination bytes. /// The number of pixels to convert. internal virtual void ToXyzwBytes( - ArrayPointer sourceColors, - ArrayPointer destBytes, + BufferPointer sourceColors, + BufferPointer destBytes, int count) { byte* sp = (byte*)sourceColors; @@ -167,12 +167,12 @@ namespace ImageSharp /// /// Bulk version of that converts data in . /// - /// The to the source bytes. - /// The to the destination colors. + /// The to the source bytes. + /// The to the destination colors. /// The number of pixels to convert. internal virtual void PackFromZyxBytes( - ArrayPointer sourceBytes, - ArrayPointer destColors, + BufferPointer sourceBytes, + BufferPointer destColors, int count) { byte* sp = (byte*)sourceBytes; @@ -191,10 +191,10 @@ namespace ImageSharp /// /// Bulk version of . /// - /// The to the source colors. - /// The to the destination bytes. + /// The to the source colors. + /// The to the destination bytes. /// The number of pixels to convert. - internal virtual void ToZyxBytes(ArrayPointer sourceColors, ArrayPointer destBytes, int count) + internal virtual void ToZyxBytes(BufferPointer sourceColors, BufferPointer destBytes, int count) { byte* sp = (byte*)sourceColors; byte[] dest = destBytes.Array; @@ -210,12 +210,12 @@ namespace ImageSharp /// /// Bulk version of that converts data in . /// - /// The to the source bytes. - /// The to the destination colors. + /// The to the source bytes. + /// The to the destination colors. /// The number of pixels to convert. internal virtual void PackFromZyxwBytes( - ArrayPointer sourceBytes, - ArrayPointer destColors, + BufferPointer sourceBytes, + BufferPointer destColors, int count) { byte* sp = (byte*)sourceBytes; @@ -234,12 +234,12 @@ namespace ImageSharp /// /// Bulk version of . /// - /// The to the source colors. - /// The to the destination bytes. + /// The to the source colors. + /// The to the destination bytes. /// The number of pixels to convert. internal virtual void ToZyxwBytes( - ArrayPointer sourceColors, - ArrayPointer destBytes, + BufferPointer sourceColors, + BufferPointer destBytes, int count) { byte* sp = (byte*)sourceColors; diff --git a/src/ImageSharp/Common/Memory/ArrayPointer.cs b/src/ImageSharp/Common/Memory/BufferPointer.cs similarity index 79% rename from src/ImageSharp/Common/Memory/ArrayPointer.cs rename to src/ImageSharp/Common/Memory/BufferPointer.cs index 342eaa6c8..c80e22e21 100644 --- a/src/ImageSharp/Common/Memory/ArrayPointer.cs +++ b/src/ImageSharp/Common/Memory/BufferPointer.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -8,32 +8,32 @@ namespace ImageSharp using System.Runtime.CompilerServices; /// - /// Utility methods to + /// Utility methods for /// - internal static class ArrayPointer + internal static class BufferPointer { /// - /// Gets an to the beginning of the raw data in 'buffer'. + /// Gets a to the beginning of the raw data in 'buffer'. /// /// The element type /// The input - /// The + /// The [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe ArrayPointer GetArrayPointer(this PinnedBuffer buffer) + public static unsafe BufferPointer Slice(this PinnedBuffer buffer) where T : struct { - return new ArrayPointer(buffer.Array, (void*)buffer.Pointer); + return new BufferPointer(buffer.Array, (void*)buffer.Pointer); } /// /// Copy 'count' number of elements of the same type from 'source' to 'dest' /// /// The element type. - /// The input - /// The destination . + /// The input + /// The destination . /// The number of elements to copy [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void Copy(ArrayPointer source, ArrayPointer destination, int count) + public static unsafe void Copy(BufferPointer source, BufferPointer destination, int count) where T : struct { Unsafe.CopyBlock((void*)source.PointerAtOffset, (void*)destination.PointerAtOffset, USizeOf(count)); @@ -47,7 +47,7 @@ namespace ImageSharp /// The destination buffer. /// The number of elements to copy from 'source' [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void Copy(ArrayPointer source, ArrayPointer destination, int countInSource) + public static unsafe void Copy(BufferPointer source, BufferPointer destination, int countInSource) where T : struct { Unsafe.CopyBlock((void*)source.PointerAtOffset, (void*)destination.PointerAtOffset, USizeOf(countInSource)); @@ -61,7 +61,7 @@ namespace ImageSharp /// The destination buffer"/> /// The number of elements to copy. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe void Copy(ArrayPointer source, ArrayPointer destination, int countInDest) + public static unsafe void Copy(BufferPointer source, BufferPointer destination, int countInDest) where T : struct { Unsafe.CopyBlock((void*)source.PointerAtOffset, (void*)destination.PointerAtOffset, USizeOf(countInDest)); diff --git a/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs b/src/ImageSharp/Common/Memory/BufferPointer{T}.cs similarity index 66% rename from src/ImageSharp/Common/Memory/ArrayPointer{T}.cs rename to src/ImageSharp/Common/Memory/BufferPointer{T}.cs index 9cbbaf094..fff4e513e 100644 --- a/src/ImageSharp/Common/Memory/ArrayPointer{T}.cs +++ b/src/ImageSharp/Common/Memory/BufferPointer{T}.cs @@ -1,4 +1,4 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // @@ -15,21 +15,21 @@ namespace ImageSharp /// - It's not possible to use it with stack objects or pointers to unmanaged memory, only with managed arrays /// - It's possible to retrieve a reference to the array () so we can pass it to API-s like /// - There is no bounds checking for performance reasons. Therefore we don't need to store length. (However this could be added as DEBUG-only feature.) - /// This makes an unsafe type! - /// - Currently the arrays provided to ArrayPointer need to be pinned. This behaviour could be changed using C#7 features. + /// This makes an unsafe type! + /// - Currently the arrays provided to BufferPointer need to be pinned. This behaviour could be changed using C#7 features. /// /// The type of elements of the array - internal unsafe struct ArrayPointer + internal unsafe struct BufferPointer where T : struct { /// - /// Initializes a new instance of the struct from a pinned array and an offset. + /// Initializes a new instance of the struct from a pinned array and an offset. /// /// The pinned array /// Pointer to the beginning of array /// The offset inside the array [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ArrayPointer(T[] array, void* pointerToArray, int offset) + public BufferPointer(T[] array, void* pointerToArray, int offset) { DebugGuard.NotNull(array, nameof(array)); @@ -39,12 +39,12 @@ namespace ImageSharp } /// - /// Initializes a new instance of the struct from a pinned array. + /// Initializes a new instance of the struct from a pinned array. /// /// The pinned array /// Pointer to the start of 'array' [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ArrayPointer(T[] array, void* pointerToArray) + public BufferPointer(T[] array, void* pointerToArray) { DebugGuard.NotNull(array, nameof(array)); @@ -69,34 +69,34 @@ namespace ImageSharp public IntPtr PointerAtOffset { get; private set; } /// - /// Convertes instance to a raw 'void*' pointer + /// Convertes instance to a raw 'void*' pointer /// - /// The to convert + /// The to convert [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator void*(ArrayPointer arrayPointer) + public static explicit operator void*(BufferPointer bufferPointer) { - return (void*)arrayPointer.PointerAtOffset; + return (void*)bufferPointer.PointerAtOffset; } /// - /// Convertes instance to a raw 'byte*' pointer + /// Convertes instance to a raw 'byte*' pointer /// - /// The to convert + /// The to convert [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static explicit operator byte*(ArrayPointer arrayPointer) + public static explicit operator byte*(BufferPointer bufferPointer) { - return (byte*)arrayPointer.PointerAtOffset; + return (byte*)bufferPointer.PointerAtOffset; } /// - /// Forms a slice out of the given ArrayPointer, beginning at 'offset'. + /// Forms a slice out of the given BufferPointer, beginning at 'offset'. /// /// The offset in number of elements - /// The offseted (sliced) ArrayPointer + /// The offseted (sliced) BufferPointer [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ArrayPointer Slice(int offset) + public BufferPointer Slice(int offset) { - ArrayPointer result = default(ArrayPointer); + BufferPointer result = default(BufferPointer); result.Array = this.Array; result.Offset = this.Offset + offset; result.PointerAtOffset = this.PointerAtOffset + (Unsafe.SizeOf() * offset); diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs index 9222d6bac..694a26f3d 100644 --- a/tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs +++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs @@ -19,13 +19,13 @@ namespace ImageSharp.Benchmarks.Color.Bulk { abstract class CopyExecutor { - internal abstract void VirtualCopy(ArrayPointer destination, ArrayPointer source, int count); + internal abstract void VirtualCopy(BufferPointer destination, BufferPointer source, int count); } class UnsafeCopyExecutor : CopyExecutor { [MethodImpl(MethodImplOptions.NoInlining)] - internal override unsafe void VirtualCopy(ArrayPointer destination, ArrayPointer source, int count) + internal override unsafe void VirtualCopy(BufferPointer destination, BufferPointer source, int count) { Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, (uint)count*4); } @@ -76,7 +76,7 @@ namespace ImageSharp.Benchmarks.Color.Bulk } [Benchmark] - public void CopyArrayPointerUnsafeInlined() + public void CopyBufferPointerUnsafeInlined() { uint byteCount = (uint)this.area.Width * 4; @@ -85,22 +85,22 @@ namespace ImageSharp.Benchmarks.Color.Bulk for (int y = 0; y < this.Height; y++) { - ArrayPointer source = this.GetAreaRow(y); - ArrayPointer destination = this.GetPixelAccessorRow(targetX, targetY + 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 CopyArrayPointerUnsafeVirtual() + public void CopyBufferPointerUnsafeVirtual() { 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); + BufferPointer source = this.GetAreaRow(y); + BufferPointer destination = this.GetPixelAccessorRow(targetX, targetY + y); this.executor.VirtualCopy(destination, source, this.area.Width); } } @@ -111,9 +111,9 @@ namespace ImageSharp.Benchmarks.Color.Bulk } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private ArrayPointer GetPixelAccessorRow(int x, int y) + private BufferPointer GetPixelAccessorRow(int x, int y) { - return new ArrayPointer( + return new BufferPointer( this.pixelAccessor.PixelBuffer, (void*)this.pixelAccessor.DataPointer, (y * this.pixelAccessor.Width) + x @@ -121,9 +121,9 @@ namespace ImageSharp.Benchmarks.Color.Bulk } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private ArrayPointer GetAreaRow(int y) + private BufferPointer GetAreaRow(int y) { - return new ArrayPointer(this.area.Bytes, this.area.PixelBase, y * this.area.RowStride); + return new BufferPointer(this.area.Bytes, this.area.PixelBase, y * this.area.RowStride); } } } diff --git a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs index 1c22411d0..80d5952a1 100644 --- a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs +++ b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs @@ -245,8 +245,8 @@ public PinnedBuffer ActualDestBuffer { get; } public PinnedBuffer ExpectedDestBuffer { get; } - public ArrayPointer Source => this.SourceBuffer.GetArrayPointer(); - public ArrayPointer ActualDest => this.ActualDestBuffer.GetArrayPointer(); + public BufferPointer Source => this.SourceBuffer.Slice(); + public BufferPointer ActualDest => this.ActualDestBuffer.Slice(); public TestBuffers(TSource[] source, TDest[] expectedDest) { @@ -277,7 +277,7 @@ private static void TestOperation( TSource[] source, TDest[] expected, - Action, ArrayPointer, ArrayPointer> action) + Action, BufferPointer, BufferPointer> action) where TSource : struct where TDest : struct { diff --git a/tests/ImageSharp.Tests/Common/ArrayPointerTests.cs b/tests/ImageSharp.Tests/Common/BufferPointerTests.cs similarity index 81% rename from tests/ImageSharp.Tests/Common/ArrayPointerTests.cs rename to tests/ImageSharp.Tests/Common/BufferPointerTests.cs index 916a10947..4b5847d53 100644 --- a/tests/ImageSharp.Tests/Common/ArrayPointerTests.cs +++ b/tests/ImageSharp.Tests/Common/BufferPointerTests.cs @@ -7,7 +7,7 @@ namespace ImageSharp.Tests.Common using Xunit; - public unsafe class ArrayPointerTests + public unsafe class BufferPointerTests { public struct Foo { @@ -33,7 +33,7 @@ namespace ImageSharp.Tests.Common fixed (Foo* p = array) { // Act: - ArrayPointer ap = new ArrayPointer(array, p); + BufferPointer ap = new BufferPointer(array, p); // Assert: Assert.Equal(array, ap.Array); @@ -49,7 +49,7 @@ namespace ImageSharp.Tests.Common fixed (Foo* p = array) { // Act: - ArrayPointer ap = new ArrayPointer(array, p, offset); + BufferPointer ap = new BufferPointer(array, p, offset); // Assert: Assert.Equal(array, ap.Array); @@ -67,7 +67,7 @@ namespace ImageSharp.Tests.Common int totalOffset = offset0 + offset1; fixed (Foo* p = array) { - ArrayPointer ap = new ArrayPointer(array, p, offset0); + BufferPointer ap = new BufferPointer(array, p, offset0); // Act: ap = ap.Slice(offset1); @@ -92,10 +92,10 @@ namespace ImageSharp.Tests.Common fixed (Foo* pSource = source) fixed (Foo* pDest = dest) { - ArrayPointer apSource = new ArrayPointer(source, pSource); - ArrayPointer apDest = new ArrayPointer(dest, pDest); + BufferPointer apSource = new BufferPointer(source, pSource); + BufferPointer apDest = new BufferPointer(dest, pDest); - ArrayPointer.Copy(apSource, apDest, count); + BufferPointer.Copy(apSource, apDest, count); } Assert.Equal(source[0], dest[0]); @@ -115,10 +115,10 @@ namespace ImageSharp.Tests.Common fixed (Foo* pSource = source) fixed (byte* pDest = dest) { - ArrayPointer apSource = new ArrayPointer(source, pSource); - ArrayPointer apDest = new ArrayPointer(dest, pDest); + BufferPointer apSource = new BufferPointer(source, pSource); + BufferPointer apDest = new BufferPointer(dest, pDest); - ArrayPointer.Copy(apSource, apDest, count); + BufferPointer.Copy(apSource, apDest, count); } Assert.True(ElementsAreEqual(source, dest, 0)); @@ -138,10 +138,10 @@ namespace ImageSharp.Tests.Common fixed(byte* pSource = source) fixed (Foo* pDest = dest) { - ArrayPointer apSource = new ArrayPointer(source, pSource); - ArrayPointer apDest = new ArrayPointer(dest, pDest); + BufferPointer apSource = new BufferPointer(source, pSource); + BufferPointer apDest = new BufferPointer(dest, pDest); - ArrayPointer.Copy(apSource, apDest, count); + BufferPointer.Copy(apSource, apDest, count); } Assert.True(ElementsAreEqual(dest, source, 0)); diff --git a/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs b/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs index c5eb2a510..65077ae7f 100644 --- a/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs +++ b/tests/ImageSharp.Tests/Common/PinnedBufferTests.cs @@ -57,13 +57,13 @@ } [Fact] - public void GetArrayPointer() + public void Slice() { Foo[] a = { new Foo() { A = 1, B = 2 }, new Foo() { A = 3, B = 4 } }; using (PinnedBuffer buffer = new PinnedBuffer(a)) { - var arrayPtr = buffer.GetArrayPointer(); + var arrayPtr = buffer.Slice(); Assert.Equal(a, arrayPtr.Array); Assert.Equal(0, arrayPtr.Offset);