From e340b3f10388d924fe50aaebebf89464771293db Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Mon, 17 Apr 2017 00:20:10 +1000 Subject: [PATCH] Begin add vector based Color --- src/ImageSharp/Colors/Color.BulkOperations.cs | 306 +--------------- src/ImageSharp/Colors/Color.cs | 335 ++++++++++++++++++ .../Colors/Color32.BulkOperations.cs | 291 +++++++++++++++ src/ImageSharp/Colors/Color32.Transforms.cs | 2 +- src/ImageSharp/Colors/Color32.cs | 2 +- src/ImageSharp/Colors/ColorspaceTransforms.cs | 2 +- .../BulkPixelOperations{TColor}.cs | 40 +-- .../ImageSharp.Benchmarks/Samplers/Resize.cs | 21 ++ 8 files changed, 676 insertions(+), 323 deletions(-) create mode 100644 src/ImageSharp/Colors/Color.cs create mode 100644 src/ImageSharp/Colors/Color32.BulkOperations.cs 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); + } + } } }