Browse Source

Reduced intermediate allocations: Tga

pull/2415/head
Günther Foidl 3 years ago
parent
commit
3c3479ee12
  1. 43
      src/ImageSharp/Formats/Tga/TgaDecoderCore.cs
  2. 12
      src/ImageSharp/Formats/Tga/TgaEncoderCore.cs

43
src/ImageSharp/Formats/Tga/TgaDecoderCore.cs

@ -17,11 +17,6 @@ namespace SixLabors.ImageSharp.Formats.Tga;
/// </summary> /// </summary>
internal sealed class TgaDecoderCore : IImageDecoderInternals internal sealed class TgaDecoderCore : IImageDecoderInternals
{ {
/// <summary>
/// A scratch buffer to reduce allocations.
/// </summary>
private readonly byte[] scratchBuffer = new byte[4];
/// <summary> /// <summary>
/// General configuration options. /// General configuration options.
/// </summary> /// </summary>
@ -407,6 +402,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
bool invertX = InvertX(origin); bool invertX = InvertX(origin);
using IMemoryOwner<byte> row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 2, 0); using IMemoryOwner<byte> row = this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, 2, 0);
Span<byte> rowSpan = row.GetSpan(); Span<byte> rowSpan = row.GetSpan();
Span<byte> scratchBuffer = stackalloc byte[2];
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
@ -417,7 +413,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
{ {
for (int x = width - 1; x >= 0; x--) for (int x = width - 1; x >= 0; x--)
{ {
int bytesRead = stream.Read(this.scratchBuffer, 0, 2); int bytesRead = stream.Read(scratchBuffer);
if (bytesRead != 2) if (bytesRead != 2)
{ {
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel row"); TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel row");
@ -425,16 +421,16 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
if (!this.hasAlpha) if (!this.hasAlpha)
{ {
this.scratchBuffer[1] |= 1 << 7; scratchBuffer[1] |= 1 << 7;
} }
if (this.fileHeader.ImageType == TgaImageType.BlackAndWhite) if (this.fileHeader.ImageType == TgaImageType.BlackAndWhite)
{ {
color.FromLa16(Unsafe.As<byte, La16>(ref MemoryMarshal.GetArrayDataReference(this.scratchBuffer))); color.FromLa16(Unsafe.As<byte, La16>(ref MemoryMarshal.GetReference(scratchBuffer)));
} }
else else
{ {
color.FromBgra5551(Unsafe.As<byte, Bgra5551>(ref MemoryMarshal.GetArrayDataReference(this.scratchBuffer))); color.FromBgra5551(Unsafe.As<byte, Bgra5551>(ref MemoryMarshal.GetReference(scratchBuffer)));
} }
pixelSpan[x] = color; pixelSpan[x] = color;
@ -484,6 +480,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
bool invertX = InvertX(origin); bool invertX = InvertX(origin);
if (invertX) if (invertX)
{ {
Span<byte> scratchBuffer = stackalloc byte[4];
TPixel color = default; TPixel color = default;
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
@ -491,7 +488,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(newY); Span<TPixel> pixelSpan = pixels.DangerousGetRowSpan(newY);
for (int x = width - 1; x >= 0; x--) for (int x = width - 1; x >= 0; x--)
{ {
this.ReadBgr24Pixel(stream, color, x, pixelSpan); ReadBgr24Pixel(stream, color, x, pixelSpan, scratchBuffer);
} }
} }
@ -558,6 +555,8 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
return; return;
} }
Span<byte> scratchBuffer = stackalloc byte[4];
for (int y = 0; y < height; y++) for (int y = 0; y < height; y++)
{ {
int newY = InvertY(y, height, origin); int newY = InvertY(y, height, origin);
@ -566,14 +565,14 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
{ {
for (int x = width - 1; x >= 0; x--) for (int x = width - 1; x >= 0; x--)
{ {
this.ReadBgra32Pixel(stream, x, color, pixelRow); this.ReadBgra32Pixel(stream, x, color, pixelRow, scratchBuffer);
} }
} }
else else
{ {
for (int x = 0; x < width; x++) for (int x = 0; x < width; x++)
{ {
this.ReadBgra32Pixel(stream, x, color, pixelRow); this.ReadBgra32Pixel(stream, x, color, pixelRow, scratchBuffer);
} }
} }
} }
@ -687,16 +686,16 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ReadBgr24Pixel<TPixel>(BufferedReadStream stream, TPixel color, int x, Span<TPixel> pixelSpan) private static void ReadBgr24Pixel<TPixel>(BufferedReadStream stream, TPixel color, int x, Span<TPixel> pixelSpan, Span<byte> scratchBuffer)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
int bytesRead = stream.Read(this.scratchBuffer, 0, 3); int bytesRead = stream.Read(scratchBuffer, 0, 3);
if (bytesRead != 3) if (bytesRead != 3)
{ {
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a bgr pixel"); TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a bgr pixel");
} }
color.FromBgr24(Unsafe.As<byte, Bgr24>(ref MemoryMarshal.GetArrayDataReference(this.scratchBuffer))); color.FromBgr24(Unsafe.As<byte, Bgr24>(ref MemoryMarshal.GetReference(scratchBuffer)));
pixelSpan[x] = color; pixelSpan[x] = color;
} }
@ -715,10 +714,10 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
private void ReadBgra32Pixel<TPixel>(BufferedReadStream stream, int x, TPixel color, Span<TPixel> pixelRow) private void ReadBgra32Pixel<TPixel>(BufferedReadStream stream, int x, TPixel color, Span<TPixel> pixelRow, Span<byte> scratchBuffer)
where TPixel : unmanaged, IPixel<TPixel> where TPixel : unmanaged, IPixel<TPixel>
{ {
int bytesRead = stream.Read(this.scratchBuffer, 0, 4); int bytesRead = stream.Read(scratchBuffer, 0, 4);
if (bytesRead != 4) if (bytesRead != 4)
{ {
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a bgra pixel"); TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a bgra pixel");
@ -726,8 +725,8 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
Guard.NotNull(this.tgaMetadata); Guard.NotNull(this.tgaMetadata);
byte alpha = this.tgaMetadata.AlphaChannelBits == 0 ? byte.MaxValue : this.scratchBuffer[3]; byte alpha = this.tgaMetadata.AlphaChannelBits == 0 ? byte.MaxValue : scratchBuffer[3];
color.FromBgra32(new Bgra32(this.scratchBuffer[2], this.scratchBuffer[1], this.scratchBuffer[0], alpha)); color.FromBgra32(new Bgra32(scratchBuffer[2], scratchBuffer[1], scratchBuffer[0], alpha));
pixelRow[x] = color; pixelRow[x] = color;
} }
@ -814,7 +813,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
private void UncompressRle(BufferedReadStream stream, int width, int height, Span<byte> buffer, int bytesPerPixel) private void UncompressRle(BufferedReadStream stream, int width, int height, Span<byte> buffer, int bytesPerPixel)
{ {
int uncompressedPixels = 0; int uncompressedPixels = 0;
Span<byte> pixel = this.scratchBuffer.AsSpan(0, bytesPerPixel); Span<byte> pixel = stackalloc byte[bytesPerPixel];
int totalPixels = width * height; int totalPixels = width * height;
while (uncompressedPixels < totalPixels) while (uncompressedPixels < totalPixels)
{ {
@ -825,7 +824,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
if (highBit == 1) if (highBit == 1)
{ {
int runLength = runLengthByte & 127; int runLength = runLengthByte & 127;
int bytesRead = stream.Read(pixel, 0, bytesPerPixel); int bytesRead = stream.Read(pixel);
if (bytesRead != bytesPerPixel) if (bytesRead != bytesPerPixel)
{ {
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel from the stream"); TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel from the stream");
@ -845,7 +844,7 @@ internal sealed class TgaDecoderCore : IImageDecoderInternals
int bufferIdx = uncompressedPixels * bytesPerPixel; int bufferIdx = uncompressedPixels * bytesPerPixel;
for (int i = 0; i < runLength + 1; i++, uncompressedPixels++) for (int i = 0; i < runLength + 1; i++, uncompressedPixels++)
{ {
int bytesRead = stream.Read(pixel, 0, bytesPerPixel); int bytesRead = stream.Read(pixel);
if (bytesRead != bytesPerPixel) if (bytesRead != bytesPerPixel)
{ {
TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel from the stream"); TgaThrowHelper.ThrowInvalidImageContentException("Not enough data to read a pixel from the stream");

12
src/ImageSharp/Formats/Tga/TgaEncoderCore.cs

@ -22,11 +22,6 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
/// </summary> /// </summary>
private readonly MemoryAllocator memoryAllocator; private readonly MemoryAllocator memoryAllocator;
/// <summary>
/// Reusable buffer for writing data.
/// </summary>
private readonly byte[] buffer = new byte[2];
/// <summary> /// <summary>
/// The color depth, in number of bits per pixel. /// The color depth, in number of bits per pixel.
/// </summary> /// </summary>
@ -221,9 +216,10 @@ internal sealed class TgaEncoderCore : IImageEncoderInternals
case TgaBitsPerPixel.Pixel16: case TgaBitsPerPixel.Pixel16:
Bgra5551 bgra5551 = new(color.ToVector4()); Bgra5551 bgra5551 = new(color.ToVector4());
BinaryPrimitives.WriteInt16LittleEndian(this.buffer, (short)bgra5551.PackedValue); Span<byte> buffer = stackalloc byte[2];
stream.WriteByte(this.buffer[0]); BinaryPrimitives.WriteInt16LittleEndian(buffer, (short)bgra5551.PackedValue);
stream.WriteByte(this.buffer[1]); stream.WriteByte(buffer[0]);
stream.WriteByte(buffer[1]);
break; break;

Loading…
Cancel
Save