diff --git a/src/ImageSharp/Common/Memory/BufferPointer{T}.cs b/src/ImageSharp/Common/Memory/BufferPointer{T}.cs
index fe79e064e..cfdd8e6de 100644
--- a/src/ImageSharp/Common/Memory/BufferPointer{T}.cs
+++ b/src/ImageSharp/Common/Memory/BufferPointer{T}.cs
@@ -63,6 +63,11 @@ namespace ImageSharp
///
public int Offset { get; private set; }
+ ///
+ /// Gets the offset inside in bytes.
+ ///
+ public int ByteOffset => this.Offset * Unsafe.SizeOf();
+
///
/// Gets the pointer to the offseted array position
///
diff --git a/src/ImageSharp/project.json b/src/ImageSharp/project.json
index 639773377..6519a3f6b 100644
--- a/src/ImageSharp/project.json
+++ b/src/ImageSharp/project.json
@@ -98,6 +98,7 @@
},
"net461": {
"dependencies": {
+ "System.Numerics.Vectors": "4.1.1",
"System.Threading.Tasks.Parallel": "4.0.0"
},
"frameworkAssemblies": {
diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs
new file mode 100644
index 000000000..1c541d28b
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/Color/Bulk/PackFromXyzw.cs
@@ -0,0 +1,63 @@
+// ReSharper disable InconsistentNaming
+namespace ImageSharp.Benchmarks.Color.Bulk
+{
+ using BenchmarkDotNet.Attributes;
+
+ using Color = ImageSharp.Color;
+
+ public abstract class PackFromXyzw
+ where TColor : struct, IPixel
+ {
+ private PinnedBuffer destination;
+
+ private PinnedBuffer source;
+
+ [Params(16, 128, 1024)]
+ public int Count { get; set; }
+
+ [Setup]
+ public void Setup()
+ {
+ this.destination = new PinnedBuffer(this.Count);
+ this.source = new PinnedBuffer(this.Count * 4);
+ }
+
+ [Cleanup]
+ public void Cleanup()
+ {
+ this.destination.Dispose();
+ this.source.Dispose();
+ }
+
+ [Benchmark(Baseline = true)]
+ public void PerElement()
+ {
+ byte[] s = this.source.Array;
+ TColor[] d = this.destination.Array;
+
+ for (int i = 0; i < this.Count; i++)
+ {
+ int i4 = i * 4;
+ TColor c = default(TColor);
+ c.PackFromBytes(s[i4], s[i4 + 1], s[i4 + 2], s[i4 + 3]);
+ d[i] = c;
+ }
+ }
+
+ [Benchmark]
+ public void CommonBulk()
+ {
+ new BulkPixelOperations().PackFromXyzwBytes(this.source, this.destination, this.Count);
+ }
+
+ [Benchmark]
+ public void OptimizedBulk()
+ {
+ BulkPixelOperations.Instance.PackFromXyzwBytes(this.source, this.destination, this.Count);
+ }
+ }
+
+ public class PackFromXyzw_Color : PackFromXyzw
+ {
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs
deleted file mode 100644
index 3df688972..000000000
--- a/tests/ImageSharp.Benchmarks/Color/Bulk/PixelAccessorVirtualCopy.cs
+++ /dev/null
@@ -1,129 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
-
-namespace ImageSharp.Benchmarks.Color.Bulk
-{
- using System.Runtime.CompilerServices;
- using System.Runtime.InteropServices;
-
- using BenchmarkDotNet.Attributes;
-
- using Color = ImageSharp.Color;
-
- ///
- /// Benchmark to measure the effect of using virtual bulk-copy calls inside PixelAccessor methods
- ///
- public unsafe class PixelAccessorVirtualCopy
- {
- abstract class CopyExecutor
- {
- internal abstract void VirtualCopy(BufferPointer destination, BufferPointer source, int count);
- }
-
- class UnsafeCopyExecutor : CopyExecutor
- {
- [MethodImpl(MethodImplOptions.NoInlining)]
- internal override unsafe void VirtualCopy(BufferPointer destination, BufferPointer source, int count)
- {
- Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, (uint)count*4);
- }
- }
-
- private PixelAccessor pixelAccessor;
-
- private PixelArea area;
-
- private CopyExecutor executor;
-
- [Params(64, 256, 512)]
- public int Width { get; set; }
-
- public int Height { get; set; } = 256;
-
-
- [Setup]
- public void Setup()
- {
- this.pixelAccessor = new PixelAccessor(this.Width, this.Height);
- this.area = new PixelArea(this.Width / 2, this.Height, ComponentOrder.Xyzw);
- this.executor = new UnsafeCopyExecutor();
- }
-
- [Cleanup]
- public void Cleanup()
- {
- this.pixelAccessor.Dispose();
- this.area.Dispose();
- }
-
- [Benchmark(Baseline = true)]
- public void CopyRawUnsafeInlined()
- {
- uint byteCount = (uint)this.area.Width * 4;
-
- int targetX = this.Width / 4;
- int targetY = 0;
-
- for (int y = 0; y < this.Height; y++)
- {
- byte* source = this.area.PixelBase + (y * this.area.RowStride);
- byte* destination = this.GetRowPointer(targetX, targetY + y);
-
- Unsafe.CopyBlock(destination, source, byteCount);
- }
- }
-
- [Benchmark]
- public void CopyBufferPointerUnsafeInlined()
- {
- uint byteCount = (uint)this.area.Width * 4;
-
- int targetX = this.Width / 4;
- int targetY = 0;
-
- for (int y = 0; y < this.Height; y++)
- {
- BufferPointer source = this.GetAreaRow(y);
- BufferPointer destination = this.GetPixelAccessorRow(targetX, targetY + y);
- Unsafe.CopyBlock((void*)destination.PointerAtOffset, (void*)source.PointerAtOffset, byteCount);
- }
- }
-
- [Benchmark]
- public void CopyBufferPointerUnsafeVirtual()
- {
- int targetX = this.Width / 4;
- int targetY = 0;
-
- for (int y = 0; y < this.Height; y++)
- {
- BufferPointer source = this.GetAreaRow(y);
- BufferPointer destination = this.GetPixelAccessorRow(targetX, targetY + y);
- this.executor.VirtualCopy(destination, source, this.area.Width);
- }
- }
-
- private byte* GetRowPointer(int x, int y)
- {
- return (byte*)this.pixelAccessor.DataPointer + (((y * this.pixelAccessor.Width) + x) * Unsafe.SizeOf());
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private BufferPointer GetPixelAccessorRow(int x, int y)
- {
- return new BufferPointer(
- this.pixelAccessor.PixelArray,
- (void*)this.pixelAccessor.DataPointer,
- (y * this.pixelAccessor.Width) + x
- );
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- private BufferPointer GetAreaRow(int y)
- {
- return new BufferPointer(this.area.Bytes, this.area.PixelBase, y * this.area.RowStride);
- }
- }
-}
diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs
new file mode 100644
index 000000000..bc59dba4e
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyz.cs
@@ -0,0 +1,61 @@
+// ReSharper disable InconsistentNaming
+namespace ImageSharp.Benchmarks.Color.Bulk
+{
+ using BenchmarkDotNet.Attributes;
+
+ using Color = ImageSharp.Color;
+
+ public abstract class ToXyz
+ where TColor : struct, IPixel
+ {
+ private PinnedBuffer source;
+
+ private PinnedBuffer destination;
+
+ [Params(16, 128, 1024)]
+ public int Count { get; set; }
+
+ [Setup]
+ public void Setup()
+ {
+ this.source = new PinnedBuffer(this.Count);
+ this.destination = new PinnedBuffer(this.Count * 3);
+ }
+
+ [Cleanup]
+ public void Cleanup()
+ {
+ this.source.Dispose();
+ this.destination.Dispose();
+ }
+
+ [Benchmark(Baseline = true)]
+ public void PerElement()
+ {
+ TColor[] s = this.source.Array;
+ byte[] d = this.destination.Array;
+
+ for (int i = 0; i < this.Count; i++)
+ {
+ TColor c = s[i];
+ c.ToXyzBytes(d, i * 4);
+ }
+ }
+
+ [Benchmark]
+ public void CommonBulk()
+ {
+ new BulkPixelOperations().ToXyzBytes(this.source, this.destination, this.Count);
+ }
+
+ [Benchmark]
+ public void OptimizedBulk()
+ {
+ BulkPixelOperations.Instance.ToXyzBytes(this.source, this.destination, this.Count);
+ }
+ }
+
+ public class ToXyz_Color : ToXyz
+ {
+ }
+}
\ No newline at end of file
diff --git a/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs
new file mode 100644
index 000000000..a4ec6f6dc
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/Color/Bulk/ToXyzw.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+// ReSharper disable InconsistentNaming
+
+namespace ImageSharp.Benchmarks.Color.Bulk
+{
+ using BenchmarkDotNet.Attributes;
+
+ using Color = ImageSharp.Color;
+
+ public abstract class ToXyzw
+ where TColor : struct, IPixel
+ {
+ private PinnedBuffer source;
+
+ private PinnedBuffer destination;
+
+ [Params(16, 128, 1024)]
+ public int Count { get; set; }
+
+ [Setup]
+ public void Setup()
+ {
+ this.source = new PinnedBuffer(this.Count);
+ this.destination = new PinnedBuffer(this.Count * 4);
+ }
+
+ [Cleanup]
+ public void Cleanup()
+ {
+ this.source.Dispose();
+ this.destination.Dispose();
+ }
+
+ [Benchmark(Baseline = true)]
+ public void PerElement()
+ {
+ TColor[] s = this.source.Array;
+ byte[] d = this.destination.Array;
+
+ for (int i = 0; i < this.Count; i++)
+ {
+ TColor c = s[i];
+ c.ToXyzwBytes(d, i * 4);
+ }
+ }
+
+ [Benchmark]
+ public void CommonBulk()
+ {
+ new BulkPixelOperations().ToXyzwBytes(this.source, this.destination, this.Count);
+ }
+
+ [Benchmark]
+ public void OptimizedBulk()
+ {
+ BulkPixelOperations.Instance.ToXyzwBytes(this.source, this.destination, this.Count);
+ }
+ }
+
+ public class ToXyzw_Color : ToXyzw
+ {
+ }
+
+ public class ToXyzw_Argb : ToXyzw
+ {
+ }
+}
diff --git a/tests/ImageSharp.Benchmarks/Image/EncodePng.cs b/tests/ImageSharp.Benchmarks/Image/EncodePng.cs
index dd1882c80..a182ccd88 100644
--- a/tests/ImageSharp.Benchmarks/Image/EncodePng.cs
+++ b/tests/ImageSharp.Benchmarks/Image/EncodePng.cs
@@ -10,6 +10,10 @@ namespace ImageSharp.Benchmarks.Image
using System.IO;
using BenchmarkDotNet.Attributes;
+
+ using ImageSharp.Formats;
+ using ImageSharp.Quantizers;
+
using CoreImage = ImageSharp.Image;
public class EncodePng : BenchmarkBase
@@ -19,12 +23,21 @@ namespace ImageSharp.Benchmarks.Image
private Image bmpDrawing;
private CoreImage bmpCore;
+ [Params(false, true)]
+ public bool LargeImage { get; set; }
+
+ [Params(false, true)]
+ public bool UseOctreeQuantizer { get; set; }
+
[Setup]
public void ReadImages()
{
if (this.bmpStream == null)
{
- this.bmpStream = File.OpenRead("../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp");
+ string path = this.LargeImage
+ ? "../ImageSharp.Tests/TestImages/Formats/Jpg/baseline/jpeg420exif.jpg"
+ : "../ImageSharp.Tests/TestImages/Formats/Bmp/Car.bmp";
+ this.bmpStream = File.OpenRead(path);
this.bmpCore = new CoreImage(this.bmpStream);
this.bmpStream.Position = 0;
this.bmpDrawing = Image.FromStream(this.bmpStream);
@@ -53,7 +66,13 @@ namespace ImageSharp.Benchmarks.Image
{
using (MemoryStream memoryStream = new MemoryStream())
{
- this.bmpCore.SaveAsPng(memoryStream);
+ Quantizer quantizer = this.UseOctreeQuantizer
+ ? (Quantizer)
+ new OctreeQuantizer()
+ : new PaletteQuantizer();
+
+ PngEncoderOptions options = new PngEncoderOptions() { Quantizer = quantizer };
+ this.bmpCore.SaveAsPng(memoryStream, options);
}
}
}
diff --git a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj
index 4d3548c81..02318d0b4 100644
--- a/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj
+++ b/tests/ImageSharp.Sandbox46/ImageSharp.Sandbox46.csproj
@@ -206,9 +206,6 @@
-
- Benchmarks\PixelAccessorVirtualCopy.cs
-
Tests\Colors\BulkPixelOperationsTests.cs
@@ -346,7 +343,9 @@
-
+
+
+
diff --git a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs
index fa1b536f3..88bc4a9be 100644
--- a/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs
+++ b/tests/ImageSharp.Tests/Colors/BulkPixelOperationsTests.cs
@@ -10,7 +10,7 @@
public class Color : BulkPixelOperationsTests
{
// For 4.6 test runner MemberData does not work without redeclaring the public field in the derived test class:
- public static TheoryData ArraySizesData => new TheoryData { 7, 16, 1111 };
+ public static new TheoryData ArraySizesData => new TheoryData { 7, 16, 1111 };
[Fact]
public void IsSpecialImplementation()
@@ -35,7 +35,7 @@
public class Argb : BulkPixelOperationsTests
{
// For 4.6 test runner MemberData does not work without redeclaring the public field in the derived test class:
- public static TheoryData ArraySizesData => new TheoryData { 7, 16, 1111 };
+ public static new TheoryData ArraySizesData => new TheoryData { 7, 16, 1111 };
}
[Theory]