Browse Source

Make GifLogicalScreenDescriptor a struct

pull/533/head
Jason Nelson 8 years ago
parent
commit
36def70cd3
  1. 20
      src/ImageSharp/Formats/Gif/GifDecoderCore.cs
  2. 44
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  3. 100
      src/ImageSharp/Formats/Gif/Sections/GifLogicalScreenDescriptor.cs

20
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);
}
/// <summary>
@ -528,7 +512,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <param name="meta">The meta data.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void SetFrameMetaData(ImageFrameMetaData meta)
{
{
if (this.graphicsControlExtension.DelayTime > 0)
{
meta.FrameDelay = this.graphicsControlExtension.DelayTime;

44
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
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The image to encode.</param>
/// <param name="writer">The writer to write to the stream with.</param>
/// <param name="stream">The stream to write to.</param>
/// <param name="transparencyIndex">The transparency index to set the default background index to.</param>
private void WriteLogicalScreenDescriptor<TPixel>(Image<TPixel> image, EndianBinaryWriter writer, int transparencyIndex)
private void WriteLogicalScreenDescriptor<TPixel>(Image<TPixel> image, Stream stream, int transparencyIndex)
where TPixel : struct, IPixel<TPixel>
{
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);
}
/// <summary>
@ -229,7 +219,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="ImageFrame{TPixel}"/> to be encoded.</param>
/// <param name="writer">The stream to write to.</param>
/// <param name="stream">The stream to write to.</param>
private void WriteComments<TPixel>(Image<TPixel> image, EndianBinaryWriter writer)
where TPixel : struct, IPixel<TPixel>
{

100
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
{
/// <summary>
@ -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
/// </summary>
internal sealed class GifLogicalScreenDescriptor
internal readonly struct GifLogicalScreenDescriptor
{
/// <summary>
/// Gets or sets the width, in pixels, of the Logical Screen where the images will
/// The size of the written structure.
/// </summary>
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;
}
/// <summary>
/// Gets the width, in pixels, of the Logical Screen where the images will
/// be rendered in the displaying device.
/// </summary>
public short Width { get; set; }
public ushort Width { get; }
/// <summary>
/// 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.
/// </summary>
public short Height { get; set; }
public ushort Height { get; }
/// <summary>
/// Gets or sets the color depth, in number of bits per pixel.
/// Gets the color depth, in number of bits per pixel.
/// </summary>
public int BitsPerPixel { get; set; }
public int BitsPerPixel { get; }
/// <summary>
/// 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.
/// </summary>
public byte BackgroundColorIndex { get; set; }
public byte BackgroundColorIndex { get; }
/// <summary>
/// Gets or sets the pixel aspect ratio. Default to 0.
/// Gets the pixel aspect ratio. Default to 0.
/// </summary>
public byte PixelAspectRatio { get; set; }
public byte PixelAspectRatio { get; }
/// <summary>
/// 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.
/// </summary>
public bool GlobalColorTableFlag { get; set; }
public bool GlobalColorTableFlag { get; }
/// <summary>
/// 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.
/// </summary>
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<byte> 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<byte> 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;
}
}
}
}
Loading…
Cancel
Save