Skip to content

Commit 2d979f9

Browse files
Merge pull request #2645 from SixLabors/js/pixelsformats
Modernize and optimize pixel format operations across platforms.
2 parents f926822 + 3a3ff89 commit 2d979f9

File tree

232 files changed

+6882
-8951
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

232 files changed

+6882
-8951
lines changed

.editorconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,8 @@ dotnet_diagnostic.IDE0063.severity = suggestion
172172
csharp_using_directive_placement = outside_namespace:warning
173173
# Modifier preferences
174174
csharp_prefer_static_local_function = true:warning
175+
# Primary constructor preferences
176+
csharp_style_prefer_primary_constructors = false:none
175177

176178
##########################################
177179
# Unnecessary Code Rules

src/ImageSharp.ruleset

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<RuleSet Name="ImageSharp" ToolsVersion="17.0">
33
<Include Path="..\shared-infrastructure\sixlabors.ruleset" Action="Default" />
4-
<Rules AnalyzerId="StyleCop.Analyzers" RuleNamespace="StyleCop.Analyzers">
5-
</Rules>
6-
</RuleSet>
4+
</RuleSet>

src/ImageSharp/Color/Color.cs

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,8 @@ public static Color FromPixel<TPixel>(TPixel pixel)
9898
{
9999
return new(pixel.ToScaledVector4());
100100
}
101-
else
102-
{
103-
return new(pixel);
104-
}
101+
102+
return new(pixel);
105103
}
106104

107105
/// <summary>
@@ -250,15 +248,12 @@ public Color WithAlpha(float alpha)
250248
[MethodImpl(InliningOptions.ShortMethod)]
251249
public string ToHex()
252250
{
253-
Rgba32 rgba = default;
254251
if (this.boxedHighPrecisionPixel is not null)
255252
{
256-
this.boxedHighPrecisionPixel.ToRgba32(ref rgba);
257-
return rgba.ToHex();
253+
return this.boxedHighPrecisionPixel.ToRgba32().ToHex();
258254
}
259255

260-
rgba.FromScaledVector4(this.data);
261-
return rgba.ToHex();
256+
return Rgba32.FromScaledVector4(this.data).ToHex();
262257
}
263258

264259
/// <inheritdoc />
@@ -280,14 +275,10 @@ public TPixel ToPixel<TPixel>()
280275

281276
if (this.boxedHighPrecisionPixel is null)
282277
{
283-
pixel = default;
284-
pixel.FromScaledVector4(this.data);
285-
return pixel;
278+
return TPixel.FromScaledVector4(this.data);
286279
}
287280

288-
pixel = default;
289-
pixel.FromScaledVector4(this.boxedHighPrecisionPixel.ToScaledVector4());
290-
return pixel;
281+
return TPixel.FromScaledVector4(this.boxedHighPrecisionPixel.ToScaledVector4());
291282
}
292283

293284
/// <summary>

src/ImageSharp/Common/Helpers/ColorNumerics.cs

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,34 @@ public static int GetBT709Luminance(ref Vector4 vector, int luminanceLevels)
4141
public static byte Get8BitBT709Luminance(byte r, byte g, byte b)
4242
=> (byte)((r * .2126F) + (g * .7152F) + (b * .0722F) + 0.5F);
4343

44+
/// <summary>
45+
/// Gets the luminance from the rgb components using the formula
46+
/// as specified by ITU-R Recommendation BT.709.
47+
/// </summary>
48+
/// <param name="r">The red component.</param>
49+
/// <param name="g">The green component.</param>
50+
/// <param name="b">The blue component.</param>
51+
/// <returns>The <see cref="byte"/>.</returns>
52+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
53+
public static byte Get8BitBT709Luminance(ushort r, ushort g, ushort b)
54+
=> (byte)((From16BitTo8Bit(r) * .2126F) +
55+
(From16BitTo8Bit(g) * .7152F) +
56+
(From16BitTo8Bit(b) * .0722F) + 0.5F);
57+
58+
/// <summary>
59+
/// Gets the luminance from the rgb components using the formula as
60+
/// specified by ITU-R Recommendation BT.709.
61+
/// </summary>
62+
/// <param name="r">The red component.</param>
63+
/// <param name="g">The green component.</param>
64+
/// <param name="b">The blue component.</param>
65+
/// <returns>The <see cref="ushort"/>.</returns>
66+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
67+
public static ushort Get16BitBT709Luminance(byte r, byte g, byte b)
68+
=> (ushort)((From8BitTo16Bit(r) * .2126F) +
69+
(From8BitTo16Bit(g) * .7152F) +
70+
(From8BitTo16Bit(b) * .0722F) + 0.5F);
71+
4472
/// <summary>
4573
/// Gets the luminance from the rgb components using the formula as
4674
/// specified by ITU-R Recommendation BT.709.
@@ -72,8 +100,8 @@ public static ushort Get16BitBT709Luminance(float r, float g, float b)
72100
/// <param name="component">The 8 bit component value.</param>
73101
/// <returns>The <see cref="byte"/></returns>
74102
[MethodImpl(MethodImplOptions.AggressiveInlining)]
75-
public static byte DownScaleFrom16BitTo8Bit(ushort component)
76-
{
103+
public static byte From16BitTo8Bit(ushort component) =>
104+
77105
// To scale to 8 bits From a 16-bit value V the required value (from the PNG specification) is:
78106
//
79107
// (V * 255) / 65535
@@ -102,8 +130,7 @@ public static byte DownScaleFrom16BitTo8Bit(ushort component)
102130
// An alternative arithmetic calculation which also gives no errors is:
103131
//
104132
// (V * 255 + 32895) >> 16
105-
return (byte)(((component * 255) + 32895) >> 16);
106-
}
133+
(byte)(((component * 255) + 32895) >> 16);
107134

108135
/// <summary>
109136
/// Scales a value from an 8 bit <see cref="byte"/> to
@@ -112,7 +139,7 @@ public static byte DownScaleFrom16BitTo8Bit(ushort component)
112139
/// <param name="component">The 8 bit component value.</param>
113140
/// <returns>The <see cref="ushort"/></returns>
114141
[MethodImpl(MethodImplOptions.AggressiveInlining)]
115-
public static ushort UpscaleFrom8BitTo16Bit(byte component)
142+
public static ushort From8BitTo16Bit(byte component)
116143
=> (ushort)(component * 257);
117144

118145
/// <summary>

src/ImageSharp/Common/Helpers/Numerics.cs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
using System.Runtime.CompilerServices;
66
using System.Runtime.InteropServices;
77
using System.Runtime.Intrinsics;
8-
using System.Runtime.Intrinsics.Arm;
98
using System.Runtime.Intrinsics.X86;
109

1110
namespace SixLabors.ImageSharp;
@@ -61,6 +60,12 @@ public static int LeastCommonMultiple(int a, int b)
6160
[MethodImpl(MethodImplOptions.AggressiveInlining)]
6261
public static nint Modulo4(nint x) => x & 3;
6362

63+
/// <summary>
64+
/// Calculates <paramref name="x"/> % 4
65+
/// </summary>
66+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
67+
public static nuint Modulo4(nuint x) => x & 3;
68+
6469
/// <summary>
6570
/// Calculates <paramref name="x"/> % 8
6671
/// </summary>
Lines changed: 11 additions & 183 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
11
// Copyright (c) Six Labors.
22
// Licensed under the Six Labors Split License.
33

4-
using System.Buffers.Binary;
5-
using System.Numerics;
6-
using System.Runtime.CompilerServices;
7-
using System.Runtime.InteropServices;
8-
using static SixLabors.ImageSharp.SimdUtils;
9-
104
// The JIT can detect and optimize rotation idioms ROTL (Rotate Left)
115
// and ROTR (Rotate Right) emitting efficient CPU instructions:
126
// https://github.com/dotnet/coreclr/pull/1830
@@ -19,190 +13,24 @@ namespace SixLabors.ImageSharp;
1913
internal interface IComponentShuffle
2014
{
2115
/// <summary>
22-
/// Shuffles then slices 8-bit integers within 128-bit lanes in <paramref name="source"/>
23-
/// using the control and store the results in <paramref name="dest"/>.
16+
/// Shuffles then slices 8-bit integers in <paramref name="source"/>
17+
/// using a byte control and store the results in <paramref name="destination"/>.
18+
/// If successful, this method will reduce the length of <paramref name="source"/> length
19+
/// by the shuffle amount.
2420
/// </summary>
2521
/// <param name="source">The source span of bytes.</param>
26-
/// <param name="dest">The destination span of bytes.</param>
27-
void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> dest);
22+
/// <param name="destination">The destination span of bytes.</param>
23+
void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> destination);
2824

2925
/// <summary>
30-
/// Shuffle 8-bit integers within 128-bit lanes in <paramref name="source"/>
31-
/// using the control and store the results in <paramref name="dest"/>.
26+
/// Shuffle 8-bit integers in <paramref name="source"/>
27+
/// using the control and store the results in <paramref name="destination"/>.
3228
/// </summary>
3329
/// <param name="source">The source span of bytes.</param>
34-
/// <param name="dest">The destination span of bytes.</param>
30+
/// <param name="destination">The destination span of bytes.</param>
3531
/// <remarks>
36-
/// Implementation can assume that source.Length is less or equal than dest.Length.
32+
/// Implementation can assume that source.Length is less or equal than destination.Length.
3733
/// Loops should iterate using source.Length.
3834
/// </remarks>
39-
void RunFallbackShuffle(ReadOnlySpan<byte> source, Span<byte> dest);
40-
}
41-
42-
/// <inheritdoc/>
43-
internal interface IShuffle4 : IComponentShuffle
44-
{
45-
}
46-
47-
internal readonly struct DefaultShuffle4 : IShuffle4
48-
{
49-
public DefaultShuffle4(byte control)
50-
=> this.Control = control;
51-
52-
public byte Control { get; }
53-
54-
[MethodImpl(InliningOptions.ShortMethod)]
55-
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> dest)
56-
=> HwIntrinsics.Shuffle4Reduce(ref source, ref dest, this.Control);
57-
58-
[MethodImpl(InliningOptions.ShortMethod)]
59-
public void RunFallbackShuffle(ReadOnlySpan<byte> source, Span<byte> dest)
60-
{
61-
ref byte sBase = ref MemoryMarshal.GetReference(source);
62-
ref byte dBase = ref MemoryMarshal.GetReference(dest);
63-
64-
Shuffle.InverseMMShuffle(this.Control, out uint p3, out uint p2, out uint p1, out uint p0);
65-
66-
for (nuint i = 0; i < (uint)source.Length; i += 4)
67-
{
68-
Unsafe.Add(ref dBase, i + 0) = Unsafe.Add(ref sBase, p0 + i);
69-
Unsafe.Add(ref dBase, i + 1) = Unsafe.Add(ref sBase, p1 + i);
70-
Unsafe.Add(ref dBase, i + 2) = Unsafe.Add(ref sBase, p2 + i);
71-
Unsafe.Add(ref dBase, i + 3) = Unsafe.Add(ref sBase, p3 + i);
72-
}
73-
}
74-
}
75-
76-
internal readonly struct WXYZShuffle4 : IShuffle4
77-
{
78-
[MethodImpl(InliningOptions.ShortMethod)]
79-
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> dest)
80-
=> HwIntrinsics.Shuffle4Reduce(ref source, ref dest, Shuffle.MMShuffle2103);
81-
82-
[MethodImpl(InliningOptions.ShortMethod)]
83-
public void RunFallbackShuffle(ReadOnlySpan<byte> source, Span<byte> dest)
84-
{
85-
ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source));
86-
ref uint dBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(dest));
87-
uint n = (uint)source.Length / 4;
88-
89-
for (nuint i = 0; i < n; i++)
90-
{
91-
uint packed = Unsafe.Add(ref sBase, i);
92-
93-
// packed = [W Z Y X]
94-
// ROTL(8, packed) = [Z Y X W]
95-
Unsafe.Add(ref dBase, i) = (packed << 8) | (packed >> 24);
96-
}
97-
}
98-
}
99-
100-
internal readonly struct WZYXShuffle4 : IShuffle4
101-
{
102-
[MethodImpl(InliningOptions.ShortMethod)]
103-
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> dest)
104-
=> HwIntrinsics.Shuffle4Reduce(ref source, ref dest, Shuffle.MMShuffle0123);
105-
106-
[MethodImpl(InliningOptions.ShortMethod)]
107-
public void RunFallbackShuffle(ReadOnlySpan<byte> source, Span<byte> dest)
108-
{
109-
ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source));
110-
ref uint dBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(dest));
111-
uint n = (uint)source.Length / 4;
112-
113-
for (nuint i = 0; i < n; i++)
114-
{
115-
uint packed = Unsafe.Add(ref sBase, i);
116-
117-
// packed = [W Z Y X]
118-
// REVERSE(packedArgb) = [X Y Z W]
119-
Unsafe.Add(ref dBase, i) = BinaryPrimitives.ReverseEndianness(packed);
120-
}
121-
}
122-
}
123-
124-
internal readonly struct YZWXShuffle4 : IShuffle4
125-
{
126-
[MethodImpl(InliningOptions.ShortMethod)]
127-
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> dest)
128-
=> HwIntrinsics.Shuffle4Reduce(ref source, ref dest, Shuffle.MMShuffle0321);
129-
130-
[MethodImpl(InliningOptions.ShortMethod)]
131-
public void RunFallbackShuffle(ReadOnlySpan<byte> source, Span<byte> dest)
132-
{
133-
ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source));
134-
ref uint dBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(dest));
135-
uint n = (uint)source.Length / 4;
136-
137-
for (nuint i = 0; i < n; i++)
138-
{
139-
uint packed = Unsafe.Add(ref sBase, i);
140-
141-
// packed = [W Z Y X]
142-
// ROTR(8, packedArgb) = [Y Z W X]
143-
Unsafe.Add(ref dBase, i) = BitOperations.RotateRight(packed, 8);
144-
}
145-
}
146-
}
147-
148-
internal readonly struct ZYXWShuffle4 : IShuffle4
149-
{
150-
[MethodImpl(InliningOptions.ShortMethod)]
151-
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> dest)
152-
=> HwIntrinsics.Shuffle4Reduce(ref source, ref dest, Shuffle.MMShuffle3012);
153-
154-
[MethodImpl(InliningOptions.ShortMethod)]
155-
public void RunFallbackShuffle(ReadOnlySpan<byte> source, Span<byte> dest)
156-
{
157-
ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source));
158-
ref uint dBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(dest));
159-
uint n = (uint)source.Length / 4;
160-
161-
for (nuint i = 0; i < n; i++)
162-
{
163-
uint packed = Unsafe.Add(ref sBase, i);
164-
165-
// packed = [W Z Y X]
166-
// tmp1 = [W 0 Y 0]
167-
// tmp2 = [0 Z 0 X]
168-
// tmp3=ROTL(16, tmp2) = [0 X 0 Z]
169-
// tmp1 + tmp3 = [W X Y Z]
170-
uint tmp1 = packed & 0xFF00FF00;
171-
uint tmp2 = packed & 0x00FF00FF;
172-
uint tmp3 = BitOperations.RotateLeft(tmp2, 16);
173-
174-
Unsafe.Add(ref dBase, i) = tmp1 + tmp3;
175-
}
176-
}
177-
}
178-
179-
internal readonly struct XWZYShuffle4 : IShuffle4
180-
{
181-
[MethodImpl(InliningOptions.ShortMethod)]
182-
public void ShuffleReduce(ref ReadOnlySpan<byte> source, ref Span<byte> dest)
183-
=> HwIntrinsics.Shuffle4Reduce(ref source, ref dest, Shuffle.MMShuffle1230);
184-
185-
[MethodImpl(InliningOptions.ShortMethod)]
186-
public void RunFallbackShuffle(ReadOnlySpan<byte> source, Span<byte> dest)
187-
{
188-
ref uint sBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(source));
189-
ref uint dBase = ref Unsafe.As<byte, uint>(ref MemoryMarshal.GetReference(dest));
190-
uint n = (uint)source.Length / 4;
191-
192-
for (nuint i = 0; i < n; i++)
193-
{
194-
uint packed = Unsafe.Add(ref sBase, i);
195-
196-
// packed = [W Z Y X]
197-
// tmp1 = [0 Z 0 X]
198-
// tmp2 = [W 0 Y 0]
199-
// tmp3=ROTL(16, tmp2) = [Y 0 W 0]
200-
// tmp1 + tmp3 = [Y Z W X]
201-
uint tmp1 = packed & 0x00FF00FF;
202-
uint tmp2 = packed & 0xFF00FF00;
203-
uint tmp3 = BitOperations.RotateLeft(tmp2, 16);
204-
205-
Unsafe.Add(ref dBase, i) = tmp1 + tmp3;
206-
}
207-
}
35+
void Shuffle(ReadOnlySpan<byte> source, Span<byte> destination);
20836
}

0 commit comments

Comments
 (0)