From ebb581b3f6ffaa3c71a732414b4be1742fdfff34 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 7 Mar 2017 01:53:46 +0100 Subject: [PATCH] Color.BulkOperations full implementation --- src/ImageSharp/Colors/Color.BulkOperations.cs | 109 ++++++++++++++++++ src/ImageSharp/Common/Memory/BufferPointer.cs | 19 +-- .../Common/Memory/BufferPointer{T}.cs | 4 +- .../Common/Memory/PinnedBuffer{T}.cs | 22 ++++ .../Common/BufferPointerTests.cs | 59 +++++++++- 5 files changed, 192 insertions(+), 21 deletions(-) diff --git a/src/ImageSharp/Colors/Color.BulkOperations.cs b/src/ImageSharp/Colors/Color.BulkOperations.cs index 9d698b8ac..18f9d92a6 100644 --- a/src/ImageSharp/Colors/Color.BulkOperations.cs +++ b/src/ImageSharp/Colors/Color.BulkOperations.cs @@ -125,6 +125,115 @@ namespace ImageSharp base.ToVector4(sourceColors, destVectors, remainder); } } + + /// + internal override unsafe void PackFromXyzBytes(BufferPointer sourceBytes, BufferPointer destColors, int count) + { + byte* source = (byte*)sourceBytes; + byte* destination = (byte*)destColors; + + for (int x = 0; x < count; x++) + { + Unsafe.Write(destination, (uint)(*source << 0 | *(source + 1) << 8 | *(source + 2) << 16 | 255 << 24)); + + source += 3; + destination += 4; + } + } + + /// + internal override unsafe void ToXyzBytes(BufferPointer sourceColors, BufferPointer destBytes, int count) + { + byte* source = (byte*)sourceColors; + byte* destination = (byte*)destBytes; + + for (int x = 0; x < count; x++) + { + *destination = *(source + 0); + *(destination + 1) = *(source + 1); + *(destination + 2) = *(source + 2); + + source += 4; + destination += 3; + } + } + + /// + internal override void PackFromXyzwBytes(BufferPointer sourceBytes, BufferPointer destColors, int count) + { + BufferPointer.Copy(sourceBytes, destColors, count); + } + + /// + internal override void ToXyzwBytes(BufferPointer sourceColors, BufferPointer destBytes, int count) + { + BufferPointer.Copy(sourceColors, destBytes, count); + } + + /// + internal override unsafe void PackFromZyxBytes(BufferPointer sourceBytes, BufferPointer destColors, int count) + { + byte* source = (byte*)sourceBytes; + byte* destination = (byte*)destColors; + + for (int x = 0; x < count; x++) + { + Unsafe.Write(destination, (uint)(*(source + 2) << 0 | *(source + 1) << 8 | *source << 16 | 255 << 24)); + + source += 3; + destination += 4; + } + } + + /// + internal override unsafe void ToZyxBytes(BufferPointer sourceColors, BufferPointer destBytes, int count) + { + byte* source = (byte*)sourceColors; + byte* destination = (byte*)destBytes; + + for (int x = 0; x < count; x++) + { + *destination = *(source + 2); + *(destination + 1) = *(source + 1); + *(destination + 2) = *(source + 0); + + source += 4; + destination += 3; + } + } + + /// + internal override unsafe void PackFromZyxwBytes(BufferPointer sourceBytes, BufferPointer destColors, int count) + { + byte* source = (byte*)sourceBytes; + byte* destination = (byte*)destColors; + + for (int x = 0; x < count; x++) + { + Unsafe.Write(destination, (uint)(*(source + 2) << 0 | *(source + 1) << 8 | *source << 16 | *(source + 3) << 24)); + + source += 4; + destination += 4; + } + } + + /// + internal override unsafe void ToZyxwBytes(BufferPointer sourceColors, BufferPointer destBytes, int count) + { + byte* source = (byte*)sourceColors; + byte* destination = (byte*)destBytes; + + for (int x = 0; x < count; x++) + { + *destination = *(source + 2); + *(destination + 1) = *(source + 1); + *(destination + 2) = *(source + 0); + *(destination + 3) = *(source + 3); + + source += 4; + destination += 4; + } + } } } } \ No newline at end of file diff --git a/src/ImageSharp/Common/Memory/BufferPointer.cs b/src/ImageSharp/Common/Memory/BufferPointer.cs index c80e22e21..f8798ba24 100644 --- a/src/ImageSharp/Common/Memory/BufferPointer.cs +++ b/src/ImageSharp/Common/Memory/BufferPointer.cs @@ -12,18 +12,7 @@ namespace ImageSharp /// internal static class BufferPointer { - /// - /// Gets a to the beginning of the raw data in 'buffer'. - /// - /// The element type - /// The input - /// The - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static unsafe BufferPointer Slice(this PinnedBuffer buffer) - where T : struct - { - return new BufferPointer(buffer.Array, (void*)buffer.Pointer); - } + /// /// Copy 'count' number of elements of the same type from 'source' to 'dest' @@ -36,7 +25,7 @@ namespace ImageSharp public static unsafe void Copy(BufferPointer source, BufferPointer destination, int count) where T : struct { - Unsafe.CopyBlock((void*)source.PointerAtOffset, (void*)destination.PointerAtOffset, USizeOf(count)); + Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, USizeOf(count)); } /// @@ -50,7 +39,7 @@ namespace ImageSharp public static unsafe void Copy(BufferPointer source, BufferPointer destination, int countInSource) where T : struct { - Unsafe.CopyBlock((void*)source.PointerAtOffset, (void*)destination.PointerAtOffset, USizeOf(countInSource)); + Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, USizeOf(countInSource)); } /// @@ -64,7 +53,7 @@ namespace ImageSharp public static unsafe void Copy(BufferPointer source, BufferPointer destination, int countInDest) where T : struct { - Unsafe.CopyBlock((void*)source.PointerAtOffset, (void*)destination.PointerAtOffset, USizeOf(countInDest)); + Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, USizeOf(countInDest)); } /// diff --git a/src/ImageSharp/Common/Memory/BufferPointer{T}.cs b/src/ImageSharp/Common/Memory/BufferPointer{T}.cs index fff4e513e..e05650a70 100644 --- a/src/ImageSharp/Common/Memory/BufferPointer{T}.cs +++ b/src/ImageSharp/Common/Memory/BufferPointer{T}.cs @@ -79,7 +79,7 @@ namespace ImageSharp } /// - /// Convertes instance to a raw 'byte*' pointer + /// Converts instance to a raw 'byte*' pointer /// /// The to convert [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -87,7 +87,7 @@ namespace ImageSharp { return (byte*)bufferPointer.PointerAtOffset; } - + /// /// Forms a slice out of the given BufferPointer, beginning at 'offset'. /// diff --git a/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs b/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs index 04217a012..8e5ded048 100644 --- a/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs +++ b/src/ImageSharp/Common/Memory/PinnedBuffer{T}.cs @@ -7,6 +7,7 @@ namespace ImageSharp { using System; using System.Buffers; + using System.Runtime.CompilerServices; using System.Runtime.InteropServices; /// @@ -94,6 +95,27 @@ namespace ImageSharp /// Gets a pointer to the pinned . /// public IntPtr Pointer { get; private set; } + + /// + /// Converts to an . + /// + /// The to convert. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static implicit operator BufferPointer(PinnedBuffer buffer) + { + return buffer.Slice(); + } + + /// + /// Gets a to the beginning of the raw data in 'buffer'. + /// + /// The element type + /// The + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe BufferPointer Slice() + { + return new BufferPointer(this.Array, (void*)this.Pointer); + } /// /// Disposes the instance by unpinning the array, and returning the pooled buffer when necessary. diff --git a/tests/ImageSharp.Tests/Common/BufferPointerTests.cs b/tests/ImageSharp.Tests/Common/BufferPointerTests.cs index 4b5847d53..a0b91b4c4 100644 --- a/tests/ImageSharp.Tests/Common/BufferPointerTests.cs +++ b/tests/ImageSharp.Tests/Common/BufferPointerTests.cs @@ -15,12 +15,18 @@ namespace ImageSharp.Tests.Common public double B; + public Foo(int a, double b) + { + this.A = a; + this.B = b; + } + internal static Foo[] CreateArray(int size) { Foo[] result = new Foo[size]; for (int i = 0; i < size; i++) { - result[i] = new Foo() { A = i, B = i }; + result[i] = new Foo(i, i); } return result; } @@ -81,6 +87,12 @@ namespace ImageSharp.Tests.Common public class Copy { + private static void AssertNotDefault(T[] data, int idx) + where T : struct + { + Assert.NotEqual(default(T), data[idx]); + } + [Theory] [InlineData(4)] [InlineData(1500)] @@ -98,7 +110,11 @@ namespace ImageSharp.Tests.Common BufferPointer.Copy(apSource, apDest, count); } + AssertNotDefault(source, 1); + AssertNotDefault(dest, 1); + Assert.Equal(source[0], dest[0]); + Assert.Equal(source[1], dest[1]); Assert.Equal(source[count-1], dest[count-1]); Assert.NotEqual(source[count], dest[count]); } @@ -121,19 +137,31 @@ namespace ImageSharp.Tests.Common BufferPointer.Copy(apSource, apDest, count); } + AssertNotDefault(source, 1); + Assert.True(ElementsAreEqual(source, dest, 0)); Assert.True(ElementsAreEqual(source, dest, count - 1)); Assert.False(ElementsAreEqual(source, dest, count)); } + private static byte[] CreateTestBytes(int count) + { + byte[] result = new byte[count]; + for (int i = 0; i < result.Length; i++) + { + result[i] = (byte)(i % 255); + } + return result; + } + [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); + int srcCount = count * sizeof(Foo); + byte[] source = CreateTestBytes(srcCount); + Foo[] dest = new Foo[count + 2]; fixed(byte* pSource = source) fixed (Foo* pDest = dest) @@ -144,10 +172,33 @@ namespace ImageSharp.Tests.Common BufferPointer.Copy(apSource, apDest, count); } + AssertNotDefault(source, sizeof(Foo) + 1); + AssertNotDefault(dest, 1); + Assert.True(ElementsAreEqual(dest, source, 0)); + Assert.True(ElementsAreEqual(dest, source, 1)); Assert.True(ElementsAreEqual(dest, source, count - 1)); Assert.False(ElementsAreEqual(dest, source, count)); } + + [Fact] + public void ColorToBytes() + { + Color[] colors = { new Color(0, 1, 2, 3), new Color(4, 5, 6, 7), new Color(8, 9, 10, 11), }; + + using (PinnedBuffer colorBuf = new PinnedBuffer(colors)) + using (PinnedBuffer byteBuf = new PinnedBuffer(colors.Length*4)) + { + BufferPointer.Copy(colorBuf, byteBuf, colorBuf.Count); + + byte[] a = byteBuf.Array; + + for (int i = 0; i < byteBuf.Count; i++) + { + Assert.Equal((byte)i, a[i]); + } + } + } private static bool ElementsAreEqual(Foo[] array, byte[] rawArray, int index) {