diff --git a/src/ImageSharp/PixelFormats/Argb32.cs b/src/ImageSharp/PixelFormats/Argb32.cs
index 61e860aee..cb2a1b87e 100644
--- a/src/ImageSharp/PixelFormats/Argb32.cs
+++ b/src/ImageSharp/PixelFormats/Argb32.cs
@@ -58,11 +58,24 @@ namespace ImageSharp.PixelFormats
/// The green component.
/// The blue component.
/// The alpha component.
- public Argb32(byte r, byte g, byte b, byte a = 255)
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Argb32(byte r, byte g, byte b, byte a)
{
this.PackedValue = Pack(r, g, b, a);
}
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The red component.
+ /// The green component.
+ /// The blue component.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Argb32(byte r, byte g, byte b)
+ {
+ this.PackedValue = Pack(r, g, b, 255);
+ }
+
///
/// Initializes a new instance of the struct.
///
diff --git a/src/ImageSharp/PixelFormats/Rgba32.cs b/src/ImageSharp/PixelFormats/Rgba32.cs
index 15a9ed0fd..c5f752336 100644
--- a/src/ImageSharp/PixelFormats/Rgba32.cs
+++ b/src/ImageSharp/PixelFormats/Rgba32.cs
@@ -49,12 +49,6 @@ namespace ImageSharp
[FieldOffset(3)]
public byte A;
- ///
- /// The packed representation of the value.
- ///
- [FieldOffset(0)]
- public uint Rgba;
-
///
/// The shift count for the red component
///
@@ -85,6 +79,21 @@ namespace ImageSharp
///
private static readonly Vector4 Half = new Vector4(0.5F);
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The red component.
+ /// The green component.
+ /// The blue component.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Rgba32(byte r, byte g, byte b)
+ {
+ this.R = r;
+ this.G = g;
+ this.B = b;
+ this.A = 255;
+ }
+
///
/// Initializes a new instance of the struct.
///
@@ -93,8 +102,7 @@ namespace ImageSharp
/// The blue component.
/// The alpha component.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
- public Rgba32(byte r, byte g, byte b, byte a = 255)
- : this()
+ public Rgba32(byte r, byte g, byte b, byte a)
{
this.R = r;
this.G = g;
@@ -155,6 +163,24 @@ namespace ImageSharp
this.Rgba = packed;
}
+ ///
+ /// Gets or sets the packed representation of the Rgba32 struct.
+ ///
+ public uint Rgba
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ return Unsafe.As(ref this);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set
+ {
+ Unsafe.As(ref this) = value;
+ }
+ }
+
///
public uint PackedValue { get => this.Rgba; set => this.Rgba = value; }
diff --git a/tests/ImageSharp.Benchmarks/General/PixelConversion.cs b/tests/ImageSharp.Benchmarks/General/PixelConversion.cs
new file mode 100644
index 000000000..77e728280
--- /dev/null
+++ b/tests/ImageSharp.Benchmarks/General/PixelConversion.cs
@@ -0,0 +1,156 @@
+namespace ImageSharp.Benchmarks.General
+{
+ using System.Runtime.CompilerServices;
+ using System.Runtime.InteropServices;
+
+ using BenchmarkDotNet.Attributes;
+
+ ///
+ /// When implementing TPixel --> Rgba32 style conversions on IPixel, should which API should we prefer?
+ /// 1. Rgba32 ToRgba32();
+ /// OR
+ /// 2. void CopyToRgba32(ref Rgba32 dest);
+ /// ?
+ ///
+ public class PixelConversion
+ {
+ interface ITestPixel
+ where T : struct, ITestPixel
+ {
+ Rgba32 ToRgba32();
+
+ void CopyToRgba32(ref Rgba32 dest);
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ struct TestArgb : ITestPixel
+ {
+ private byte a, r, g, b;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Rgba32 ToRgba32()
+ {
+ return new Rgba32(this.r, this.g, this.b, this.a);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void CopyToRgba32(ref Rgba32 dest)
+ {
+ dest.R = this.r;
+ dest.G = this.g;
+ dest.B = this.b;
+ dest.A = this.a;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ struct TestRgba : ITestPixel
+ {
+ private byte r, g, b, a;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Rgba32 ToRgba32()
+ {
+ return Unsafe.As(ref this);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void CopyToRgba32(ref Rgba32 dest)
+ {
+ dest = Unsafe.As(ref this);
+ }
+ }
+
+ struct ConversionRunner
+ where T : struct, ITestPixel
+ {
+ private T[] source;
+
+ private Rgba32[] dest;
+
+ public ConversionRunner(int count)
+ {
+ this.source = new T[count];
+ this.dest = new Rgba32[count];
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void RunRetvalConversion()
+ {
+ int count = this.source.Length;
+
+ ref T sourceBaseRef = ref this.source[0];
+ ref Rgba32 destBaseRef = ref this.dest[0];
+
+ for (int i = 0; i < count; i++)
+ {
+ Unsafe.Add(ref destBaseRef, i) = Unsafe.Add(ref sourceBaseRef, i).ToRgba32();
+ }
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void RunCopyToConversion()
+ {
+ int count = this.source.Length;
+
+ ref T sourceBaseRef = ref this.source[0];
+ ref Rgba32 destBaseRef = ref this.dest[0];
+
+ for (int i = 0; i < count; i++)
+ {
+ Unsafe.Add(ref sourceBaseRef, i).CopyToRgba32(ref Unsafe.Add(ref destBaseRef, i));
+ }
+ }
+ }
+
+ private ConversionRunner inOrderRunner;
+
+ private ConversionRunner permutedRunner;
+
+ [Params(128)]
+ public int Count { get; set; }
+
+ [Setup]
+ public void Setup()
+ {
+ this.inOrderRunner = new ConversionRunner(this.Count);
+ this.permutedRunner = new ConversionRunner(this.Count);
+ }
+
+ [Benchmark(Baseline = true)]
+ public void InOrderRetval()
+ {
+ this.inOrderRunner.RunRetvalConversion();
+ }
+
+ [Benchmark]
+ public void InOrderCopyTo()
+ {
+ this.inOrderRunner.RunCopyToConversion();
+ }
+
+ [Benchmark]
+ public void PermutedRetval()
+ {
+ this.permutedRunner.RunRetvalConversion();
+ }
+
+ [Benchmark]
+ public void PermutedCopyTo()
+ {
+ this.permutedRunner.RunCopyToConversion();
+ }
+ }
+
+ /*
+ * Results:
+ *
+ * Method | Count | Mean | StdDev | Scaled | Scaled-StdDev |
+ * --------------- |------ |------------ |---------- |------- |-------------- |
+ * InOrderRetval | 128 | 89.7358 ns | 2.2389 ns | 1.00 | 0.00 |
+ * InOrderCopyTo | 128 | 89.4112 ns | 2.2901 ns | 1.00 | 0.03 |
+ * PermutedRetval | 128 | 845.4038 ns | 5.6154 ns | 9.43 | 0.23 |
+ * PermutedCopyTo | 128 | 155.6004 ns | 3.8870 ns | 1.73 | 0.06 |
+ *
+ */
+}
\ No newline at end of file