Skip to content

Commit 9cca58a

Browse files
committed
NumberUtilities + NumericExtensions = NumericUtilities, some SIMD
1 parent f873ce7 commit 9cca58a

8 files changed

Lines changed: 96 additions & 79 deletions

File tree

src/ImageSharp.Drawing/Processing/PatternBrush.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
using System;
55
using System.Buffers;
66
using System.Numerics;
7-
using SixLabors.ImageSharp.Advanced;
7+
using SixLabors.ImageSharp.Drawing.Utilities;
88
using SixLabors.ImageSharp.Memory;
99
using SixLabors.ImageSharp.PixelFormats;
1010

@@ -155,7 +155,7 @@ public override void Apply(Span<float> scanline, int x, int y)
155155

156156
for (int i = 0; i < scanline.Length; i++)
157157
{
158-
amountSpan[i] = NumberUtilities.ClampFloat(scanline[i] * this.Options.BlendPercentage, 0, 1F);
158+
amountSpan[i] = NumericUtilities.ClampFloat(scanline[i] * this.Options.BlendPercentage, 0, 1F);
159159

160160
int patternX = (x + i) % this.pattern.Columns;
161161
overlaySpan[i] = this.pattern[patternY, patternX];

src/ImageSharp.Drawing/Shapes/Rasterization/RasterizerExtensions.cs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the Apache License, Version 2.0.
33

44
using System;
5+
using SixLabors.ImageSharp.Drawing.Utilities;
56

67
namespace SixLabors.ImageSharp.Drawing.Shapes.Rasterization
78
{
@@ -59,11 +60,8 @@ private static void ScanCurrentSubpixelLineInto(this ref PolygonScanner scanner,
5960
int nextX = startX + 1;
6061
endX = Math.Min(endX, scanline.Length); // reduce to end to the right edge
6162
nextX = Math.Max(nextX, 0);
62-
for (int x = nextX; x < endX; x++)
63-
{
64-
scanline[x] += scanner.SubpixelDistance;
65-
scanlineDirty = true;
66-
}
63+
64+
scanline.Slice(nextX, endX - nextX).AddToAllElements(scanner.SubpixelDistance);
6765
}
6866
}
6967
}

src/ImageSharp.Drawing/Utilities/NumberUtilities.cs

Lines changed: 0 additions & 29 deletions
This file was deleted.

src/ImageSharp.Drawing/Common/Extensions/NumericExtensions.cs renamed to src/ImageSharp.Drawing/Utilities/NumericUtilities.cs

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,63 @@
11
// Copyright (c) Six Labors.
22
// Licensed under the Apache License, Version 2.0.
33

4+
using System;
5+
using System.Numerics;
46
using System.Runtime.CompilerServices;
7+
using System.Runtime.InteropServices;
58

6-
namespace SixLabors.ImageSharp.Drawing
9+
namespace SixLabors.ImageSharp.Drawing.Utilities
710
{
8-
internal static class NumericExtensions
11+
internal static class NumericUtilities
912
{
13+
public static void AddToAllElements(this Span<float> span, float value)
14+
{
15+
ref float current = ref MemoryMarshal.GetReference(span);
16+
ref float max = ref Unsafe.Add(ref current, span.Length);
17+
18+
if (Vector.IsHardwareAccelerated)
19+
{
20+
int n = span.Length / Vector<float>.Count;
21+
ref Vector<float> currentVec = ref Unsafe.As<float, Vector<float>>(ref current);
22+
ref Vector<float> maxVec = ref Unsafe.Add(ref currentVec, n);
23+
24+
Vector<float> vecVal = new Vector<float>(value);
25+
while (Unsafe.IsAddressLessThan(ref currentVec, ref maxVec))
26+
{
27+
currentVec += vecVal;
28+
currentVec = ref Unsafe.Add(ref currentVec, 1);
29+
}
30+
31+
// current = ref Unsafe.Add(ref current, n * Vector<float>.Count);
32+
current = ref Unsafe.As<Vector<float>, float>(ref currentVec);
33+
}
34+
35+
while (Unsafe.IsAddressLessThan(ref current, ref max))
36+
{
37+
current += value;
38+
current = ref Unsafe.Add(ref current, 1);
39+
}
40+
}
41+
1042
// https://apisof.net/catalog/System.Numerics.BitOperations.Log2(UInt32)
1143
// BitOperations.Log2() has been introduced in .NET Core 3.0,
1244
// since we do target only 3.1+, we can detect it's presence by using SUPPORTS_RUNTIME_INTRINSICS
1345
// TODO: Ideally this should have a separate definition in Build.props, but that adaption should be done cross-repo. Using a workaround until then.
1446
#if SUPPORTS_RUNTIME_INTRINSICS
1547
[MethodImpl(MethodImplOptions.AggressiveInlining)]
1648
public static int Log2(uint value) => System.Numerics.BitOperations.Log2(value);
49+
1750
#else
18-
private static System.ReadOnlySpan<byte> Log2DeBruijn => new byte[32]
51+
52+
#pragma warning disable SA1515, SA1414, SA1114, SA1201
53+
private static ReadOnlySpan<byte> Log2DeBruijn => new byte[32]
1954
{
2055
00, 09, 01, 10, 13, 21, 02, 29,
2156
11, 14, 16, 18, 22, 25, 03, 30,
2257
08, 12, 20, 28, 15, 17, 24, 07,
2358
19, 27, 23, 06, 26, 05, 04, 31
2459
};
2560

26-
#pragma warning disable SA1515, SA1414, SA1114
2761
// Adapted from:
2862
// https://github.com/dotnet/runtime/blob/5c65d891f203618245184fa54397ced0a8ca806c/src/libraries/System.Private.CoreLib/src/System/Numerics/BitOperations.cs#L205-L223
2963
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -42,11 +76,27 @@ public static int Log2(uint value)
4276
// uint.MaxValue >> 27 is always in range [0 - 31] so we use Unsafe.AddByteOffset to avoid bounds check
4377
return Unsafe.AddByteOffset(
4478
// Using deBruijn sequence, k=2, n=5 (2^5=32) : 0b_0000_0111_1100_0100_1010_1100_1101_1101u
45-
ref System.Runtime.InteropServices.MemoryMarshal.GetReference(Log2DeBruijn),
79+
ref MemoryMarshal.GetReference(Log2DeBruijn),
4680
// uint|long -> IntPtr cast on 32-bit platforms does expensive overflow checks not needed here
47-
(System.IntPtr)(int)((value * 0x07C4ACDDu) >> 27));
81+
(IntPtr)(int)((value * 0x07C4ACDDu) >> 27));
4882
}
83+
4984
#pragma warning restore
5085
#endif
86+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
87+
public static float ClampFloat(float value, float min, float max)
88+
{
89+
if (value >= max)
90+
{
91+
return max;
92+
}
93+
94+
if (value <= min)
95+
{
96+
return min;
97+
}
98+
99+
return value;
100+
}
51101
}
52102
}

src/ImageSharp.Drawing/Utilities/SortUtility.KeyValueSort.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ private static void IntrospectiveSort(Span<float> keys, Span<TValue> values)
4949
{
5050
if (keys.Length > 1)
5151
{
52-
IntroSort(keys, values, 2 * (NumericExtensions.Log2((uint)keys.Length) + 1));
52+
IntroSort(keys, values, 2 * (NumericUtilities.Log2((uint)keys.Length) + 1));
5353
}
5454
}
5555

tests/ImageSharp.Drawing.Tests/ImageInfoTests.cs

Lines changed: 0 additions & 33 deletions
This file was deleted.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Copyright (c) Six Labors.
2+
// Licensed under the Apache License, Version 2.0.
3+
4+
using System;
5+
using System.Linq;
6+
using SixLabors.ImageSharp.Drawing.Utilities;
7+
using Xunit;
8+
9+
namespace SixLabors.ImageSharp.Drawing.Tests.Drawing.Utils
10+
{
11+
public class NumericUtilitiesTests
12+
{
13+
[Theory]
14+
[InlineData(0)]
15+
[InlineData(1)]
16+
[InlineData(3)]
17+
[InlineData(7)]
18+
[InlineData(8)]
19+
[InlineData(13)]
20+
[InlineData(130)]
21+
public void AddToAllElements(int length)
22+
{
23+
float[] values = Enumerable.Range(0, length).Select(v => (float)v).ToArray();
24+
25+
const float val = 13.4321f;
26+
float[] expected = values.Select(x => x + val).ToArray();
27+
values.AsSpan().AddToAllElements(val);
28+
29+
Assert.Equal(expected, values);
30+
}
31+
}
32+
}

tests/ImageSharp.Drawing.Tests/Drawing/Utils/SortUtilityTests.cs renamed to tests/ImageSharp.Drawing.Tests/Utilities/SortUtilityTests.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,10 @@
33

44
using System;
55
using System.Linq;
6-
7-
using Xunit;
86
using SixLabors.ImageSharp.Drawing.Utilities;
7+
using Xunit;
98

10-
namespace SixLabors.ImageSharp.Drawing.Tests.Drawing.Utils
9+
namespace SixLabors.ImageSharp.Drawing.Tests.Utilities
1110
{
1211
public class SortUtilityTests
1312
{

0 commit comments

Comments
 (0)