diff --git a/src/ImageSharp/Colors/Color.BulkOperations.cs b/src/ImageSharp/Colors/Color.BulkOperations.cs
index 096e85db8..671578e4c 100644
--- a/src/ImageSharp/Colors/Color.BulkOperations.cs
+++ b/src/ImageSharp/Colors/Color.BulkOperations.cs
@@ -1,305 +1,35 @@
-//
-// Copyright (c) James Jackson-South and contributors.
-// Licensed under the Apache License, Version 2.0.
-//
-
-namespace ImageSharp
+namespace ImageSharp
{
using System;
using System.Numerics;
using System.Runtime.CompilerServices;
- using System.Runtime.InteropServices;
- ///
- /// Conains the definition of
- ///
- public partial struct Color32
+ ///
+ /// Unpacked pixel type containing four 16-bit unsigned normalized values ranging from 0 to 1.
+ /// The color components are stored in red, green, blue, and alpha order.
+ ///
+ ///
+ /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
+ /// as it avoids the need to create new values for modification operations.
+ ///
+ public partial struct Color
{
///
- /// implementation optimized for .
+ /// implementation optimized for .
///
- internal class BulkOperations : BulkPixelOperations
+ internal class BulkOperations : BulkPixelOperations
{
- ///
- /// SIMD optimized bulk implementation of
- /// that works only with `count` divisible by .
- ///
- /// The to the source colors.
- /// The to the dstination vectors.
- /// The number of pixels to convert.
- ///
- /// Implementation adapted from:
- ///
- /// http://stackoverflow.com/a/5362789
- ///
- /// TODO: We can replace this implementation in the future using new Vector API-s:
- ///
- /// https://github.com/dotnet/corefx/issues/15957
- ///
- ///
- internal static unsafe void ToVector4SimdAligned(
- BufferSpan sourceColor32s,
- BufferSpan destVectors,
- int count)
- {
- if (!Vector.IsHardwareAccelerated)
- {
- throw new InvalidOperationException(
- "Color32.BulkOperations.ToVector4SimdAligned() should not be called when Vector.IsHardwareAccelerated == false!");
- }
-
- int vecSize = Vector.Count;
-
- DebugGuard.IsTrue(
- count % vecSize == 0,
- nameof(count),
- "Argument 'count' should divisible by Vector.Count!");
-
- Vector bVec = new Vector(256.0f / 255.0f);
- Vector magicFloat = new Vector(32768.0f);
- Vector magicInt = new Vector(1191182336); // reinterpreded value of 32768.0f
- Vector mask = new Vector(255);
-
- int unpackedRawCount = count * 4;
-
- ref uint src = ref Unsafe.As(ref sourceColor32s.DangerousGetPinnableReference());
-
- using (Buffer tempBuf = new Buffer(
- unpackedRawCount + Vector.Count))
- {
- uint[] temp = tempBuf.Array;
- float[] fTemp = Unsafe.As(temp);
-
- ref UnpackedRGBA tempBase = ref Unsafe.As(ref tempBuf[0]);
-
- for (int i = 0; i < count; i++)
- {
- uint sVal = Unsafe.Add(ref src, i);
- ref UnpackedRGBA dst = ref Unsafe.Add(ref tempBase, i);
-
- // This call is the bottleneck now:
- dst.Load(sVal);
- }
-
- for (int i = 0; i < unpackedRawCount; i += vecSize)
- {
- Vector vi = new Vector(temp, i);
-
- vi &= mask;
- vi |= magicInt;
-
- Vector vf = Vector.AsVectorSingle(vi);
- vf = (vf - magicFloat) * bVec;
- vf.CopyTo(fTemp, i);
- }
-
- BufferSpan.Copy(tempBuf.Span.AsBytes(), destVectors.AsBytes(), unpackedRawCount * sizeof(uint));
- }
- }
-
///
- internal override void ToVector4(BufferSpan sourceColor32s, BufferSpan destVectors, int count)
+ internal override void ToVector4(BufferSpan sourceColors, BufferSpan destVectors, int count)
{
- if (count < 256 || !Vector.IsHardwareAccelerated)
- {
- // Doesn't worth to bother with SIMD:
- base.ToVector4(sourceColor32s, destVectors, count);
- return;
- }
-
- int remainder = count % Vector.Count;
-
- int alignedCount = count - remainder;
-
- if (alignedCount > 0)
- {
- ToVector4SimdAligned(sourceColor32s, destVectors, alignedCount);
- }
-
- if (remainder > 0)
- {
- sourceColor32s = sourceColor32s.Slice(alignedCount);
- destVectors = destVectors.Slice(alignedCount);
- base.ToVector4(sourceColor32s, destVectors, remainder);
- }
- }
-
- ///
- internal override void PackFromXyzBytes(
- BufferSpan sourceBytes,
- BufferSpan destColor32s,
- int count)
- {
- ref RGB24 sourceRef = ref Unsafe.As(ref sourceBytes.DangerousGetPinnableReference());
- ref Color32 destRef = ref destColor32s.DangerousGetPinnableReference();
+ ref Vector4 sourceRef = ref Unsafe.As(ref sourceColors.DangerousGetPinnableReference());
+ ref Vector4 destRef = ref destVectors.DangerousGetPinnableReference();
for (int i = 0; i < count; i++)
{
- ref RGB24 sp = ref Unsafe.Add(ref sourceRef, i);
- ref Color32 dp = ref Unsafe.Add(ref destRef, i);
-
- Unsafe.As(ref dp) = sp;
- dp.A = 255;
- }
- }
-
- ///
- internal override void ToXyzBytes(BufferSpan sourceColor32s, BufferSpan destBytes, int count)
- {
- ref Color32 sourceRef = ref sourceColor32s.DangerousGetPinnableReference();
- ref RGB24 destRef = ref Unsafe.As(ref destBytes.DangerousGetPinnableReference());
-
- for (int i = 0; i < count; i++)
- {
- ref Color32 sp = ref Unsafe.Add(ref sourceRef, i);
- ref RGB24 dp = ref Unsafe.Add(ref destRef, i);
-
- dp = Unsafe.As(ref sp);
- }
- }
-
- ///
- internal override unsafe void PackFromXyzwBytes(
- BufferSpan sourceBytes,
- BufferSpan destColor32s,
- int count)
- {
- BufferSpan.Copy(sourceBytes, destColor32s.AsBytes(), count * sizeof(Color32));
- }
-
- ///
- internal override unsafe void ToXyzwBytes(BufferSpan sourceColor32s, BufferSpan destBytes, int count)
- {
- BufferSpan.Copy(sourceColor32s.AsBytes(), destBytes, count * sizeof(Color32));
- }
-
- ///
- internal override void PackFromZyxBytes(
- BufferSpan sourceBytes,
- BufferSpan destColor32s,
- int count)
- {
- ref RGB24 sourceRef = ref Unsafe.As(ref sourceBytes.DangerousGetPinnableReference());
- ref Color32 destRef = ref destColor32s.DangerousGetPinnableReference();
-
- for (int i = 0; i < count; i++)
- {
- ref RGB24 sp = ref Unsafe.Add(ref sourceRef, i);
- ref Color32 dp = ref Unsafe.Add(ref destRef, i);
-
- Unsafe.As(ref dp) = sp.ToZyx();
- dp.A = 255;
- }
- }
-
- ///
- internal override void ToZyxBytes(
- BufferSpan sourceColor32s,
- BufferSpan destBytes,
- int count)
- {
- ref Color32 sourceRef = ref sourceColor32s.DangerousGetPinnableReference();
- ref RGB24 destRef = ref Unsafe.As(ref destBytes.DangerousGetPinnableReference());
-
- for (int i = 0; i < count; i++)
- {
- ref Color32 sp = ref Unsafe.Add(ref sourceRef, i);
- ref RGB24 dp = ref Unsafe.Add(ref destRef, i);
-
- dp = Unsafe.As(ref sp).ToZyx();
- }
- }
-
- ///
- internal override void PackFromZyxwBytes(
- BufferSpan sourceBytes,
- BufferSpan destColor32s,
- int count)
- {
- ref RGBA32 sourceRef = ref Unsafe.As(ref sourceBytes.DangerousGetPinnableReference());
- ref Color32 destRef = ref destColor32s.DangerousGetPinnableReference();
-
- for (int i = 0; i < count; i++)
- {
- ref RGBA32 sp = ref Unsafe.Add(ref sourceRef, i);
- ref Color32 dp = ref Unsafe.Add(ref destRef, i);
- RGBA32 zyxw = sp.ToZyxw();
- dp = Unsafe.As(ref zyxw);
- }
- }
-
- ///
- internal override void ToZyxwBytes(
- BufferSpan sourceColor32s,
- BufferSpan destBytes,
- int count)
- {
- ref Color32 sourceRef = ref sourceColor32s.DangerousGetPinnableReference();
- ref RGBA32 destRef = ref Unsafe.As(ref destBytes.DangerousGetPinnableReference());
-
- for (int i = 0; i < count; i++)
- {
- ref RGBA32 sp = ref Unsafe.As(ref Unsafe.Add(ref sourceRef, i));
- ref RGBA32 dp = ref Unsafe.Add(ref destRef, i);
- dp = sp.ToZyxw();
- }
- }
-
- ///
- /// Helper struct to manipulate 3-byte RGB data.
- ///
- [StructLayout(LayoutKind.Sequential)]
- private struct RGB24
- {
- private byte x;
-
- private byte y;
-
- private byte z;
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public RGB24 ToZyx() => new RGB24 { x = this.z, y = this.y, z = this.x };
- }
-
- ///
- /// Helper struct to manipulate 4-byte RGBA data.
- ///
- [StructLayout(LayoutKind.Sequential)]
- private struct RGBA32
- {
- private byte x;
-
- private byte y;
-
- private byte z;
-
- private byte w;
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public RGBA32 ToZyxw() => new RGBA32 { x = this.z, y = this.y, z = this.x, w = this.w };
- }
-
- ///
- /// Value type to store -s unpacked into multiple -s.
- ///
- [StructLayout(LayoutKind.Sequential)]
- private struct UnpackedRGBA
- {
- private uint r;
-
- private uint g;
-
- private uint b;
-
- private uint a;
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void Load(uint p)
- {
- this.r = p;
- this.g = p >> GreenShift;
- this.b = p >> BlueShift;
- this.a = p >> AlphaShift;
+ ref Vector4 sp = ref Unsafe.Add(ref sourceRef, i);
+ ref Vector4 dp = ref Unsafe.Add(ref destRef, i);
+ dp = sp;
}
}
}
diff --git a/src/ImageSharp/Colors/Color.cs b/src/ImageSharp/Colors/Color.cs
new file mode 100644
index 000000000..5c533e87b
--- /dev/null
+++ b/src/ImageSharp/Colors/Color.cs
@@ -0,0 +1,335 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using System.Numerics;
+ using System.Runtime.CompilerServices;
+
+ ///
+ /// Unpacked pixel type containing four 16-bit unsigned normalized values ranging from 0 to 1.
+ /// The color components are stored in red, green, blue, and alpha order.
+ ///
+ ///
+ /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
+ /// as it avoids the need to create new values for modification operations.
+ ///
+ public partial struct Color : IPixel
+ {
+ ///
+ /// The shift count for the red component
+ ///
+ private const int RedShift = 0;
+
+ ///
+ /// The shift count for the green component
+ ///
+ private const int GreenShift = 8;
+
+ ///
+ /// The shift count for the blue component
+ ///
+ private const int BlueShift = 16;
+
+ ///
+ /// The shift count for the alpha component
+ ///
+ private const int AlphaShift = 24;
+
+ ///
+ /// The maximum byte value.
+ ///
+ private static readonly Vector4 MaxBytes = new Vector4(255);
+
+ ///
+ /// The half vector value.
+ ///
+ private static readonly Vector4 Half = new Vector4(0.5F);
+
+ ///
+ /// The backing vector for SIMD support.
+ ///
+ private Vector4 backingVector;
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The red component.
+ /// The green component.
+ /// The blue component.
+ /// The alpha component.
+ public Color(byte r, byte g, byte b, byte a = 255)
+ : this()
+ {
+ this.backingVector = new Vector4(r, g, b, a) / MaxBytes;
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ /// The red component.
+ /// The green component.
+ /// The blue component.
+ /// The alpha component.
+ public Color(float r, float g, float b, float a = 1)
+ : this()
+ {
+ this.backingVector = new Vector4(r, g, b, a);
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ ///
+ /// The vector containing the components for the packed vector.
+ ///
+ public Color(Vector3 vector)
+ : this()
+ {
+ this.backingVector = new Vector4(vector, 1);
+ }
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ ///
+ /// The vector containing the components for the packed vector.
+ ///
+ public Color(Vector4 vector)
+ : this()
+ {
+ this.backingVector = vector;
+ }
+
+ ///
+ /// Gets or sets the red component.
+ ///
+ public float R
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ return this.backingVector.X;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set
+ {
+ this.backingVector.X = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the green component.
+ ///
+ public float G
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ return this.backingVector.Y;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set
+ {
+ this.backingVector.Y = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the blue component.
+ ///
+ public float B
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ return this.backingVector.Z;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set
+ {
+ this.backingVector.Z = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the alpha component.
+ ///
+ public float A
+ {
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ get
+ {
+ return this.backingVector.W;
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ set
+ {
+ this.backingVector.W = value;
+ }
+ }
+
+ ///
+ /// Compares two objects for equality.
+ ///
+ ///
+ /// The on the left side of the operand.
+ ///
+ ///
+ /// The on the right side of the operand.
+ ///
+ ///
+ /// True if the parameter is equal to the parameter; otherwise, false.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool operator ==(Color left, Color right)
+ {
+ return left.backingVector == right.backingVector;
+ }
+
+ ///
+ /// Compares two objects for equality.
+ ///
+ /// The on the left side of the operand.
+ /// The on the right side of the operand.
+ ///
+ /// True if the parameter is not equal to the parameter; otherwise, false.
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static bool operator !=(Color left, Color right)
+ {
+ return left.backingVector != right.backingVector;
+ }
+
+ ///
+ /// Creates a new instance of the struct.
+ ///
+ ///
+ /// The hexadecimal representation of the combined color components arranged
+ /// in rgb, rgba, rrggbb, or rrggbbaa format to match web syntax.
+ ///
+ ///
+ /// The .
+ ///
+ public static Color FromHex(string hex)
+ {
+ return ColorBuilder.FromHex(hex);
+ }
+
+ ///
+ public BulkPixelOperations CreateBulkOperations() => new Color.BulkOperations();
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void PackFromBytes(byte x, byte y, byte z, byte w)
+ {
+ this.backingVector = new Vector4(x, y, z, w) / MaxBytes;
+ }
+
+ ///
+ /// Converts the value of this instance to a hexadecimal string.
+ ///
+ /// A hexadecimal string representation of the value.
+ public string ToHex()
+ {
+ Vector4 vector = this.backingVector * MaxBytes;
+ vector += Half;
+ uint hexOrder = (uint)((byte)vector.X << RedShift | (byte)vector.Y << GreenShift | (byte)vector.Z << BlueShift | (byte)vector.W << AlphaShift);
+ return hexOrder.ToString("X8");
+ }
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void ToXyzBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = Vector4.Clamp(this.backingVector, Vector4.Zero, Vector4.One) * MaxBytes;
+ vector += Half;
+ bytes[startIndex] = (byte)vector.X;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.Z;
+ }
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void ToXyzwBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = Vector4.Clamp(this.backingVector, Vector4.Zero, Vector4.One) * MaxBytes;
+ vector += Half;
+ bytes[startIndex] = (byte)vector.X;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.Z;
+ bytes[startIndex + 2] = (byte)vector.W;
+ }
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void ToZyxBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = Vector4.Clamp(this.backingVector, Vector4.Zero, Vector4.One) * MaxBytes;
+ vector += Half;
+ bytes[startIndex] = (byte)vector.Z;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.X;
+ }
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void ToZyxwBytes(byte[] bytes, int startIndex)
+ {
+ Vector4 vector = Vector4.Clamp(this.backingVector, Vector4.Zero, Vector4.One) * MaxBytes;
+ vector += Half;
+ bytes[startIndex] = (byte)vector.Z;
+ bytes[startIndex + 1] = (byte)vector.Y;
+ bytes[startIndex + 2] = (byte)vector.X;
+ bytes[startIndex + 2] = (byte)vector.W;
+ }
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void PackFromVector4(Vector4 vector)
+ {
+ this.backingVector = vector;
+ }
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public Vector4 ToVector4()
+ {
+ return this.backingVector;
+ }
+
+ ///
+ public override bool Equals(object obj)
+ {
+ return (obj is Color) && this.Equals((Color)obj);
+ }
+
+ ///
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public bool Equals(Color other)
+ {
+ return this.backingVector == other.backingVector;
+ }
+
+ ///
+ /// Gets a string representation of the packed vector.
+ ///
+ /// A string representation of the packed vector.
+ public override string ToString()
+ {
+ return this.ToVector4().ToString();
+ }
+
+ ///
+ public override int GetHashCode()
+ {
+ return this.backingVector.GetHashCode();
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Colors/Color32.BulkOperations.cs b/src/ImageSharp/Colors/Color32.BulkOperations.cs
new file mode 100644
index 000000000..b86994e5d
--- /dev/null
+++ b/src/ImageSharp/Colors/Color32.BulkOperations.cs
@@ -0,0 +1,291 @@
+//
+// Copyright (c) James Jackson-South and contributors.
+// Licensed under the Apache License, Version 2.0.
+//
+
+namespace ImageSharp
+{
+ using System;
+ using System.Numerics;
+ using System.Runtime.CompilerServices;
+ using System.Runtime.InteropServices;
+
+ ///
+ /// Unpacked pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255.
+ /// The color components are stored in red, green, blue, and alpha order.
+ ///
+ ///
+ /// This struct is fully mutable. This is done (against the guidelines) for the sake of performance,
+ /// as it avoids the need to create new values for modification operations.
+ ///
+ public partial struct Color32
+ {
+ ///
+ /// implementation optimized for .
+ ///
+ internal class BulkOperations : BulkPixelOperations
+ {
+ ///
+ /// SIMD optimized bulk implementation of
+ /// that works only with `count` divisible by .
+ ///
+ /// The to the source colors.
+ /// The to the dstination vectors.
+ /// The number of pixels to convert.
+ ///
+ /// Implementation adapted from:
+ ///
+ /// http://stackoverflow.com/a/5362789
+ ///
+ /// TODO: We can replace this implementation in the future using new Vector API-s:
+ ///
+ /// https://github.com/dotnet/corefx/issues/15957
+ ///
+ ///
+ internal static unsafe void ToVector4SimdAligned(BufferSpan sourceColors, BufferSpan destVectors, int count)
+ {
+ if (!Vector.IsHardwareAccelerated)
+ {
+ throw new InvalidOperationException(
+ "Color32.BulkOperations.ToVector4SimdAligned() should not be called when Vector.IsHardwareAccelerated == false!");
+ }
+
+ int vecSize = Vector.Count;
+
+ DebugGuard.IsTrue(
+ count % vecSize == 0,
+ nameof(count),
+ "Argument 'count' should divisible by Vector.Count!");
+
+ Vector bVec = new Vector(256.0f / 255.0f);
+ Vector magicFloat = new Vector(32768.0f);
+ Vector magicInt = new Vector(1191182336); // reinterpreded value of 32768.0f
+ Vector mask = new Vector(255);
+
+ int unpackedRawCount = count * 4;
+
+ ref uint src = ref Unsafe.As(ref sourceColors.DangerousGetPinnableReference());
+
+ using (Buffer tempBuf = new Buffer(
+ unpackedRawCount + Vector.Count))
+ {
+ uint[] temp = tempBuf.Array;
+ float[] fTemp = Unsafe.As(temp);
+
+ ref UnpackedRGBA tempBase = ref Unsafe.As(ref tempBuf[0]);
+
+ for (int i = 0; i < count; i++)
+ {
+ uint sVal = Unsafe.Add(ref src, i);
+ ref UnpackedRGBA dst = ref Unsafe.Add(ref tempBase, i);
+
+ // This call is the bottleneck now:
+ dst.Load(sVal);
+ }
+
+ for (int i = 0; i < unpackedRawCount; i += vecSize)
+ {
+ Vector vi = new Vector(temp, i);
+
+ vi &= mask;
+ vi |= magicInt;
+
+ Vector vf = Vector.AsVectorSingle(vi);
+ vf = (vf - magicFloat) * bVec;
+ vf.CopyTo(fTemp, i);
+ }
+
+ BufferSpan.Copy(tempBuf.Span.AsBytes(), destVectors.AsBytes(), unpackedRawCount * sizeof(uint));
+ }
+ }
+
+ ///
+ internal override void ToVector4(BufferSpan sourceColors, BufferSpan destVectors, int count)
+ {
+ if (count < 256 || !Vector.IsHardwareAccelerated)
+ {
+ // Doesn't worth to bother with SIMD:
+ base.ToVector4(sourceColors, destVectors, count);
+ return;
+ }
+
+ int remainder = count % Vector.Count;
+
+ int alignedCount = count - remainder;
+
+ if (alignedCount > 0)
+ {
+ ToVector4SimdAligned(sourceColors, destVectors, alignedCount);
+ }
+
+ if (remainder > 0)
+ {
+ sourceColors = sourceColors.Slice(alignedCount);
+ destVectors = destVectors.Slice(alignedCount);
+ base.ToVector4(sourceColors, destVectors, remainder);
+ }
+ }
+
+ ///
+ internal override void PackFromXyzBytes(BufferSpan sourceBytes, BufferSpan destColors, int count)
+ {
+ ref RGB24 sourceRef = ref Unsafe.As(ref sourceBytes.DangerousGetPinnableReference());
+ ref Color32 destRef = ref destColors.DangerousGetPinnableReference();
+
+ for (int i = 0; i < count; i++)
+ {
+ ref RGB24 sp = ref Unsafe.Add(ref sourceRef, i);
+ ref Color32 dp = ref Unsafe.Add(ref destRef, i);
+
+ Unsafe.As(ref dp) = sp;
+ dp.A = 255;
+ }
+ }
+
+ ///
+ internal override void ToXyzBytes(BufferSpan sourceColors, BufferSpan destBytes, int count)
+ {
+ ref Color32 sourceRef = ref sourceColors.DangerousGetPinnableReference();
+ ref RGB24 destRef = ref Unsafe.As(ref destBytes.DangerousGetPinnableReference());
+
+ for (int i = 0; i < count; i++)
+ {
+ ref Color32 sp = ref Unsafe.Add(ref sourceRef, i);
+ ref RGB24 dp = ref Unsafe.Add(ref destRef, i);
+
+ dp = Unsafe.As(ref sp);
+ }
+ }
+
+ ///
+ internal override unsafe void PackFromXyzwBytes(BufferSpan sourceBytes, BufferSpan destColors, int count)
+ {
+ BufferSpan.Copy(sourceBytes, destColors.AsBytes(), count * sizeof(Color32));
+ }
+
+ ///
+ internal override unsafe void ToXyzwBytes(BufferSpan sourceColors, BufferSpan destBytes, int count)
+ {
+ BufferSpan.Copy(sourceColors.AsBytes(), destBytes, count * sizeof(Color32));
+ }
+
+ ///
+ internal override void PackFromZyxBytes(BufferSpan sourceBytes, BufferSpan destColors, int count)
+ {
+ ref RGB24 sourceRef = ref Unsafe.As(ref sourceBytes.DangerousGetPinnableReference());
+ ref Color32 destRef = ref destColors.DangerousGetPinnableReference();
+
+ for (int i = 0; i < count; i++)
+ {
+ ref RGB24 sp = ref Unsafe.Add(ref sourceRef, i);
+ ref Color32 dp = ref Unsafe.Add(ref destRef, i);
+
+ Unsafe.As(ref dp) = sp.ToZyx();
+ dp.A = 255;
+ }
+ }
+
+ ///
+ internal override void ToZyxBytes(BufferSpan sourceColors, BufferSpan destBytes, int count)
+ {
+ ref Color32 sourceRef = ref sourceColors.DangerousGetPinnableReference();
+ ref RGB24 destRef = ref Unsafe.As(ref destBytes.DangerousGetPinnableReference());
+
+ for (int i = 0; i < count; i++)
+ {
+ ref Color32 sp = ref Unsafe.Add(ref sourceRef, i);
+ ref RGB24 dp = ref Unsafe.Add(ref destRef, i);
+
+ dp = Unsafe.As(ref sp).ToZyx();
+ }
+ }
+
+ ///
+ internal override void PackFromZyxwBytes(BufferSpan sourceBytes, BufferSpan destColors, int count)
+ {
+ ref RGBA32 sourceRef = ref Unsafe.As(ref sourceBytes.DangerousGetPinnableReference());
+ ref Color32 destRef = ref destColors.DangerousGetPinnableReference();
+
+ for (int i = 0; i < count; i++)
+ {
+ ref RGBA32 sp = ref Unsafe.Add(ref sourceRef, i);
+ ref Color32 dp = ref Unsafe.Add(ref destRef, i);
+ RGBA32 zyxw = sp.ToZyxw();
+ dp = Unsafe.As(ref zyxw);
+ }
+ }
+
+ ///
+ internal override void ToZyxwBytes(BufferSpan sourceColors, BufferSpan destBytes, int count)
+ {
+ ref Color32 sourceRef = ref sourceColors.DangerousGetPinnableReference();
+ ref RGBA32 destRef = ref Unsafe.As(ref destBytes.DangerousGetPinnableReference());
+
+ for (int i = 0; i < count; i++)
+ {
+ ref RGBA32 sp = ref Unsafe.As(ref Unsafe.Add(ref sourceRef, i));
+ ref RGBA32 dp = ref Unsafe.Add(ref destRef, i);
+ dp = sp.ToZyxw();
+ }
+ }
+
+ ///
+ /// Helper struct to manipulate 3-byte RGB data.
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ private struct RGB24
+ {
+ private byte x;
+
+ private byte y;
+
+ private byte z;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public RGB24 ToZyx() => new RGB24 { x = this.z, y = this.y, z = this.x };
+ }
+
+ ///
+ /// Helper struct to manipulate 4-byte RGBA data.
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ private struct RGBA32
+ {
+ private byte x;
+
+ private byte y;
+
+ private byte z;
+
+ private byte w;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public RGBA32 ToZyxw() => new RGBA32 { x = this.z, y = this.y, z = this.x, w = this.w };
+ }
+
+ ///
+ /// Value type to store -s unpacked into multiple -s.
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ private struct UnpackedRGBA
+ {
+ private uint r;
+
+ private uint g;
+
+ private uint b;
+
+ private uint a;
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public void Load(uint p)
+ {
+ this.r = p;
+ this.g = p >> GreenShift;
+ this.b = p >> BlueShift;
+ this.a = p >> AlphaShift;
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Colors/Color32.Transforms.cs b/src/ImageSharp/Colors/Color32.Transforms.cs
index 43c947b8b..935b04217 100644
--- a/src/ImageSharp/Colors/Color32.Transforms.cs
+++ b/src/ImageSharp/Colors/Color32.Transforms.cs
@@ -8,7 +8,7 @@ namespace ImageSharp
using System.Numerics;
///
- /// Packed vector type containing four 8-bit unsigned normalized values ranging from 0 to 255.
+ /// Unpacked pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255.
/// The color components are stored in red, green, blue, and alpha order.
///
///
diff --git a/src/ImageSharp/Colors/Color32.cs b/src/ImageSharp/Colors/Color32.cs
index f7f1aceec..d646b9c4c 100644
--- a/src/ImageSharp/Colors/Color32.cs
+++ b/src/ImageSharp/Colors/Color32.cs
@@ -10,7 +10,7 @@ namespace ImageSharp
using System.Runtime.InteropServices;
///
- /// Packed pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255.
+ /// Unpacked pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255.
/// The color components are stored in red, green, blue, and alpha order.
///
///
diff --git a/src/ImageSharp/Colors/ColorspaceTransforms.cs b/src/ImageSharp/Colors/ColorspaceTransforms.cs
index ec713f915..2c767386c 100644
--- a/src/ImageSharp/Colors/ColorspaceTransforms.cs
+++ b/src/ImageSharp/Colors/ColorspaceTransforms.cs
@@ -10,7 +10,7 @@ namespace ImageSharp
using Colors.Spaces;
///
- /// Packed vector type containing four 8-bit unsigned normalized values ranging from 0 to 255.
+ /// Unpacked pixel type containing four 8-bit unsigned normalized values ranging from 0 to 255.
/// The color components are stored in red, green, blue, and alpha order.
///
///
diff --git a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs
index ccb1c2261..db0251703 100644
--- a/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs
+++ b/src/ImageSharp/Colors/PackedPixel/BulkPixelOperations{TColor}.cs
@@ -27,10 +27,7 @@ namespace ImageSharp
/// The to the source vectors.
/// The to the destination colors.
/// The number of pixels to convert.
- internal virtual void PackFromVector4(
- BufferSpan sourceVectors,
- BufferSpan destColors,
- int count)
+ internal virtual void PackFromVector4(BufferSpan sourceVectors, BufferSpan destColors, int count)
{
ref Vector4 sourceRef = ref sourceVectors.DangerousGetPinnableReference();
ref TColor destRef = ref destColors.DangerousGetPinnableReference();
@@ -49,10 +46,7 @@ namespace ImageSharp
/// The to the source colors.
/// The to the destination vectors.
/// The number of pixels to convert.
- internal virtual void ToVector4(
- BufferSpan sourceColors,
- BufferSpan destVectors,
- int count)
+ internal virtual void ToVector4(BufferSpan sourceColors, BufferSpan destVectors, int count)
{
ref TColor sourceRef = ref sourceColors.DangerousGetPinnableReference();
ref Vector4 destRef = ref destVectors.DangerousGetPinnableReference();
@@ -71,10 +65,7 @@ namespace ImageSharp
/// The to the source bytes.
/// The to the destination colors.
/// The number of pixels to convert.
- internal virtual void PackFromXyzBytes(
- BufferSpan sourceBytes,
- BufferSpan destColors,
- int count)
+ internal virtual void PackFromXyzBytes(BufferSpan sourceBytes, BufferSpan destColors, int count)
{
ref byte sourceRef = ref sourceBytes.DangerousGetPinnableReference();
ref TColor destRef = ref destColors.DangerousGetPinnableReference();
@@ -115,10 +106,7 @@ namespace ImageSharp
/// The to the source bytes.
/// The to the destination colors.
/// The number of pixels to convert.
- internal virtual void PackFromXyzwBytes(
- BufferSpan sourceBytes,
- BufferSpan destColors,
- int count)
+ internal virtual void PackFromXyzwBytes(BufferSpan sourceBytes, BufferSpan destColors, int count)
{
ref byte sourceRef = ref sourceBytes.DangerousGetPinnableReference();
ref TColor destRef = ref destColors.DangerousGetPinnableReference();
@@ -141,10 +129,7 @@ namespace ImageSharp
/// The to the source colors.
/// The to the destination bytes.
/// The number of pixels to convert.
- internal virtual void ToXyzwBytes(
- BufferSpan sourceColors,
- BufferSpan destBytes,
- int count)
+ internal virtual void ToXyzwBytes(BufferSpan sourceColors, BufferSpan destBytes, int count)
{
ref TColor sourceRef = ref sourceColors.DangerousGetPinnableReference();
byte[] dest = destBytes.Array;
@@ -162,10 +147,7 @@ namespace ImageSharp
/// The to the source bytes.
/// The to the destination colors.
/// The number of pixels to convert.
- internal virtual void PackFromZyxBytes(
- BufferSpan sourceBytes,
- BufferSpan destColors,
- int count)
+ internal virtual void PackFromZyxBytes(BufferSpan sourceBytes, BufferSpan destColors, int count)
{
ref byte sourceRef = ref sourceBytes.DangerousGetPinnableReference();
ref TColor destRef = ref destColors.DangerousGetPinnableReference();
@@ -206,10 +188,7 @@ namespace ImageSharp
/// The to the source bytes.
/// The to the destination colors.
/// The number of pixels to convert.
- internal virtual void PackFromZyxwBytes(
- BufferSpan sourceBytes,
- BufferSpan destColors,
- int count)
+ internal virtual void PackFromZyxwBytes(BufferSpan sourceBytes, BufferSpan destColors, int count)
{
ref byte sourceRef = ref sourceBytes.DangerousGetPinnableReference();
ref TColor destRef = ref destColors.DangerousGetPinnableReference();
@@ -232,10 +211,7 @@ namespace ImageSharp
/// The to the source colors.
/// The to the destination bytes.
/// The number of pixels to convert.
- internal virtual void ToZyxwBytes(
- BufferSpan sourceColors,
- BufferSpan destBytes,
- int count)
+ internal virtual void ToZyxwBytes(BufferSpan sourceColors, BufferSpan destBytes, int count)
{
ref TColor sourceRef = ref sourceColors.DangerousGetPinnableReference();
byte[] dest = destBytes.Array;
diff --git a/tests/ImageSharp.Benchmarks/Samplers/Resize.cs b/tests/ImageSharp.Benchmarks/Samplers/Resize.cs
index 04570ce8f..c82990134 100644
--- a/tests/ImageSharp.Benchmarks/Samplers/Resize.cs
+++ b/tests/ImageSharp.Benchmarks/Samplers/Resize.cs
@@ -11,6 +11,7 @@ namespace ImageSharp.Benchmarks
using BenchmarkDotNet.Attributes;
using CoreSize = ImageSharp.Size;
using CoreImage = ImageSharp.Image;
+ using CoreVectorImage = ImageSharp.Image;
public class Resize : BenchmarkBase
{
@@ -44,6 +45,16 @@ namespace ImageSharp.Benchmarks
}
}
+ [Benchmark(Description = "ImageSharp Vector Resize")]
+ public CoreSize ResizeCoreVector()
+ {
+ using (CoreVectorImage image = new CoreVectorImage(2000, 2000))
+ {
+ image.Resize(400, 400);
+ return new CoreSize(image.Width, image.Height);
+ }
+ }
+
[Benchmark(Description = "ImageSharp Compand Resize")]
public CoreSize ResizeCoreCompand()
{
@@ -53,5 +64,15 @@ namespace ImageSharp.Benchmarks
return new CoreSize(image.Width, image.Height);
}
}
+
+ [Benchmark(Description = "ImageSharp Vector Compand Resize")]
+ public CoreSize ResizeCoreVectorCompand()
+ {
+ using (CoreVectorImage image = new CoreVectorImage(2000, 2000))
+ {
+ image.Resize(400, 400, true);
+ return new CoreSize(image.Width, image.Height);
+ }
+ }
}
}