Skip to content

Commit d9eb61f

Browse files
authored
Merge branch 'main' into bp/morewebptests
2 parents 73e1306 + bab277c commit d9eb61f

File tree

65 files changed

+1465
-717
lines changed

Some content is hidden

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

65 files changed

+1465
-717
lines changed

src/ImageSharp/Common/Helpers/Numerics.cs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -949,4 +949,94 @@ public static int EvenReduceSum(Vector256<int> accumulator)
949949
[MethodImpl(MethodImplOptions.AggressiveInlining)]
950950
public static bool IsOutOfRange(int value, int min, int max)
951951
=> (uint)(value - min) > (uint)(max - min);
952+
953+
/// <summary>
954+
/// Gets the count of vectors that safely fit into the given span.
955+
/// </summary>
956+
/// <typeparam name="TVector">The type of the vector.</typeparam>
957+
/// <param name="span">The given span.</param>
958+
/// <returns>Count of vectors that safely fit into the span.</returns>
959+
public static nuint VectorCount<TVector>(this Span<byte> span)
960+
where TVector : struct
961+
=> (uint)span.Length / (uint)Vector<TVector>.Count;
962+
963+
/// <summary>
964+
/// Gets the count of vectors that safely fit into the given span.
965+
/// </summary>
966+
/// <typeparam name="TVector">The type of the vector.</typeparam>
967+
/// <param name="span">The given span.</param>
968+
/// <returns>Count of vectors that safely fit into the span.</returns>
969+
public static nuint Vector128Count<TVector>(this Span<byte> span)
970+
where TVector : struct
971+
=> (uint)span.Length / (uint)Vector128<TVector>.Count;
972+
973+
/// <summary>
974+
/// Gets the count of vectors that safely fit into the given span.
975+
/// </summary>
976+
/// <typeparam name="TVector">The type of the vector.</typeparam>
977+
/// <param name="span">The given span.</param>
978+
/// <returns>Count of vectors that safely fit into the span.</returns>
979+
public static nuint Vector128Count<TVector>(this ReadOnlySpan<byte> span)
980+
where TVector : struct
981+
=> (uint)span.Length / (uint)Vector128<TVector>.Count;
982+
983+
/// <summary>
984+
/// Gets the count of vectors that safely fit into the given span.
985+
/// </summary>
986+
/// <typeparam name="TVector">The type of the vector.</typeparam>
987+
/// <param name="span">The given span.</param>
988+
/// <returns>Count of vectors that safely fit into the span.</returns>
989+
public static nuint Vector256Count<TVector>(this Span<byte> span)
990+
where TVector : struct
991+
=> (uint)span.Length / (uint)Vector256<TVector>.Count;
992+
993+
/// <summary>
994+
/// Gets the count of vectors that safely fit into the given span.
995+
/// </summary>
996+
/// <typeparam name="TVector">The type of the vector.</typeparam>
997+
/// <param name="span">The given span.</param>
998+
/// <returns>Count of vectors that safely fit into the span.</returns>
999+
public static nuint Vector256Count<TVector>(this ReadOnlySpan<byte> span)
1000+
where TVector : struct
1001+
=> (uint)span.Length / (uint)Vector256<TVector>.Count;
1002+
1003+
/// <summary>
1004+
/// Gets the count of vectors that safely fit into the given span.
1005+
/// </summary>
1006+
/// <typeparam name="TVector">The type of the vector.</typeparam>
1007+
/// <param name="span">The given span.</param>
1008+
/// <returns>Count of vectors that safely fit into the span.</returns>
1009+
public static nuint VectorCount<TVector>(this Span<float> span)
1010+
where TVector : struct
1011+
=> (uint)span.Length / (uint)Vector<TVector>.Count;
1012+
1013+
/// <summary>
1014+
/// Gets the count of vectors that safely fit into the given span.
1015+
/// </summary>
1016+
/// <typeparam name="TVector">The type of the vector.</typeparam>
1017+
/// <param name="span">The given span.</param>
1018+
/// <returns>Count of vectors that safely fit into the span.</returns>
1019+
public static nuint Vector128Count<TVector>(this Span<float> span)
1020+
where TVector : struct
1021+
=> (uint)span.Length / (uint)Vector128<TVector>.Count;
1022+
1023+
/// <summary>
1024+
/// Gets the count of vectors that safely fit into the given span.
1025+
/// </summary>
1026+
/// <typeparam name="TVector">The type of the vector.</typeparam>
1027+
/// <param name="span">The given span.</param>
1028+
/// <returns>Count of vectors that safely fit into the span.</returns>
1029+
public static nuint Vector256Count<TVector>(this Span<float> span)
1030+
where TVector : struct
1031+
=> (uint)span.Length / (uint)Vector256<TVector>.Count;
1032+
1033+
/// <summary>
1034+
/// Gets the count of vectors that safely fit into length.
1035+
/// </summary>
1036+
/// <typeparam name="TVector">The type of the vector.</typeparam>
1037+
/// <param name="length">The given length.</param>
1038+
/// <returns>Count of vectors that safely fit into the length.</returns>
1039+
public static nuint Vector256Count<TVector>(int length)
1040+
where TVector : struct
1041+
=> (uint)length / (uint)Vector256<TVector>.Count;
9521042
}

src/ImageSharp/Common/Helpers/SimdUtils.ExtendedIntrinsics.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ internal static void ByteToNormalizedFloat(ReadOnlySpan<byte> source, Span<float
9797
{
9898
VerifySpanInput(source, dest, Vector<byte>.Count);
9999

100-
nuint n = (uint)dest.Length / (uint)Vector<byte>.Count;
100+
nuint n = dest.VectorCount<byte>();
101101

102102
ref Vector<byte> sourceBase = ref Unsafe.As<byte, Vector<byte>>(ref MemoryMarshal.GetReference(source));
103103
ref Vector<float> destBase = ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(dest));
@@ -132,7 +132,7 @@ internal static void NormalizedFloatToByteSaturate(
132132
{
133133
VerifySpanInput(source, dest, Vector<byte>.Count);
134134

135-
nuint n = (uint)dest.Length / (uint)Vector<byte>.Count;
135+
nuint n = dest.VectorCount<byte>();
136136

137137
ref Vector<float> sourceBase =
138138
ref Unsafe.As<float, Vector<float>>(ref MemoryMarshal.GetReference(source));

src/ImageSharp/Common/Helpers/SimdUtils.HwIntrinsics.cs

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Runtime.CompilerServices;
55
using System.Runtime.InteropServices;
66
using System.Runtime.Intrinsics;
7+
using System.Runtime.Intrinsics.Arm;
78
using System.Runtime.Intrinsics.X86;
89
using SixLabors.ImageSharp.PixelFormats;
910

@@ -221,7 +222,7 @@ private static void Shuffle4(
221222
ref Vector256<float> destBase =
222223
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(dest));
223224

224-
nint n = (nint)(uint)(dest.Length / Vector256<float>.Count);
225+
nint n = (nint)dest.Vector256Count<float>();
225226
nint m = Numerics.Modulo4(n);
226227
nint u = n - m;
227228

@@ -391,7 +392,7 @@ private static void Shuffle3(
391392
ref Vector128<byte> destBase =
392393
ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(dest));
393394

394-
nuint n = (uint)source.Length / (uint)Vector128<byte>.Count;
395+
nuint n = source.Vector128Count<byte>();
395396

396397
for (nuint i = 0; i < n; i += 3)
397398
{
@@ -454,7 +455,7 @@ private static void Pad3Shuffle4(
454455
ref Vector128<byte> destBase =
455456
ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(dest));
456457

457-
nuint n = (uint)source.Length / (uint)Vector128<byte>.Count;
458+
nuint n = source.Vector128Count<byte>();
458459

459460
for (nuint i = 0, j = 0; i < n; i += 3, j += 4)
460461
{
@@ -498,7 +499,7 @@ private static void Shuffle4Slice3(
498499
ref Vector128<byte> destBase =
499500
ref Unsafe.As<byte, Vector128<byte>>(ref MemoryMarshal.GetReference(dest));
500501

501-
nuint n = (uint)source.Length / (uint)Vector128<byte>.Count;
502+
nuint n = source.Vector128Count<byte>();
502503

503504
for (nuint i = 0, j = 0; i < n; i += 4, j += 3)
504505
{
@@ -554,6 +555,34 @@ public static Vector256<float> MultiplyAdd(
554555
return Avx.Add(Avx.Multiply(vm0, vm1), va);
555556
}
556557

558+
/// <summary>
559+
/// Performs a multiplication and an addition of the <see cref="Vector128{Single}"/>.
560+
/// TODO: Fix. The arguments are in a different order to the FMA intrinsic.
561+
/// </summary>
562+
/// <remarks>ret = (vm0 * vm1) + va</remarks>
563+
/// <param name="va">The vector to add to the intermediate result.</param>
564+
/// <param name="vm0">The first vector to multiply.</param>
565+
/// <param name="vm1">The second vector to multiply.</param>
566+
/// <returns>The <see cref="Vector256{T}"/>.</returns>
567+
[MethodImpl(InliningOptions.AlwaysInline)]
568+
public static Vector128<float> MultiplyAdd(
569+
Vector128<float> va,
570+
Vector128<float> vm0,
571+
Vector128<float> vm1)
572+
{
573+
if (Fma.IsSupported)
574+
{
575+
return Fma.MultiplyAdd(vm1, vm0, va);
576+
}
577+
578+
if (AdvSimd.IsSupported)
579+
{
580+
return AdvSimd.Add(AdvSimd.Multiply(vm0, vm1), va);
581+
}
582+
583+
return Sse.Add(Sse.Multiply(vm0, vm1), va);
584+
}
585+
557586
/// <summary>
558587
/// Performs a multiplication and a subtraction of the <see cref="Vector256{Single}"/>.
559588
/// TODO: Fix. The arguments are in a different order to the FMA intrinsic.
@@ -650,7 +679,7 @@ internal static unsafe void ByteToNormalizedFloat(
650679
{
651680
VerifySpanInput(source, dest, Vector256<byte>.Count);
652681

653-
nuint n = (uint)dest.Length / (uint)Vector256<byte>.Count;
682+
nuint n = dest.Vector256Count<byte>();
654683

655684
ref Vector256<float> destBase =
656685
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(dest));
@@ -683,7 +712,7 @@ internal static unsafe void ByteToNormalizedFloat(
683712
// Sse
684713
VerifySpanInput(source, dest, Vector128<byte>.Count);
685714

686-
nuint n = (uint)dest.Length / (uint)Vector128<byte>.Count;
715+
nuint n = dest.Vector128Count<byte>();
687716

688717
ref Vector128<float> destBase =
689718
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(dest));
@@ -782,7 +811,7 @@ internal static void NormalizedFloatToByteSaturate(
782811
{
783812
VerifySpanInput(source, dest, Vector256<byte>.Count);
784813

785-
nuint n = (uint)dest.Length / (uint)Vector256<byte>.Count;
814+
nuint n = dest.Vector256Count<byte>();
786815

787816
ref Vector256<float> sourceBase =
788817
ref Unsafe.As<float, Vector256<float>>(ref MemoryMarshal.GetReference(source));
@@ -821,7 +850,7 @@ internal static void NormalizedFloatToByteSaturate(
821850
// Sse
822851
VerifySpanInput(source, dest, Vector128<byte>.Count);
823852

824-
nuint n = (uint)dest.Length / (uint)Vector128<byte>.Count;
853+
nuint n = dest.Vector128Count<byte>();
825854

826855
ref Vector128<float> sourceBase =
827856
ref Unsafe.As<float, Vector128<float>>(ref MemoryMarshal.GetReference(source));
@@ -864,7 +893,7 @@ internal static void PackFromRgbPlanesAvx2Reduce(
864893
ref Vector256<byte> bBase = ref Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(blueChannel));
865894
ref byte dBase = ref Unsafe.As<Rgb24, byte>(ref MemoryMarshal.GetReference(destination));
866895

867-
nuint count = (uint)redChannel.Length / (uint)Vector256<byte>.Count;
896+
nuint count = redChannel.Vector256Count<byte>();
868897

869898
ref byte control1Bytes = ref MemoryMarshal.GetReference(PermuteMaskEvenOdd8x32);
870899
Vector256<uint> control1 = Unsafe.As<byte, Vector256<uint>>(ref control1Bytes);
@@ -936,7 +965,7 @@ internal static void PackFromRgbPlanesAvx2Reduce(
936965
ref Vector256<byte> bBase = ref Unsafe.As<byte, Vector256<byte>>(ref MemoryMarshal.GetReference(blueChannel));
937966
ref Vector256<byte> dBase = ref Unsafe.As<Rgba32, Vector256<byte>>(ref MemoryMarshal.GetReference(destination));
938967

939-
nuint count = (uint)redChannel.Length / (uint)Vector256<byte>.Count;
968+
nuint count = redChannel.Vector256Count<byte>();
940969
ref byte control1Bytes = ref MemoryMarshal.GetReference(PermuteMaskEvenOdd8x32);
941970
Vector256<uint> control1 = Unsafe.As<byte, Vector256<uint>>(ref control1Bytes);
942971
var a = Vector256.Create((byte)255);

src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,7 @@ private void ReadRle24<TPixel>(BufferedReadStream stream, Buffer2D<TPixel> pixel
453453
/// <param name="rowsWithUndefinedPixels">Keeps track of rows, which have undefined pixels.</param>
454454
private void UncompressRle4(BufferedReadStream stream, int w, Span<byte> buffer, Span<bool> undefinedPixels, Span<bool> rowsWithUndefinedPixels)
455455
{
456+
Span<byte> scratchBuffer = stackalloc byte[128];
456457
Span<byte> cmd = stackalloc byte[2];
457458
int count = 0;
458459

@@ -491,9 +492,9 @@ private void UncompressRle4(BufferedReadStream stream, int w, Span<byte> buffer,
491492
int max = cmd[1];
492493
int bytesToRead = (int)(((uint)max + 1) / 2);
493494

494-
byte[] run = new byte[bytesToRead];
495+
Span<byte> run = bytesToRead <= 128 ? scratchBuffer.Slice(0, bytesToRead) : new byte[bytesToRead];
495496

496-
stream.Read(run, 0, run.Length);
497+
stream.Read(run);
497498

498499
int idx = 0;
499500
for (int i = 0; i < max; i++)
@@ -559,6 +560,7 @@ private void UncompressRle4(BufferedReadStream stream, int w, Span<byte> buffer,
559560
/// <param name="rowsWithUndefinedPixels">Keeps track of rows, which have undefined pixels.</param>
560561
private void UncompressRle8(BufferedReadStream stream, int w, Span<byte> buffer, Span<bool> undefinedPixels, Span<bool> rowsWithUndefinedPixels)
561562
{
563+
Span<byte> scratchBuffer = stackalloc byte[128];
562564
Span<byte> cmd = stackalloc byte[2];
563565
int count = 0;
564566

@@ -596,13 +598,13 @@ private void UncompressRle8(BufferedReadStream stream, int w, Span<byte> buffer,
596598
// Take this number of bytes from the stream as uncompressed data.
597599
int length = cmd[1];
598600

599-
byte[] run = new byte[length];
601+
Span<byte> run = length <= 128 ? scratchBuffer.Slice(0, length) : new byte[length];
600602

601-
stream.Read(run, 0, run.Length);
603+
stream.Read(run);
602604

603-
run.AsSpan().CopyTo(buffer[count..]);
605+
run.CopyTo(buffer[count..]);
604606

605-
count += run.Length;
607+
count += length;
606608

607609
// Absolute mode data is aligned to two-byte word-boundary.
608610
int padding = length & 1;
@@ -639,6 +641,7 @@ private void UncompressRle8(BufferedReadStream stream, int w, Span<byte> buffer,
639641
/// <param name="rowsWithUndefinedPixels">Keeps track of rows, which have undefined pixels.</param>
640642
private void UncompressRle24(BufferedReadStream stream, int w, Span<byte> buffer, Span<bool> undefinedPixels, Span<bool> rowsWithUndefinedPixels)
641643
{
644+
Span<byte> scratchBuffer = stackalloc byte[128];
642645
Span<byte> cmd = stackalloc byte[2];
643646
int uncompressedPixels = 0;
644647

@@ -675,17 +678,18 @@ private void UncompressRle24(BufferedReadStream stream, int w, Span<byte> buffer
675678
// If the second byte > 2, we are in 'absolute mode'.
676679
// Take this number of bytes from the stream as uncompressed data.
677680
int length = cmd[1];
681+
int length3 = length * 3;
678682

679-
byte[] run = new byte[length * 3];
683+
Span<byte> run = length3 <= 128 ? scratchBuffer.Slice(0, length3) : new byte[length3];
680684

681-
stream.Read(run, 0, run.Length);
685+
stream.Read(run);
682686

683-
run.AsSpan().CopyTo(buffer[(uncompressedPixels * 3)..]);
687+
run.CopyTo(buffer[(uncompressedPixels * 3)..]);
684688

685689
uncompressedPixels += length;
686690

687691
// Absolute mode data is aligned to two-byte word-boundary.
688-
int padding = run.Length & 1;
692+
int padding = length3 & 1;
689693

690694
stream.Skip(padding);
691695

@@ -1286,18 +1290,18 @@ private void ReadInfoHeader(BufferedReadStream stream)
12861290
// color masks for each color channel follow the info header.
12871291
if (this.infoHeader.Compression == BmpCompression.BitFields)
12881292
{
1289-
byte[] bitfieldsBuffer = new byte[12];
1290-
stream.Read(bitfieldsBuffer, 0, 12);
1291-
Span<byte> data = bitfieldsBuffer.AsSpan();
1293+
Span<byte> bitfieldsBuffer = stackalloc byte[12];
1294+
stream.Read(bitfieldsBuffer);
1295+
Span<byte> data = bitfieldsBuffer;
12921296
this.infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(data[..4]);
12931297
this.infoHeader.GreenMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4));
12941298
this.infoHeader.BlueMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4));
12951299
}
12961300
else if (this.infoHeader.Compression == BmpCompression.BI_ALPHABITFIELDS)
12971301
{
1298-
byte[] bitfieldsBuffer = new byte[16];
1299-
stream.Read(bitfieldsBuffer, 0, 16);
1300-
Span<byte> data = bitfieldsBuffer.AsSpan();
1302+
Span<byte> bitfieldsBuffer = stackalloc byte[16];
1303+
stream.Read(bitfieldsBuffer);
1304+
Span<byte> data = bitfieldsBuffer;
13011305
this.infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(data[..4]);
13021306
this.infoHeader.GreenMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4));
13031307
this.infoHeader.BlueMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4));
@@ -1470,7 +1474,7 @@ private int ReadImageHeaders(BufferedReadStream stream, out bool inverted, out b
14701474
{
14711475
// Usually the color palette is 1024 byte (256 colors * 4), but the documentation does not mention a size limit.
14721476
// Make sure, that we will not read pass the bitmap offset (starting position of image data).
1473-
if ((stream.Position + colorMapSizeBytes) > this.fileHeader.Offset)
1477+
if (stream.Position > this.fileHeader.Offset - colorMapSizeBytes)
14741478
{
14751479
BmpThrowHelper.ThrowInvalidImageContentException(
14761480
$"Reading the color map would read beyond the bitmap offset. Either the color map size of '{colorMapSizeBytes}' is invalid or the bitmap offset.");

0 commit comments

Comments
 (0)