Skip to content

Commit b9b6f72

Browse files
committed
Reduced intermediate allocations: Bmp
1 parent 3c3479e commit b9b6f72

File tree

1 file changed

+21
-17
lines changed

1 file changed

+21
-17
lines changed

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)