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