diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
index e93e23f708..d92a0f591e 100644
--- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
@@ -254,15 +254,33 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// The index of the color in the color palette to make transparent.
private void WriteGraphicalControlExtension(ImageFrameMetaData metaData, Stream stream, int transparencyIndex)
{
- var extension = new GifGraphicsControlExtension(
+ byte packedValue = GifGraphicsControlExtension.GetPackedValue(
disposalMethod: metaData.DisposalMethod,
- transparencyFlag: transparencyIndex > -1,
+ transparencyFlag: transparencyIndex > -1);
+
+ var extension = new GifGraphicsControlExtension(
+ packed: packedValue,
transparencyIndex: unchecked((byte)transparencyIndex),
delayTime: (ushort)metaData.FrameDelay);
- extension.WriteTo(this.buffer);
+ this.WriteExtension(extension, stream);
+ }
+
+ ///
+ /// Writes the provided extension to the stream.
+ ///
+ /// The extension to write to the stream.
+ /// The stream to write to.
+ public void WriteExtension(IGifExtension extension, Stream stream)
+ {
+ this.buffer[0] = GifConstants.ExtensionIntroducer;
+ this.buffer[1] = extension.Label;
+
+ int extensionSize = extension.WriteTo(this.buffer.AsSpan(2));
+
+ this.buffer[extensionSize + 2] = GifConstants.Terminator;
- stream.Write(this.buffer, 0, GifGraphicsControlExtension.Size);
+ stream.Write(this.buffer, 0, 8);
}
///
diff --git a/src/ImageSharp/Formats/Gif/Sections/GifGraphicsControlExtension.cs b/src/ImageSharp/Formats/Gif/Sections/GifGraphicsControlExtension.cs
index bb4c8a59ec..230c9cca2d 100644
--- a/src/ImageSharp/Formats/Gif/Sections/GifGraphicsControlExtension.cs
+++ b/src/ImageSharp/Formats/Gif/Sections/GifGraphicsControlExtension.cs
@@ -3,6 +3,8 @@
using System;
using System.Buffers.Binary;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Gif
{
@@ -10,34 +12,31 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// The Graphic Control Extension contains parameters used when
/// processing a graphic rendering block.
///
- internal readonly struct GifGraphicsControlExtension
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ internal readonly struct GifGraphicsControlExtension : IGifExtension
{
- public const int Size = 8;
+ public const int Size = 4;
public GifGraphicsControlExtension(
- DisposalMethod disposalMethod,
- bool transparencyFlag,
+ byte packed,
ushort delayTime,
byte transparencyIndex)
{
- this.DisposalMethod = disposalMethod;
- this.TransparencyFlag = transparencyFlag;
+ this.BlockSize = 4;
+ this.Packed = packed;
this.DelayTime = delayTime;
this.TransparencyIndex = transparencyIndex;
}
///
- /// Gets the disposal method which indicates the way in which the
- /// graphic is to be treated after being displayed.
+ /// Gets the size of the block.
///
- public DisposalMethod DisposalMethod { get; }
+ public int BlockSize { get; }
///
- /// Gets a value indicating whether transparency flag is to be set.
- /// This indicates whether a transparency index is given in the Transparent Index field.
- /// (This field is the least significant bit of the byte.)
+ /// Gets the packed disposalMethod and transparencyFlag value.
///
- public bool TransparencyFlag { get; }
+ public byte Packed { get; }
///
/// Gets the delay time.
@@ -54,41 +53,45 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
public byte TransparencyIndex { get; }
- public byte PackField()
- {
- PackedField field = default;
+ ///
+ /// Gets the disposal method which indicates the way in which the
+ /// graphic is to be treated after being displayed.
+ ///
+ public DisposalMethod DisposalMethod => (DisposalMethod)((this.Packed & 0x1C) >> 2);
+
+ ///
+ /// Gets a value indicating whether transparency flag is to be set.
+ /// This indicates whether a transparency index is given in the Transparent Index field.
+ /// (This field is the least significant bit of the byte.)
+ ///
+ public bool TransparencyFlag => (this.Packed & 0x01) == 1;
- field.SetBits(3, 3, (int)this.DisposalMethod); // 1-3 : Reserved, 4-6 : Disposal
+ byte IGifExtension.Label => GifConstants.GraphicControlLabel;
- // TODO: Allow this as an option.
- field.SetBit(6, false); // 7 : User input - 0 = none
- field.SetBit(7, this.TransparencyFlag); // 8: Has transparent.
+ public int WriteTo(Span buffer)
+ {
+ ref GifGraphicsControlExtension dest = ref Unsafe.As(ref MemoryMarshal.GetReference(buffer));
- return field.Byte;
+ dest = this;
+
+ return 5;
}
- public void WriteTo(Span buffer)
+ public static GifGraphicsControlExtension Parse(ReadOnlySpan buffer)
{
- buffer[0] = GifConstants.ExtensionIntroducer;
- buffer[1] = GifConstants.GraphicControlLabel;
- buffer[2] = 4; // Block Size
- buffer[3] = this.PackField(); // Packed Field
- BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(4, 2), this.DelayTime); // Delay Time
- buffer[6] = this.TransparencyIndex;
- buffer[7] = GifConstants.Terminator;
+ return MemoryMarshal.Cast(buffer)[0];
}
- public static GifGraphicsControlExtension Parse(ReadOnlySpan buffer)
+ public static byte GetPackedValue(DisposalMethod disposalMethod, bool userInputFlag = false, bool transparencyFlag = false)
{
- // We've already read the Extension Introducer introducer & Graphic Control Label
- // Start from the block size (0)
- byte packed = buffer[1];
-
- return new GifGraphicsControlExtension(
- disposalMethod: (DisposalMethod)((packed & 0x1C) >> 2),
- delayTime: BinaryPrimitives.ReadUInt16LittleEndian(buffer.Slice(2, 2)),
- transparencyIndex: buffer[4],
- transparencyFlag: (packed & 0x01) == 1);
+ PackedField field = default;
+
+ // --------------------------------------- // Reserved | 3 bits
+ field.SetBits(3, 3, (int)disposalMethod); // Disposal Method | 3 bits
+ field.SetBit(6, userInputFlag); // User Input Flag | 1 bit
+ field.SetBit(7, transparencyFlag); // Transparent Color Flag | 1 bit
+
+ return field.Byte;
}
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Gif/Sections/IGifExtension.cs b/src/ImageSharp/Formats/Gif/Sections/IGifExtension.cs
new file mode 100644
index 0000000000..2fdc233b0c
--- /dev/null
+++ b/src/ImageSharp/Formats/Gif/Sections/IGifExtension.cs
@@ -0,0 +1,22 @@
+using System;
+
+namespace SixLabors.ImageSharp.Formats.Gif
+{
+ ///
+ /// A base interface for GIF extensions.
+ ///
+ public interface IGifExtension
+ {
+ ///
+ /// Gets the label identifying the extensions.
+ ///
+ byte Label { get; }
+
+ ///
+ /// Writes the extension data to the buffer.
+ ///
+ /// The buffer to write the extension to.
+ /// The number of bytes written to the buffer.
+ int WriteTo(Span buffer);
+ }
+}
\ No newline at end of file