diff --git a/src/ImageSharp/Primitives/ColorMatrix.Impl.cs b/src/ImageSharp/Primitives/ColorMatrix.Impl.cs new file mode 100644 index 0000000000..d71f392730 --- /dev/null +++ b/src/ImageSharp/Primitives/ColorMatrix.Impl.cs @@ -0,0 +1,209 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +#if NET7_0_OR_GREATER +#pragma warning disable SA1117 // Parameters should be on same line or separate lines +using System.Diagnostics.CodeAnalysis; +using System.Numerics; +using System.Runtime.CompilerServices; + +namespace SixLabors.ImageSharp; + +/// +/// A structure encapsulating a 5x4 matrix used for transforming the color and alpha components of an image. +/// +public partial struct ColorMatrix +{ + [UnscopedRef] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal ref Impl AsImpl() => ref Unsafe.As(ref this); + + [UnscopedRef] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal readonly ref readonly Impl AsROImpl() => ref Unsafe.As(ref Unsafe.AsRef(in this)); + + internal struct Impl : IEquatable + { + public Vector4 X; + public Vector4 Y; + public Vector4 Z; + public Vector4 W; + public Vector4 V; + + public static Impl Identity + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + Impl result; + + result.X = Vector4.UnitX; + result.Y = Vector4.UnitY; + result.Z = Vector4.UnitZ; + result.W = Vector4.UnitW; + result.V = Vector4.Zero; + + return result; + } + } + + public readonly bool IsIdentity + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => + (this.X == Vector4.UnitX) + && (this.Y == Vector4.UnitY) + && (this.Z == Vector4.UnitZ) + && (this.W == Vector4.UnitW) + && (this.V == Vector4.Zero); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Impl operator +(in Impl left, in Impl right) + { + Impl result; + + result.X = left.X + right.X; + result.Y = left.Y + right.Y; + result.Z = left.Z + right.Z; + result.W = left.W + right.W; + result.V = left.V + right.V; + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Impl operator -(in Impl left, in Impl right) + { + Impl result; + + result.X = left.X - right.X; + result.Y = left.Y - right.Y; + result.Z = left.Z - right.Z; + result.W = left.W - right.W; + result.V = left.V - right.V; + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Impl operator -(in Impl value) + { + Impl result; + + result.X = -value.X; + result.Y = -value.Y; + result.Z = -value.Z; + result.W = -value.W; + result.V = -value.V; + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Impl operator *(in Impl left, in Impl right) + { + Impl result; + + // result.X = Transform(left.X, in right); + result.X = right.X * left.X.X; + result.X += right.Y * left.X.Y; + result.X += right.Z * left.X.Z; + result.X += right.W * left.X.W; + + // result.Y = Transform(left.Y, in right); + result.Y = right.X * left.Y.X; + result.Y += right.Y * left.Y.Y; + result.Y += right.Z * left.Y.Z; + result.Y += right.W * left.Y.W; + + // result.Z = Transform(left.Z, in right); + result.Z = right.X * left.Z.X; + result.Z += right.Y * left.Z.Y; + result.Z += right.Z * left.Z.Z; + result.Z += right.W * left.Z.W; + + // result.W = Transform(left.W, in right); + result.W = right.X * left.W.X; + result.W += right.Y * left.W.Y; + result.W += right.Z * left.W.Z; + result.W += right.W * left.W.W; + + // result.V = Transform(left.V, in right); + result.V = right.X * left.V.X; + result.V += right.Y * left.V.Y; + result.V += right.Z * left.V.Z; + result.V += right.W * left.V.W; + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static Impl operator *(in Impl left, float right) + { + Impl result; + + result.X = left.X * right; + result.Y = left.Y * right; + result.Z = left.Z * right; + result.W = left.W * right; + result.V = left.V * right; + + return result; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(in Impl left, in Impl right) => + (left.X == right.X) + && (left.Y == right.Y) + && (left.Z == right.Z) + && (left.W == right.W) + && (left.V == right.V); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(in Impl left, in Impl right) => + (left.X != right.X) + && (left.Y != right.Y) + && (left.Z != right.Z) + && (left.W != right.W) + && (left.V != right.V); + + [UnscopedRef] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref ColorMatrix AsColorMatrix() => ref Unsafe.As(ref this); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Init( + float m11, float m12, float m13, float m14, + float m21, float m22, float m23, float m24, + float m31, float m32, float m33, float m34, + float m41, float m42, float m43, float m44, + float m51, float m52, float m53, float m54) + { + this.X = new Vector4(m11, m12, m13, m14); + this.Y = new Vector4(m21, m22, m23, m24); + this.Z = new Vector4(m31, m32, m33, m34); + this.W = new Vector4(m41, m42, m43, m44); + this.V = new Vector4(m51, m52, m53, m54); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly bool Equals([NotNullWhen(true)] object? obj) + => (obj is ColorMatrix other) && this.Equals(in other.AsImpl()); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(in Impl other) => + this.X.Equals(other.X) + && this.Y.Equals(other.Y) + && this.Z.Equals(other.Z) + && this.W.Equals(other.W) + && this.V.Equals(other.V); + + bool IEquatable.Equals(Impl other) => this.Equals(in other); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly int GetHashCode() => HashCode.Combine(this.X, this.Y, this.Z, this.W, this.V); + } +} +#endif + diff --git a/src/ImageSharp/Primitives/ColorMatrix.cs b/src/ImageSharp/Primitives/ColorMatrix.cs index e06dc1f6a5..fcc179266c 100644 --- a/src/ImageSharp/Primitives/ColorMatrix.cs +++ b/src/ImageSharp/Primitives/ColorMatrix.cs @@ -2,6 +2,269 @@ // Licensed under the Six Labors Split License. #pragma warning disable SA1117 // Parameters should be on same line or separate lines + +#if NET7_0_OR_GREATER + +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace SixLabors.ImageSharp; + +/// +/// A structure encapsulating a 5x4 matrix used for transforming the color and alpha components of an image. +/// +[StructLayout(LayoutKind.Sequential)] +public partial struct ColorMatrix : IEquatable +{ + /// + /// Value at row 1, column 1 of the matrix. + /// + public float M11; + + /// + /// Value at row 1, column 2 of the matrix. + /// + public float M12; + + /// + /// Value at row 1, column 3 of the matrix. + /// + public float M13; + + /// + /// Value at row 1, column 4 of the matrix. + /// + public float M14; + + /// + /// Value at row 2, column 1 of the matrix. + /// + public float M21; + + /// + /// Value at row 2, column 2 of the matrix. + /// + public float M22; + + /// + /// Value at row 2, column 3 of the matrix. + /// + public float M23; + + /// + /// Value at row 2, column 4 of the matrix. + /// + public float M24; + + /// + /// Value at row 3, column 1 of the matrix. + /// + public float M31; + + /// + /// Value at row 3, column 2 of the matrix. + /// + public float M32; + + /// + /// Value at row 3, column 3 of the matrix. + /// + public float M33; + + /// + /// Value at row 3, column 4 of the matrix. + /// + public float M34; + + /// + /// Value at row 4, column 1 of the matrix. + /// + public float M41; + + /// + /// Value at row 4, column 2 of the matrix. + /// + public float M42; + + /// + /// Value at row 4, column 3 of the matrix. + /// + public float M43; + + /// + /// Value at row 4, column 4 of the matrix. + /// + public float M44; + + /// + /// Value at row 5, column 1 of the matrix. + /// + public float M51; + + /// + /// Value at row 5, column 2 of the matrix. + /// + public float M52; + + /// + /// Value at row 5, column 3 of the matrix. + /// + public float M53; + + /// + /// Value at row 5, column 4 of the matrix. + /// + public float M54; + + /// + /// Initializes a new instance of the struct. + /// + /// The value at row 1, column 1 of the matrix. + /// The value at row 1, column 2 of the matrix. + /// The value at row 1, column 3 of the matrix. + /// The value at row 1, column 4 of the matrix. + /// The value at row 2, column 1 of the matrix. + /// The value at row 2, column 2 of the matrix. + /// The value at row 2, column 3 of the matrix. + /// The value at row 2, column 4 of the matrix. + /// The value at row 3, column 1 of the matrix. + /// The value at row 3, column 2 of the matrix. + /// The value at row 3, column 3 of the matrix. + /// The value at row 3, column 4 of the matrix. + /// The value at row 4, column 1 of the matrix. + /// The value at row 4, column 2 of the matrix. + /// The value at row 4, column 3 of the matrix. + /// The value at row 4, column 4 of the matrix. + /// The value at row 5, column 1 of the matrix. + /// The value at row 5, column 2 of the matrix. + /// The value at row 5, column 3 of the matrix. + /// The value at row 5, column 4 of the matrix. + public ColorMatrix(float m11, float m12, float m13, float m14, + float m21, float m22, float m23, float m24, + float m31, float m32, float m33, float m34, + float m41, float m42, float m43, float m44, + float m51, float m52, float m53, float m54) + { + Unsafe.SkipInit(out this); + + this.AsImpl().Init(m11, m12, m13, m14, m21, m22, m23, m24, m31, m32, m33, m34, m41, m42, m43, m44, m51, m52, m53, + m54); + } + + /// + /// Gets the multiplicative identity matrix. + /// + public static ColorMatrix Identity + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => Impl.Identity.AsColorMatrix(); + } + + /// + /// Gets a value indicating whether the matrix is the identity matrix. + /// + public bool IsIdentity + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => this.AsROImpl().IsIdentity; + } + + /// + /// Adds two matrices together. + /// + /// The first source matrix. + /// The second source matrix. + /// The resulting matrix. + public static ColorMatrix operator +(ColorMatrix value1, ColorMatrix value2) + => (value1.AsImpl() + value2.AsImpl()).AsColorMatrix(); + + /// + /// Subtracts the second matrix from the first. + /// + /// The first source matrix. + /// The second source matrix. + /// The result of the subtraction. + public static ColorMatrix operator -(ColorMatrix value1, ColorMatrix value2) + => (value1.AsImpl() - value2.AsImpl()).AsColorMatrix(); + + /// + /// Returns a new matrix with the negated elements of the given matrix. + /// + /// The source matrix. + /// The negated matrix. + public static ColorMatrix operator -(ColorMatrix value) + => (-value.AsImpl()).AsColorMatrix(); + + /// + /// Multiplies a matrix by another matrix. + /// + /// The first source matrix. + /// The second source matrix. + /// The result of the multiplication. + public static ColorMatrix operator *(ColorMatrix value1, ColorMatrix value2) + => (value1.AsImpl() * value2.AsImpl()).AsColorMatrix(); + + /// + /// Multiplies a matrix by a scalar value. + /// + /// The source matrix. + /// The scaling factor. + /// The scaled matrix. + public static ColorMatrix operator *(ColorMatrix value1, float value2) + => (value1.AsImpl() * value2).AsColorMatrix(); + + /// + /// Returns a boolean indicating whether the given two matrices are equal. + /// + /// The first matrix to compare. + /// The second matrix to compare. + /// True if the given matrices are equal; False otherwise. + public static bool operator ==(ColorMatrix value1, ColorMatrix value2) + => value1.AsImpl() == value2.AsImpl(); + + /// + /// Returns a boolean indicating whether the given two matrices are not equal. + /// + /// The first matrix to compare. + /// The second matrix to compare. + /// True if the given matrices are equal; False otherwise. + public static bool operator !=(ColorMatrix value1, ColorMatrix value2) + => value1.AsImpl() != value2.AsImpl(); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override readonly bool Equals([NotNullWhen(true)] object? obj) + => this.AsROImpl().Equals(obj); + + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public readonly bool Equals(ColorMatrix other) + => this.AsROImpl().Equals(in other.AsImpl()); + + /// + public override int GetHashCode() + => this.AsROImpl().GetHashCode(); + + /// + public override string ToString() + { + CultureInfo ci = CultureInfo.CurrentCulture; + + return string.Format( + ci, + "{{ {{M11:{0} M12:{1} M13:{2} M14:{3}}} {{M21:{4} M22:{5} M23:{6} M24:{7}}} {{M31:{8} M32:{9} M33:{10} M34:{11}}} {{M41:{12} M42:{13} M43:{14} M44:{15}}} {{M51:{16} M52:{17} M53:{18} M54:{19}}} }}", + this.M11.ToString(ci), this.M12.ToString(ci), this.M13.ToString(ci), this.M14.ToString(ci), + this.M21.ToString(ci), this.M22.ToString(ci), this.M23.ToString(ci), this.M24.ToString(ci), + this.M31.ToString(ci), this.M32.ToString(ci), this.M33.ToString(ci), this.M34.ToString(ci), + this.M41.ToString(ci), this.M42.ToString(ci), this.M43.ToString(ci), this.M44.ToString(ci), + this.M51.ToString(ci), this.M52.ToString(ci), this.M53.ToString(ci), this.M54.ToString(ci)); + } +} +#endif +#if NET6_0 + using System.Globalization; using System.Runtime.InteropServices; @@ -455,3 +718,5 @@ public struct ColorMatrix : IEquatable this.M51.ToString(ci), this.M52.ToString(ci), this.M53.ToString(ci), this.M54.ToString(ci)); } } + +#endif