diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
index 6ff2723ddd..efde4e9aa8 100644
--- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
@@ -22,7 +22,7 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
///
/// The temp buffer used to reduce allocations.
///
- private readonly byte[] buffer = new byte[16];
+ private ScratchBuffer buffer; // mutable struct, don't make readonly
///
/// The global color table.
@@ -249,13 +249,13 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// The containing image data.
private void ReadGraphicalControlExtension(BufferedReadStream stream)
{
- int bytesRead = stream.Read(this.buffer, 0, 6);
+ int bytesRead = stream.Read(this.buffer.Span, 0, 6);
if (bytesRead != 6)
{
GifThrowHelper.ThrowInvalidImageContentException("Not enough data to read the graphic control extension");
}
- this.graphicsControlExtension = GifGraphicControlExtension.Parse(this.buffer);
+ this.graphicsControlExtension = GifGraphicControlExtension.Parse(this.buffer.Span);
}
///
@@ -264,13 +264,13 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// The containing image data.
private void ReadImageDescriptor(BufferedReadStream stream)
{
- int bytesRead = stream.Read(this.buffer, 0, 9);
+ int bytesRead = stream.Read(this.buffer.Span, 0, 9);
if (bytesRead != 9)
{
GifThrowHelper.ThrowInvalidImageContentException("Not enough data to read the image descriptor");
}
- this.imageDescriptor = GifImageDescriptor.Parse(this.buffer);
+ this.imageDescriptor = GifImageDescriptor.Parse(this.buffer.Span);
if (this.imageDescriptor.Height == 0 || this.imageDescriptor.Width == 0)
{
GifThrowHelper.ThrowInvalidImageContentException("Width or height should not be 0");
@@ -283,13 +283,13 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
/// The containing image data.
private void ReadLogicalScreenDescriptor(BufferedReadStream stream)
{
- int bytesRead = stream.Read(this.buffer, 0, 7);
+ int bytesRead = stream.Read(this.buffer.Span, 0, 7);
if (bytesRead != 7)
{
GifThrowHelper.ThrowInvalidImageContentException("Not enough data to read the logical screen descriptor");
}
- this.logicalScreenDescriptor = GifLogicalScreenDescriptor.Parse(this.buffer);
+ this.logicalScreenDescriptor = GifLogicalScreenDescriptor.Parse(this.buffer.Span);
}
///
@@ -306,8 +306,8 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
long position = stream.Position;
if (appLength == GifConstants.ApplicationBlockSize)
{
- stream.Read(this.buffer, 0, GifConstants.ApplicationBlockSize);
- bool isXmp = this.buffer.AsSpan().StartsWith(GifConstants.XmpApplicationIdentificationBytes);
+ stream.Read(this.buffer.Span, 0, GifConstants.ApplicationBlockSize);
+ bool isXmp = this.buffer.Span.StartsWith(GifConstants.XmpApplicationIdentificationBytes);
if (isXmp && !this.skipMetadata)
{
GifXmpApplicationExtension extension = GifXmpApplicationExtension.Read(stream, this.memoryAllocator);
@@ -331,8 +331,8 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
// http://www.vurdalakov.net/misc/gif/netscape-buffering-application-extension
if (subBlockSize == GifConstants.NetscapeLoopingSubBlockSize)
{
- stream.Read(this.buffer, 0, GifConstants.NetscapeLoopingSubBlockSize);
- this.gifMetadata!.RepeatCount = GifNetscapeLoopingApplicationExtension.Parse(this.buffer.AsSpan(1)).RepeatCount;
+ stream.Read(this.buffer.Span, 0, GifConstants.NetscapeLoopingSubBlockSize);
+ this.gifMetadata!.RepeatCount = GifNetscapeLoopingApplicationExtension.Parse(this.buffer.Span.Slice(1)).RepeatCount;
stream.Skip(1); // Skip the terminator.
return;
}
@@ -762,4 +762,11 @@ internal sealed class GifDecoderCore : IImageDecoderInternals
}
}
}
+
+ private unsafe struct ScratchBuffer
+ {
+ private fixed byte scratch[16];
+
+ public Span Span => MemoryMarshal.CreateSpan(ref this.scratch[0], 16);
+ }
}
diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
index f736da78dd..c01cc78ef0 100644
--- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
@@ -28,11 +28,6 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
///
private readonly Configuration configuration;
- ///
- /// A reusable buffer used to reduce allocations.
- ///
- private readonly byte[] buffer = new byte[20];
-
///
/// Whether to skip metadata during encode.
///
@@ -324,9 +319,10 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
backgroundColorIndex: unchecked((byte)transparencyIndex),
ratio);
- descriptor.WriteTo(this.buffer);
+ Span buffer = stackalloc byte[20];
+ descriptor.WriteTo(buffer);
- stream.Write(this.buffer, 0, GifLogicalScreenDescriptor.Size);
+ stream.Write(buffer, 0, GifLogicalScreenDescriptor.Size);
}
///
@@ -365,12 +361,14 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
return;
}
+ Span buffer = stackalloc byte[2];
+
for (int i = 0; i < metadata.Comments.Count; i++)
{
string comment = metadata.Comments[i];
- this.buffer[0] = GifConstants.ExtensionIntroducer;
- this.buffer[1] = GifConstants.CommentLabel;
- stream.Write(this.buffer, 0, 2);
+ buffer[1] = GifConstants.CommentLabel;
+ buffer[0] = GifConstants.ExtensionIntroducer;
+ stream.Write(buffer);
// Comment will be stored in chunks of 255 bytes, if it exceeds this size.
ReadOnlySpan commentSpan = comment.AsSpan();
@@ -437,22 +435,23 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
private void WriteExtension(TGifExtension extension, Stream stream)
where TGifExtension : struct, IGifExtension
{
- IMemoryOwner? owner = null;
- Span extensionBuffer;
int extensionSize = extension.ContentLength;
if (extensionSize == 0)
{
return;
}
- else if (extensionSize > this.buffer.Length - 3)
+
+ IMemoryOwner? owner = null;
+ Span extensionBuffer = stackalloc byte[0]; // workaround compiler limitation
+ if (extensionSize > 128)
{
owner = this.memoryAllocator.Allocate(extensionSize + 3);
extensionBuffer = owner.GetSpan();
}
else
{
- extensionBuffer = this.buffer;
+ extensionBuffer = stackalloc byte[extensionSize + 3];
}
extensionBuffer[0] = GifConstants.ExtensionIntroducer;
@@ -489,9 +488,10 @@ internal sealed class GifEncoderCore : IImageEncoderInternals
height: (ushort)image.Height,
packed: packedValue);
- descriptor.WriteTo(this.buffer);
+ Span buffer = stackalloc byte[20];
+ descriptor.WriteTo(buffer);
- stream.Write(this.buffer, 0, GifImageDescriptor.Size);
+ stream.Write(buffer, 0, GifImageDescriptor.Size);
}
///