Skip to content

Commit

Permalink
Merge pull request #2371 from stefannikolei/stefannikolei/rewrite_col…
Browse files Browse the repository at this point in the history
…ormatrix

Rewrite ColorMatrix
  • Loading branch information
JimBobSquarePants authored Feb 27, 2023
2 parents cac0960 + 32d08ba commit 84b261c
Show file tree
Hide file tree
Showing 26 changed files with 419 additions and 348 deletions.
32 changes: 16 additions & 16 deletions src/ImageSharp/Common/Helpers/ColorNumerics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

using System.Numerics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace SixLabors.ImageSharp;

Expand All @@ -17,7 +16,7 @@ internal static class ColorNumerics
/// Vector for converting pixel to gray value as specified by
/// ITU-R Recommendation BT.709.
/// </summary>
private static readonly Vector4 Bt709 = new Vector4(.2126f, .7152f, .0722f, 0.0f);
private static readonly Vector4 Bt709 = new(.2126f, .7152f, .0722f, 0.0f);

/// <summary>
/// Convert a pixel value to grayscale using ITU-R Recommendation BT.709.
Expand Down Expand Up @@ -137,24 +136,27 @@ public static int GetBitsNeededForColorDepth(int colors)
public static int GetColorCountForBitDepth(int bitDepth)
=> 1 << bitDepth;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static Vector4 Transform(Vector4 vector, in ColorMatrix.Impl matrix)
{
Vector4 result = matrix.X * vector.X;

result += matrix.Y * vector.Y;
result += matrix.Z * vector.Z;
result += matrix.W * vector.W;
result += matrix.V;

return result;
}

/// <summary>
/// Transforms a vector by the given color matrix.
/// </summary>
/// <param name="vector">The source vector.</param>
/// <param name="matrix">The transformation color matrix.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Transform(ref Vector4 vector, ref ColorMatrix matrix)
{
float x = vector.X;
float y = vector.Y;
float z = vector.Z;
float w = vector.W;

vector.X = (x * matrix.M11) + (y * matrix.M21) + (z * matrix.M31) + (w * matrix.M41) + matrix.M51;
vector.Y = (x * matrix.M12) + (y * matrix.M22) + (z * matrix.M32) + (w * matrix.M42) + matrix.M52;
vector.Z = (x * matrix.M13) + (y * matrix.M23) + (z * matrix.M33) + (w * matrix.M43) + matrix.M53;
vector.W = (x * matrix.M14) + (y * matrix.M24) + (z * matrix.M34) + (w * matrix.M44) + matrix.M54;
}
=> vector = Transform(vector, matrix.AsImpl());

/// <summary>
/// Bulk variant of <see cref="Transform(ref Vector4, ref ColorMatrix)"/>.
Expand All @@ -164,11 +166,9 @@ public static void Transform(ref Vector4 vector, ref ColorMatrix matrix)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Transform(Span<Vector4> vectors, ref ColorMatrix matrix)
{
ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors);

for (int i = 0; i < vectors.Length; i++)
{
ref Vector4 v = ref Unsafe.Add(ref baseRef, i);
ref Vector4 v = ref vectors[i];
Transform(ref v, ref matrix);
}
}
Expand Down
42 changes: 42 additions & 0 deletions src/ImageSharp/Diagnostics/CodeAnalysis/UnscopedRefAttribute.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
#if NET6_0
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
namespace System.Diagnostics.CodeAnalysis
{
/// <summary>
/// Used to indicate a byref escapes and is not scoped.
/// </summary>
/// <remarks>
/// <para>
/// There are several cases where the C# compiler treats a <see langword="ref"/> as implicitly
/// <see langword="scoped"/> - where the compiler does not allow the <see langword="ref"/> to escape the method.
/// </para>
/// <para>
/// For example:
/// <list type="number">
/// <item><see langword="this"/> for <see langword="struct"/> instance methods.</item>
/// <item><see langword="ref"/> parameters that refer to <see langword="ref"/> <see langword="struct"/> types.</item>
/// <item><see langword="out"/> parameters.</item>
/// </list>
/// </para>
/// <para>
/// This attribute is used in those instances where the <see langword="ref"/> should be allowed to escape.
/// </para>
/// <para>
/// Applying this attribute, in any form, has impact on consumers of the applicable API. It is necessary for
/// API authors to understand the lifetime implications of applying this attribute and how it may impact their users.
/// </para>
/// </remarks>
[global::System.AttributeUsage(
global::System.AttributeTargets.Method |
global::System.AttributeTargets.Property |
global::System.AttributeTargets.Parameter,
AllowMultiple = false,
Inherited = false)]
internal sealed class UnscopedRefAttribute : global::System.Attribute
{
}
}
#endif
208 changes: 208 additions & 0 deletions src/ImageSharp/Primitives/ColorMatrix.Impl.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,208 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

#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;

/// <summary>
/// A structure encapsulating a 5x4 matrix used for transforming the color and alpha components of an image.
/// </summary>
public partial struct ColorMatrix
{
[UnscopedRef]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal ref Impl AsImpl() => ref Unsafe.As<ColorMatrix, Impl>(ref this);

[UnscopedRef]
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal readonly ref readonly Impl AsROImpl() => ref Unsafe.As<ColorMatrix, Impl>(ref Unsafe.AsRef(in this));

internal struct Impl : IEquatable<Impl>
{
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;

result.V += right.V;

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<Impl, ColorMatrix>(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<Impl>.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);
}
}
Loading

0 comments on commit 84b261c

Please sign in to comment.