Skip to content

Commit 84b261c

Browse files
Merge pull request #2371 from stefannikolei/stefannikolei/rewrite_colormatrix
Rewrite ColorMatrix
2 parents cac0960 + 32d08ba commit 84b261c

26 files changed

+419
-348
lines changed

src/ImageSharp/Common/Helpers/ColorNumerics.cs

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33

44
using System.Numerics;
55
using System.Runtime.CompilerServices;
6-
using System.Runtime.InteropServices;
76

87
namespace SixLabors.ImageSharp;
98

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

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

139+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
140+
internal static Vector4 Transform(Vector4 vector, in ColorMatrix.Impl matrix)
141+
{
142+
Vector4 result = matrix.X * vector.X;
143+
144+
result += matrix.Y * vector.Y;
145+
result += matrix.Z * vector.Z;
146+
result += matrix.W * vector.W;
147+
result += matrix.V;
148+
149+
return result;
150+
}
151+
140152
/// <summary>
141153
/// Transforms a vector by the given color matrix.
142154
/// </summary>
143155
/// <param name="vector">The source vector.</param>
144156
/// <param name="matrix">The transformation color matrix.</param>
145157
[MethodImpl(MethodImplOptions.AggressiveInlining)]
146158
public static void Transform(ref Vector4 vector, ref ColorMatrix matrix)
147-
{
148-
float x = vector.X;
149-
float y = vector.Y;
150-
float z = vector.Z;
151-
float w = vector.W;
152-
153-
vector.X = (x * matrix.M11) + (y * matrix.M21) + (z * matrix.M31) + (w * matrix.M41) + matrix.M51;
154-
vector.Y = (x * matrix.M12) + (y * matrix.M22) + (z * matrix.M32) + (w * matrix.M42) + matrix.M52;
155-
vector.Z = (x * matrix.M13) + (y * matrix.M23) + (z * matrix.M33) + (w * matrix.M43) + matrix.M53;
156-
vector.W = (x * matrix.M14) + (y * matrix.M24) + (z * matrix.M34) + (w * matrix.M44) + matrix.M54;
157-
}
159+
=> vector = Transform(vector, matrix.AsImpl());
158160

159161
/// <summary>
160162
/// Bulk variant of <see cref="Transform(ref Vector4, ref ColorMatrix)"/>.
@@ -164,11 +166,9 @@ public static void Transform(ref Vector4 vector, ref ColorMatrix matrix)
164166
[MethodImpl(MethodImplOptions.AggressiveInlining)]
165167
public static void Transform(Span<Vector4> vectors, ref ColorMatrix matrix)
166168
{
167-
ref Vector4 baseRef = ref MemoryMarshal.GetReference(vectors);
168-
169169
for (int i = 0; i < vectors.Length; i++)
170170
{
171-
ref Vector4 v = ref Unsafe.Add(ref baseRef, i);
171+
ref Vector4 v = ref vectors[i];
172172
Transform(ref v, ref matrix);
173173
}
174174
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
#if NET6_0
4+
// Licensed to the .NET Foundation under one or more agreements.
5+
// The .NET Foundation licenses this file to you under the MIT license.
6+
namespace System.Diagnostics.CodeAnalysis
7+
{
8+
/// <summary>
9+
/// Used to indicate a byref escapes and is not scoped.
10+
/// </summary>
11+
/// <remarks>
12+
/// <para>
13+
/// There are several cases where the C# compiler treats a <see langword="ref"/> as implicitly
14+
/// <see langword="scoped"/> - where the compiler does not allow the <see langword="ref"/> to escape the method.
15+
/// </para>
16+
/// <para>
17+
/// For example:
18+
/// <list type="number">
19+
/// <item><see langword="this"/> for <see langword="struct"/> instance methods.</item>
20+
/// <item><see langword="ref"/> parameters that refer to <see langword="ref"/> <see langword="struct"/> types.</item>
21+
/// <item><see langword="out"/> parameters.</item>
22+
/// </list>
23+
/// </para>
24+
/// <para>
25+
/// This attribute is used in those instances where the <see langword="ref"/> should be allowed to escape.
26+
/// </para>
27+
/// <para>
28+
/// Applying this attribute, in any form, has impact on consumers of the applicable API. It is necessary for
29+
/// API authors to understand the lifetime implications of applying this attribute and how it may impact their users.
30+
/// </para>
31+
/// </remarks>
32+
[global::System.AttributeUsage(
33+
global::System.AttributeTargets.Method |
34+
global::System.AttributeTargets.Property |
35+
global::System.AttributeTargets.Parameter,
36+
AllowMultiple = false,
37+
Inherited = false)]
38+
internal sealed class UnscopedRefAttribute : global::System.Attribute
39+
{
40+
}
41+
}
42+
#endif
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Six Labors Split License.
3+
4+
#pragma warning disable SA1117 // Parameters should be on same line or separate lines
5+
using System.Diagnostics.CodeAnalysis;
6+
using System.Numerics;
7+
using System.Runtime.CompilerServices;
8+
9+
namespace SixLabors.ImageSharp;
10+
11+
/// <summary>
12+
/// A structure encapsulating a 5x4 matrix used for transforming the color and alpha components of an image.
13+
/// </summary>
14+
public partial struct ColorMatrix
15+
{
16+
[UnscopedRef]
17+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
18+
internal ref Impl AsImpl() => ref Unsafe.As<ColorMatrix, Impl>(ref this);
19+
20+
[UnscopedRef]
21+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
22+
internal readonly ref readonly Impl AsROImpl() => ref Unsafe.As<ColorMatrix, Impl>(ref Unsafe.AsRef(in this));
23+
24+
internal struct Impl : IEquatable<Impl>
25+
{
26+
public Vector4 X;
27+
public Vector4 Y;
28+
public Vector4 Z;
29+
public Vector4 W;
30+
public Vector4 V;
31+
32+
public static Impl Identity
33+
{
34+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
35+
get
36+
{
37+
Impl result;
38+
39+
result.X = Vector4.UnitX;
40+
result.Y = Vector4.UnitY;
41+
result.Z = Vector4.UnitZ;
42+
result.W = Vector4.UnitW;
43+
result.V = Vector4.Zero;
44+
45+
return result;
46+
}
47+
}
48+
49+
public readonly bool IsIdentity
50+
{
51+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
52+
get =>
53+
(this.X == Vector4.UnitX)
54+
&& (this.Y == Vector4.UnitY)
55+
&& (this.Z == Vector4.UnitZ)
56+
&& (this.W == Vector4.UnitW)
57+
&& (this.V == Vector4.Zero);
58+
}
59+
60+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
61+
public static Impl operator +(in Impl left, in Impl right)
62+
{
63+
Impl result;
64+
65+
result.X = left.X + right.X;
66+
result.Y = left.Y + right.Y;
67+
result.Z = left.Z + right.Z;
68+
result.W = left.W + right.W;
69+
result.V = left.V + right.V;
70+
71+
return result;
72+
}
73+
74+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
75+
public static Impl operator -(in Impl left, in Impl right)
76+
{
77+
Impl result;
78+
79+
result.X = left.X - right.X;
80+
result.Y = left.Y - right.Y;
81+
result.Z = left.Z - right.Z;
82+
result.W = left.W - right.W;
83+
result.V = left.V - right.V;
84+
85+
return result;
86+
}
87+
88+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
89+
public static Impl operator -(in Impl value)
90+
{
91+
Impl result;
92+
93+
result.X = -value.X;
94+
result.Y = -value.Y;
95+
result.Z = -value.Z;
96+
result.W = -value.W;
97+
result.V = -value.V;
98+
99+
return result;
100+
}
101+
102+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
103+
public static Impl operator *(in Impl left, in Impl right)
104+
{
105+
Impl result;
106+
107+
// result.X = Transform(left.X, in right);
108+
result.X = right.X * left.X.X;
109+
result.X += right.Y * left.X.Y;
110+
result.X += right.Z * left.X.Z;
111+
result.X += right.W * left.X.W;
112+
113+
// result.Y = Transform(left.Y, in right);
114+
result.Y = right.X * left.Y.X;
115+
result.Y += right.Y * left.Y.Y;
116+
result.Y += right.Z * left.Y.Z;
117+
result.Y += right.W * left.Y.W;
118+
119+
// result.Z = Transform(left.Z, in right);
120+
result.Z = right.X * left.Z.X;
121+
result.Z += right.Y * left.Z.Y;
122+
result.Z += right.Z * left.Z.Z;
123+
result.Z += right.W * left.Z.W;
124+
125+
// result.W = Transform(left.W, in right);
126+
result.W = right.X * left.W.X;
127+
result.W += right.Y * left.W.Y;
128+
result.W += right.Z * left.W.Z;
129+
result.W += right.W * left.W.W;
130+
131+
// result.V = Transform(left.V, in right);
132+
result.V = right.X * left.V.X;
133+
result.V += right.Y * left.V.Y;
134+
result.V += right.Z * left.V.Z;
135+
result.V += right.W * left.V.W;
136+
137+
result.V += right.V;
138+
139+
return result;
140+
}
141+
142+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
143+
public static Impl operator *(in Impl left, float right)
144+
{
145+
Impl result;
146+
147+
result.X = left.X * right;
148+
result.Y = left.Y * right;
149+
result.Z = left.Z * right;
150+
result.W = left.W * right;
151+
result.V = left.V * right;
152+
153+
return result;
154+
}
155+
156+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
157+
public static bool operator ==(in Impl left, in Impl right) =>
158+
(left.X == right.X)
159+
&& (left.Y == right.Y)
160+
&& (left.Z == right.Z)
161+
&& (left.W == right.W)
162+
&& (left.V == right.V);
163+
164+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
165+
public static bool operator !=(in Impl left, in Impl right) =>
166+
(left.X != right.X)
167+
&& (left.Y != right.Y)
168+
&& (left.Z != right.Z)
169+
&& (left.W != right.W)
170+
&& (left.V != right.V);
171+
172+
[UnscopedRef]
173+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
174+
public ref ColorMatrix AsColorMatrix() => ref Unsafe.As<Impl, ColorMatrix>(ref this);
175+
176+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
177+
public void Init(
178+
float m11, float m12, float m13, float m14,
179+
float m21, float m22, float m23, float m24,
180+
float m31, float m32, float m33, float m34,
181+
float m41, float m42, float m43, float m44,
182+
float m51, float m52, float m53, float m54)
183+
{
184+
this.X = new Vector4(m11, m12, m13, m14);
185+
this.Y = new Vector4(m21, m22, m23, m24);
186+
this.Z = new Vector4(m31, m32, m33, m34);
187+
this.W = new Vector4(m41, m42, m43, m44);
188+
this.V = new Vector4(m51, m52, m53, m54);
189+
}
190+
191+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
192+
public override readonly bool Equals([NotNullWhen(true)] object? obj)
193+
=> (obj is ColorMatrix other) && this.Equals(in other.AsImpl());
194+
195+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
196+
public readonly bool Equals(in Impl other) =>
197+
this.X.Equals(other.X)
198+
&& this.Y.Equals(other.Y)
199+
&& this.Z.Equals(other.Z)
200+
&& this.W.Equals(other.W)
201+
&& this.V.Equals(other.V);
202+
203+
bool IEquatable<Impl>.Equals(Impl other) => this.Equals(in other);
204+
205+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
206+
public override readonly int GetHashCode() => HashCode.Combine(this.X, this.Y, this.Z, this.W, this.V);
207+
}
208+
}

0 commit comments

Comments
 (0)