diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
index eaf79671f..167fb94ff 100644
--- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs
@@ -262,23 +262,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
{
this.currentStream.Read(this.buffer, 0, 7);
- byte packed = this.buffer[4];
-
- this.logicalScreenDescriptor = new GifLogicalScreenDescriptor
- {
- Width = BitConverter.ToInt16(this.buffer, 0),
- Height = BitConverter.ToInt16(this.buffer, 2),
- BitsPerPixel = (this.buffer[4] & 0x07) + 1, // The lowest 3 bits represent the bit depth minus 1
- BackgroundColorIndex = this.buffer[5],
- PixelAspectRatio = this.buffer[6],
- GlobalColorTableFlag = ((packed & 0x80) >> 7) == 1,
- GlobalColorTableSize = 2 << (packed & 0x07)
- };
-
- if (this.logicalScreenDescriptor.GlobalColorTableSize > 255 * 4)
- {
- throw new ImageFormatException($"Invalid gif colormap size '{this.logicalScreenDescriptor.GlobalColorTableSize}'");
- }
+ this.logicalScreenDescriptor = GifLogicalScreenDescriptor.Parse(this.buffer);
}
///
@@ -528,7 +512,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// The meta data.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void SetFrameMetaData(ImageFrameMetaData meta)
- {
+ {
if (this.graphicsControlExtension.DelayTime > 0)
{
meta.FrameDelay = this.graphicsControlExtension.DelayTime;
diff --git a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
index f0e95ed1d..7d51acb99 100644
--- a/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
+++ b/src/ImageSharp/Formats/Gif/GifEncoderCore.cs
@@ -90,7 +90,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
this.WriteHeader(writer);
// Write the LSD. We'll use local color tables for now.
- this.WriteLogicalScreenDescriptor(image, writer, index);
+ this.WriteLogicalScreenDescriptor(image, stream, index);
// Write the first frame.
this.WriteComments(image, writer);
@@ -165,35 +165,25 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// The pixel format.
/// The image to encode.
- /// The writer to write to the stream with.
+ /// The stream to write to.
/// The transparency index to set the default background index to.
- private void WriteLogicalScreenDescriptor(Image image, EndianBinaryWriter writer, int transparencyIndex)
+ private void WriteLogicalScreenDescriptor(Image image, Stream stream, int transparencyIndex)
where TPixel : struct, IPixel
{
- var descriptor = new GifLogicalScreenDescriptor
- {
- Width = (short)image.Width,
- Height = (short)image.Height,
- GlobalColorTableFlag = false, // TODO: Always false for now.
- GlobalColorTableSize = this.bitDepth - 1,
- BackgroundColorIndex = unchecked((byte)transparencyIndex)
- };
-
- writer.Write((ushort)descriptor.Width);
- writer.Write((ushort)descriptor.Height);
-
- var field = default(PackedField);
- field.SetBit(0, descriptor.GlobalColorTableFlag); // 1 : Global color table flag = 1 || 0 (GCT used/ not used)
- field.SetBits(1, 3, descriptor.GlobalColorTableSize); // 2-4 : color resolution
- field.SetBit(4, false); // 5 : GCT sort flag = 0
- field.SetBits(5, 3, descriptor.GlobalColorTableSize); // 6-8 : GCT size. 2^(N+1)
-
- // Reduce the number of writes
- this.buffer[0] = field.Byte;
- this.buffer[1] = descriptor.BackgroundColorIndex; // Background Color Index
- this.buffer[2] = descriptor.PixelAspectRatio; // Pixel aspect ratio. Assume 1:1
+ var descriptor = new GifLogicalScreenDescriptor(
+ width: (ushort)image.Width,
+ height: (ushort)image.Height,
+ bitsPerPixel: 0,
+ pixelAspectRatio: 0,
+ globalColorTableFlag: false, // TODO: Always false for now.
+ globalColorTableSize: this.bitDepth - 1,
+ backgroundColorIndex: unchecked((byte)transparencyIndex)
+ );
+
+ descriptor.WriteTo(this.buffer);
+
+ stream.Write(this.buffer, 0, GifLogicalScreenDescriptor.Size);
- writer.Write(this.buffer, 0, 3);
}
///
@@ -229,7 +219,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// The pixel format.
/// The to be encoded.
- /// The stream to write to.
+ /// The stream to write to.
private void WriteComments(Image image, EndianBinaryWriter writer)
where TPixel : struct, IPixel
{
diff --git a/src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs b/src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs
index 05f232a4b..4f2a17ddf 100644
--- a/src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs
+++ b/src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs
@@ -1,6 +1,9 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
+using System;
+using System.Buffers.Binary;
+
namespace SixLabors.ImageSharp.Formats.Gif
{
///
@@ -8,51 +11,116 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// necessary to define the area of the display device
/// within which the images will be rendered
///
- internal sealed class GifLogicalScreenDescriptor
+ internal readonly struct GifLogicalScreenDescriptor
{
///
- /// Gets or sets the width, in pixels, of the Logical Screen where the images will
+ /// The size of the written structure.
+ ///
+ public const int Size = 7;
+
+ public GifLogicalScreenDescriptor(
+ ushort width,
+ ushort height,
+ int bitsPerPixel,
+ byte backgroundColorIndex,
+ byte pixelAspectRatio,
+ bool globalColorTableFlag,
+ int globalColorTableSize)
+ {
+ this.Width = width;
+ this.Height = height;
+ this.BitsPerPixel = bitsPerPixel;
+ this.BackgroundColorIndex = backgroundColorIndex;
+ this.PixelAspectRatio = pixelAspectRatio;
+ this.GlobalColorTableFlag = globalColorTableFlag;
+ this.GlobalColorTableSize = globalColorTableSize;
+ }
+
+ ///
+ /// Gets the width, in pixels, of the Logical Screen where the images will
/// be rendered in the displaying device.
///
- public short Width { get; set; }
+ public ushort Width { get; }
///
- /// Gets or sets the height, in pixels, of the Logical Screen where the images will be
+ /// Gets the height, in pixels, of the Logical Screen where the images will be
/// rendered in the displaying device.
///
- public short Height { get; set; }
+ public ushort Height { get; }
///
- /// Gets or sets the color depth, in number of bits per pixel.
+ /// Gets the color depth, in number of bits per pixel.
///
- public int BitsPerPixel { get; set; }
+ public int BitsPerPixel { get; }
///
- /// Gets or sets the index at the Global Color Table for the Background Color.
+ /// Gets the index at the Global Color Table for the Background Color.
/// The Background Color is the color used for those
/// pixels on the screen that are not covered by an image.
///
- public byte BackgroundColorIndex { get; set; }
+ public byte BackgroundColorIndex { get; }
///
- /// Gets or sets the pixel aspect ratio. Default to 0.
+ /// Gets the pixel aspect ratio. Default to 0.
///
- public byte PixelAspectRatio { get; set; }
+ public byte PixelAspectRatio { get; }
///
- /// Gets or sets a value indicating whether a flag denoting the presence of a Global Color Table
+ /// Gets a value indicating whether a flag denoting the presence of a Global Color Table
/// should be set.
/// If the flag is set, the Global Color Table will immediately
/// follow the Logical Screen Descriptor.
///
- public bool GlobalColorTableFlag { get; set; }
+ public bool GlobalColorTableFlag { get; }
///
- /// Gets or sets the global color table size.
+ /// Gets the global color table size.
/// If the Global Color Table Flag is set to 1,
/// the value in this field is used to calculate the number of
/// bytes contained in the Global Color Table.
///
- public int GlobalColorTableSize { get; set; }
+ public int GlobalColorTableSize { get; }
+
+ public byte PackFields()
+ {
+ PackedField field = default;
+
+ field.SetBit(0, this.GlobalColorTableFlag); // 0 : Global Color Table Flag | 1 bit
+ field.SetBits(1, 3, this.GlobalColorTableSize); // 1-3 : Color Resolution | 3 bits
+ field.SetBit(4, false); // 4 : Sort Flag | 1 bits
+ field.SetBits(5, 3, this.GlobalColorTableSize); // 5-7 : Size of Global Color Table | 3 bits
+
+ return field.Byte;
+ }
+
+ public void WriteTo(Span buffer)
+ {
+ BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(0, 2), this.Width); // Logical Screen Width
+ BinaryPrimitives.WriteUInt16LittleEndian(buffer.Slice(2, 2), this.Height); // Logical Screen Height
+ buffer[4] = this.PackFields(); // Packed Fields
+ buffer[5] = this.BackgroundColorIndex; // Background Color Index
+ buffer[6] = this.PixelAspectRatio; // Pixel Aspect Ratio
+ }
+
+ public static GifLogicalScreenDescriptor Parse(ReadOnlySpan buffer)
+ {
+ byte packed = buffer[4];
+
+ var result = new GifLogicalScreenDescriptor(
+ width: BinaryPrimitives.ReadUInt16LittleEndian(buffer.Slice(0, 2)),
+ height: BinaryPrimitives.ReadUInt16LittleEndian(buffer.Slice(2, 2)),
+ bitsPerPixel: (buffer[4] & 0x07) + 1, // The lowest 3 bits represent the bit depth minus 1
+ backgroundColorIndex: buffer[5],
+ pixelAspectRatio: buffer[6],
+ globalColorTableFlag: ((packed & 0x80) >> 7) == 1,
+ globalColorTableSize: 2 << (packed & 0x07));
+
+ if (result.GlobalColorTableSize > 255 * 4)
+ {
+ throw new ImageFormatException($"Invalid gif colormap size '{result.GlobalColorTableSize}'");
+ }
+
+ return result;
+ }
}
-}
+}
\ No newline at end of file