diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs index 21b6ecb5b8..9651cf4408 100644 --- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs @@ -275,13 +275,18 @@ namespace SixLabors.ImageSharp.Formats.Gif private void WriteImageDescriptor(ImageFrame image, Stream stream) where TPixel : struct, IPixel { + byte packedValue = GifImageDescriptor.GetPackedValue( + localColorTableFlag: true, + interfaceFlag: false, + sortFlag: false, + localColorTableSize: this.bitDepth); // Note: we subtract 1 from the colorTableSize writing + var descriptor = new GifImageDescriptor( left: 0, top: 0, width: (ushort)image.Width, height: (ushort)image.Height, - localColorTableFlag: true, - localColorTableSize: this.bitDepth); // Note: we subtract 1 from the colorTableSize writing + packed: packedValue); descriptor.WriteTo(this.buffer); diff --git a/src/ImageSharp/Formats/Gif/Sections/GifImageDescriptor.cs b/src/ImageSharp/Formats/Gif/Sections/GifImageDescriptor.cs index d17bc20391..52ed9bed93 100644 --- a/src/ImageSharp/Formats/Gif/Sections/GifImageDescriptor.cs +++ b/src/ImageSharp/Formats/Gif/Sections/GifImageDescriptor.cs @@ -2,7 +2,8 @@ // Licensed under the Apache License, Version 2.0. using System; -using System.Buffers.Binary; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace SixLabors.ImageSharp.Formats.Gif { @@ -12,6 +13,7 @@ namespace SixLabors.ImageSharp.Formats.Gif /// Each image must fit within the boundaries of the /// Logical Screen, as defined in the Logical Screen Descriptor. /// + [StructLayout(LayoutKind.Sequential, Pack = 1)] internal readonly struct GifImageDescriptor { public const int Size = 10; @@ -21,19 +23,13 @@ namespace SixLabors.ImageSharp.Formats.Gif ushort top, ushort width, ushort height, - bool localColorTableFlag, - int localColorTableSize, - bool interlaceFlag = false, - bool sortFlag = false) + byte packed) { this.Left = left; this.Top = top; this.Width = width; this.Height = height; - this.LocalColorTableFlag = localColorTableFlag; - this.LocalColorTableSize = localColorTableSize; - this.InterlaceFlag = interlaceFlag; - this.SortFlag = sortFlag; + this.Packed = packed; } /// @@ -61,63 +57,41 @@ namespace SixLabors.ImageSharp.Formats.Gif public ushort Height { get; } /// - /// Gets a value indicating whether the presence of a Local Color Table immediately - /// follows this Image Descriptor. + /// Gets the packed value of localColorTableFlag, interlaceFlag, sortFlag, and localColorTableSize. /// - public bool LocalColorTableFlag { get; } + public byte Packed { get; } - /// - /// Gets the local color table size. - /// If the Local Color Table Flag is set to 1, the value in this field - /// is used to calculate the number of bytes contained in the Local Color Table. - /// - public int LocalColorTableSize { get; } + public bool LocalColorTableFlag => ((this.Packed & 0x80) >> 7) == 1; - /// - /// Gets a value indicating whether the image is to be interlaced. - /// An image is interlaced in a four-pass interlace pattern. - /// - public bool InterlaceFlag { get; } + public int LocalColorTableSize => 2 << (this.Packed & 0x07); - /// - /// Gets a value indicating whether the Global Color Table is sorted. - /// - public bool SortFlag { get; } + public bool InterlaceFlag => ((this.Packed & 0x40) >> 6) == 1; - public byte PackFields() + public void WriteTo(Span buffer) { - var field = default(PackedField); + buffer[0] = GifConstants.ImageDescriptorLabel; - field.SetBit(0, this.LocalColorTableFlag); // 0: Local color table flag = 1 (LCT used) - field.SetBit(1, this.InterlaceFlag); // 1: Interlace flag 0 - field.SetBit(2, this.SortFlag); // 2: Sort flag 0 - field.SetBits(5, 3, this.LocalColorTableSize - 1); // 3-4: Reserved, 5-7 : LCT size. 2^(N+1) + ref GifImageDescriptor dest = ref Unsafe.As(ref MemoryMarshal.GetReference(buffer.Slice(1))); - return field.Byte; + dest = this; } - public void WriteTo(Span buffer) + public static GifImageDescriptor Parse(ReadOnlySpan buffer) { - buffer[0] = GifConstants.ImageDescriptorLabel; // Image Separator (0x2C) - BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(1, 2), this.Left); // Image Left Position - BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(3, 2), this.Top); // Image Top Position - BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(5, 2), this.Width); // Image Width - BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(7, 2), this.Height); // Image Height - buffer[9] = this.PackFields(); // Packed Fields + return MemoryMarshal.Cast(buffer)[0]; } - public static GifImageDescriptor Parse(ReadOnlySpan buffer) + public static byte GetPackedValue(bool localColorTableFlag, bool interfaceFlag, bool sortFlag, int localColorTableSize) { - byte packed = buffer[8]; - - return new GifImageDescriptor( - left: BinaryPrimitives.ReadUInt16LittleEndian(buffer.Slice(0, 2)), - top: BinaryPrimitives.ReadUInt16LittleEndian(buffer.Slice(2, 2)), - width: BinaryPrimitives.ReadUInt16LittleEndian(buffer.Slice(4, 2)), - height: BinaryPrimitives.ReadUInt16LittleEndian(buffer.Slice(6, 2)), - localColorTableFlag: ((packed & 0x80) >> 7) == 1, - localColorTableSize: 2 << (packed & 0x07), - interlaceFlag: ((packed & 0x40) >> 6) == 1); + var field = default(PackedField); + + field.SetBit(0, localColorTableFlag); // 0: Local color table flag = 1 (LCT used) + field.SetBit(1, interfaceFlag); // 1: Interlace flag 0 + field.SetBit(2, sortFlag); // 2: Sort flag 0 + field.SetBits(5, 3, localColorTableSize - 1); // 3-4: Reserved, 5-7 : LCT size. 2^(N+1) + + return field.Byte; } } + } \ No newline at end of file