mirror of https://github.com/SixLabors/ImageSharp
committed by
GitHub
29 changed files with 439 additions and 1333 deletions
@ -1,177 +0,0 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Diagnostics; |
|
||||
using System.Runtime.CompilerServices; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Formats.Gif |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Represents a byte of data in a GIF data stream which contains a number
|
|
||||
/// of data items.
|
|
||||
/// </summary>
|
|
||||
internal readonly struct PackedField : IEquatable<PackedField> |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// The individual bits representing the packed byte.
|
|
||||
/// </summary>
|
|
||||
private static readonly bool[] Bits = new bool[8]; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the byte which represents the data items held in this instance.
|
|
||||
/// </summary>
|
|
||||
public byte Byte |
|
||||
{ |
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] |
|
||||
get |
|
||||
{ |
|
||||
int returnValue = 0; |
|
||||
int bitShift = 7; |
|
||||
foreach (bool bit in Bits) |
|
||||
{ |
|
||||
int bitValue; |
|
||||
if (bit) |
|
||||
{ |
|
||||
bitValue = 1 << bitShift; |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
bitValue = 0; |
|
||||
} |
|
||||
|
|
||||
returnValue |= bitValue; |
|
||||
bitShift--; |
|
||||
} |
|
||||
|
|
||||
return Convert.ToByte(returnValue & 0xFF); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Returns a new <see cref="PackedField"/> with the bits in the packed fields to
|
|
||||
/// the corresponding bits from the supplied byte.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The value to pack.</param>
|
|
||||
/// <returns>The <see cref="PackedField"/></returns>
|
|
||||
public static PackedField FromInt(byte value) |
|
||||
{ |
|
||||
PackedField packed = default; |
|
||||
packed.SetBits(0, 8, value); |
|
||||
return packed; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Sets the specified bit within the packed fields to the supplied
|
|
||||
/// value.
|
|
||||
/// </summary>
|
|
||||
/// <param name="index">
|
|
||||
/// The zero-based index within the packed fields of the bit to set.
|
|
||||
/// </param>
|
|
||||
/// <param name="valueToSet">
|
|
||||
/// The value to set the bit to.
|
|
||||
/// </param>
|
|
||||
public void SetBit(int index, bool valueToSet) |
|
||||
{ |
|
||||
DebugGuard.MustBeBetweenOrEqualTo(index, 0, 7, nameof(index)); |
|
||||
Bits[index] = valueToSet; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Sets the specified bits within the packed fields to the supplied
|
|
||||
/// value.
|
|
||||
/// </summary>
|
|
||||
/// <param name="startIndex">The zero-based index within the packed fields of the first bit to set.</param>
|
|
||||
/// <param name="length">The number of bits to set.</param>
|
|
||||
/// <param name="valueToSet">The value to set the bits to.</param>
|
|
||||
public void SetBits(int startIndex, int length, int valueToSet) |
|
||||
{ |
|
||||
DebugGuard.MustBeBetweenOrEqualTo(startIndex, 0, 7, nameof(startIndex)); |
|
||||
DebugCheckLength(startIndex, length); |
|
||||
|
|
||||
int bitShift = length - 1; |
|
||||
for (int i = startIndex; i < startIndex + length; i++) |
|
||||
{ |
|
||||
int bitValueIfSet = 1 << bitShift; |
|
||||
int bitValue = valueToSet & bitValueIfSet; |
|
||||
int bitIsSet = bitValue >> bitShift; |
|
||||
Bits[i] = bitIsSet == 1; |
|
||||
bitShift--; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the value of the specified bit within the byte.
|
|
||||
/// </summary>
|
|
||||
/// <param name="index">The zero-based index of the bit to get.</param>
|
|
||||
/// <returns>
|
|
||||
/// The value of the specified bit within the byte.
|
|
||||
/// </returns>
|
|
||||
public bool GetBit(int index) |
|
||||
{ |
|
||||
DebugGuard.MustBeBetweenOrEqualTo(index, 0, 7, nameof(index)); |
|
||||
return Bits[index]; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the value of the specified bits within the byte.
|
|
||||
/// </summary>
|
|
||||
/// <param name="startIndex">The zero-based index of the first bit to get.</param>
|
|
||||
/// <param name="length">The number of bits to get.</param>
|
|
||||
/// <returns>
|
|
||||
/// The value of the specified bits within the byte.
|
|
||||
/// </returns>
|
|
||||
public int GetBits(int startIndex, int length) |
|
||||
{ |
|
||||
DebugGuard.MustBeBetweenOrEqualTo(startIndex, 1, 8, nameof(startIndex)); |
|
||||
DebugCheckLength(startIndex, length); |
|
||||
|
|
||||
int returnValue = 0; |
|
||||
int bitShift = length - 1; |
|
||||
for (int i = startIndex; i < startIndex + length; i++) |
|
||||
{ |
|
||||
int bitValue = (Bits[i] ? 1 : 0) << bitShift; |
|
||||
returnValue += bitValue; |
|
||||
bitShift--; |
|
||||
} |
|
||||
|
|
||||
return returnValue; |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override bool Equals(object obj) |
|
||||
{ |
|
||||
return obj is PackedField other && this.Equals(other); |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public bool Equals(PackedField other) |
|
||||
{ |
|
||||
return this.Byte.Equals(other.Byte); |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override string ToString() |
|
||||
{ |
|
||||
return $"PackedField [ Byte={this.Byte} ]"; |
|
||||
} |
|
||||
|
|
||||
/// <inheritdoc/>
|
|
||||
public override int GetHashCode() |
|
||||
{ |
|
||||
return this.Byte.GetHashCode(); |
|
||||
} |
|
||||
|
|
||||
[Conditional("DEBUG")] |
|
||||
private static void DebugCheckLength(int startIndex, int length) |
|
||||
{ |
|
||||
if (length < 1 || startIndex + length > 8) |
|
||||
{ |
|
||||
string message = "Length must be greater than zero and the sum of length and start index must be less than 8. " |
|
||||
+ $"Supplied length: {length}. Supplied start index: {startIndex}"; |
|
||||
|
|
||||
throw new ArgumentOutOfRangeException(nameof(length), message); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,106 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using System; |
||||
|
using System.Runtime.CompilerServices; |
||||
|
using System.Runtime.InteropServices; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Gif |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// The Graphic Control Extension contains parameters used when
|
||||
|
/// processing a graphic rendering block.
|
||||
|
/// </summary>
|
||||
|
[StructLayout(LayoutKind.Sequential, Pack = 1)] |
||||
|
internal readonly struct GifGraphicControlExtension : IGifExtension |
||||
|
{ |
||||
|
public GifGraphicControlExtension( |
||||
|
byte packed, |
||||
|
ushort delayTime, |
||||
|
byte transparencyIndex) |
||||
|
{ |
||||
|
this.BlockSize = 4; |
||||
|
this.Packed = packed; |
||||
|
this.DelayTime = delayTime; |
||||
|
this.TransparencyIndex = transparencyIndex; |
||||
|
} |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the size of the block.
|
||||
|
/// </summary>
|
||||
|
public byte BlockSize { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the packed disposalMethod and transparencyFlag value.
|
||||
|
/// </summary>
|
||||
|
public byte Packed { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the delay time in of hundredths (1/100) of a second
|
||||
|
/// to wait before continuing with the processing of the Data Stream.
|
||||
|
/// The clock starts ticking immediately after the graphic is rendered.
|
||||
|
/// </summary>
|
||||
|
public ushort DelayTime { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the transparency index.
|
||||
|
/// The Transparency Index is such that when encountered, the corresponding pixel
|
||||
|
/// of the display device is not modified and processing goes on to the next pixel.
|
||||
|
/// </summary>
|
||||
|
public byte TransparencyIndex { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets the disposal method which indicates the way in which the
|
||||
|
/// graphic is to be treated after being displayed.
|
||||
|
/// </summary>
|
||||
|
public DisposalMethod DisposalMethod => (DisposalMethod)((this.Packed & 0x1C) >> 2); |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Gets a value indicating whether transparency flag is to be set.
|
||||
|
/// This indicates whether a transparency index is given in the Transparent Index field.
|
||||
|
/// </summary>
|
||||
|
public bool TransparencyFlag => (this.Packed & 0x01) == 1; |
||||
|
|
||||
|
byte IGifExtension.Label => GifConstants.GraphicControlLabel; |
||||
|
|
||||
|
public int WriteTo(Span<byte> buffer) |
||||
|
{ |
||||
|
ref GifGraphicControlExtension dest = ref Unsafe.As<byte, GifGraphicControlExtension>(ref MemoryMarshal.GetReference(buffer)); |
||||
|
|
||||
|
dest = this; |
||||
|
|
||||
|
return 5; |
||||
|
} |
||||
|
|
||||
|
public static GifGraphicControlExtension Parse(ReadOnlySpan<byte> buffer) |
||||
|
{ |
||||
|
return MemoryMarshal.Cast<byte, GifGraphicControlExtension>(buffer)[0]; |
||||
|
} |
||||
|
|
||||
|
public static byte GetPackedValue(DisposalMethod disposalMethod, bool userInputFlag = false, bool transparencyFlag = false) |
||||
|
{ |
||||
|
/* |
||||
|
Reserved | 3 Bits |
||||
|
Disposal Method | 3 Bits |
||||
|
User Input Flag | 1 Bit |
||||
|
Transparent Color Flag | 1 Bit |
||||
|
*/ |
||||
|
|
||||
|
byte value = 0; |
||||
|
|
||||
|
value |= (byte)((int)disposalMethod << 2); |
||||
|
|
||||
|
if (userInputFlag) |
||||
|
{ |
||||
|
value |= 1 << 1; |
||||
|
} |
||||
|
|
||||
|
if (transparencyFlag) |
||||
|
{ |
||||
|
value |= 1; |
||||
|
} |
||||
|
|
||||
|
return value; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,94 +0,0 @@ |
|||||
// 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>
|
|
||||
/// The Graphic Control Extension contains parameters used when
|
|
||||
/// processing a graphic rendering block.
|
|
||||
/// </summary>
|
|
||||
internal readonly struct GifGraphicsControlExtension |
|
||||
{ |
|
||||
public const int Size = 8; |
|
||||
|
|
||||
public GifGraphicsControlExtension( |
|
||||
DisposalMethod disposalMethod, |
|
||||
bool transparencyFlag, |
|
||||
ushort delayTime, |
|
||||
byte transparencyIndex) |
|
||||
{ |
|
||||
this.DisposalMethod = disposalMethod; |
|
||||
this.TransparencyFlag = transparencyFlag; |
|
||||
this.DelayTime = delayTime; |
|
||||
this.TransparencyIndex = transparencyIndex; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the disposal method which indicates the way in which the
|
|
||||
/// graphic is to be treated after being displayed.
|
|
||||
/// </summary>
|
|
||||
public DisposalMethod DisposalMethod { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// 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.)
|
|
||||
/// </summary>
|
|
||||
public bool TransparencyFlag { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the delay time.
|
|
||||
/// If not 0, this field specifies the number of hundredths (1/100) of a second to
|
|
||||
/// wait before continuing with the processing of the Data Stream.
|
|
||||
/// The clock starts ticking immediately after the graphic is rendered.
|
|
||||
/// </summary>
|
|
||||
public ushort DelayTime { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the transparency index.
|
|
||||
/// The Transparency Index is such that when encountered, the corresponding pixel
|
|
||||
/// of the display device is not modified and processing goes on to the next pixel.
|
|
||||
/// </summary>
|
|
||||
public byte TransparencyIndex { get; } |
|
||||
|
|
||||
public byte PackField() |
|
||||
{ |
|
||||
PackedField field = default; |
|
||||
|
|
||||
field.SetBits(3, 3, (int)this.DisposalMethod); // 1-3 : Reserved, 4-6 : Disposal
|
|
||||
|
|
||||
// TODO: Allow this as an option.
|
|
||||
field.SetBit(6, false); // 7 : User input - 0 = none
|
|
||||
field.SetBit(7, this.TransparencyFlag); // 8: Has transparent.
|
|
||||
|
|
||||
return field.Byte; |
|
||||
} |
|
||||
|
|
||||
public void WriteTo(Span<byte> 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; |
|
||||
} |
|
||||
|
|
||||
public static GifGraphicsControlExtension Parse(ReadOnlySpan<byte> buffer) |
|
||||
{ |
|
||||
// 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); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,22 @@ |
|||||
|
using System; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Formats.Gif |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// A base interface for GIF extensions.
|
||||
|
/// </summary>
|
||||
|
public interface IGifExtension |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Gets the label identifying the extensions.
|
||||
|
/// </summary>
|
||||
|
byte Label { get; } |
||||
|
|
||||
|
/// <summary>
|
||||
|
/// Writes the extension data to the buffer.
|
||||
|
/// </summary>
|
||||
|
/// <param name="buffer">The buffer to write the extension to.</param>
|
||||
|
/// <returns>The number of bytes written to the buffer.</returns>
|
||||
|
int WriteTo(Span<byte> buffer); |
||||
|
} |
||||
|
} |
||||
@ -1,364 +0,0 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Buffers.Binary; |
|
||||
using System.IO; |
|
||||
using System.Text; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.IO |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Equivalent of <see cref="BinaryReader"/>, but with either endianness.
|
|
||||
/// No data is buffered in the reader; the client may seek within the stream at will.
|
|
||||
/// </summary>
|
|
||||
internal class EndianBinaryReader : IDisposable |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Buffer used for temporary storage before conversion into primitives
|
|
||||
/// </summary>
|
|
||||
private readonly byte[] storageBuffer = new byte[16]; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Whether or not this reader has been disposed yet.
|
|
||||
/// </summary>
|
|
||||
private bool disposed; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The endianness used to read data
|
|
||||
/// </summary>
|
|
||||
private readonly Endianness endianness; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="EndianBinaryReader"/> class.
|
|
||||
/// Constructs a new binary reader with the given bit converter, reading
|
|
||||
/// to the given stream, using the given encoding.
|
|
||||
/// </summary>
|
|
||||
/// <param name="endianness">Endianness to use when reading data</param>
|
|
||||
/// <param name="stream">Stream to read data from</param>
|
|
||||
public EndianBinaryReader(Endianness endianness, Stream stream) |
|
||||
{ |
|
||||
Guard.NotNull(stream, nameof(stream)); |
|
||||
Guard.IsTrue(stream.CanRead, nameof(stream), "Stream isn't readable"); |
|
||||
|
|
||||
this.BaseStream = stream; |
|
||||
this.endianness = endianness; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the underlying stream of the EndianBinaryReader.
|
|
||||
/// </summary>
|
|
||||
public Stream BaseStream { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Closes the reader, including the underlying stream.
|
|
||||
/// </summary>
|
|
||||
public void Close() |
|
||||
{ |
|
||||
this.Dispose(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Seeks within the stream.
|
|
||||
/// </summary>
|
|
||||
/// <param name="offset">Offset to seek to.</param>
|
|
||||
/// <param name="origin">Origin of seek operation.</param>
|
|
||||
public void Seek(int offset, SeekOrigin origin) |
|
||||
{ |
|
||||
this.CheckDisposed(); |
|
||||
this.BaseStream.Seek(offset, origin); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Reads a single byte from the stream.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The byte read</returns>
|
|
||||
public byte ReadByte() |
|
||||
{ |
|
||||
this.ReadInternal(this.storageBuffer, 1); |
|
||||
return this.storageBuffer[0]; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Reads a single signed byte from the stream.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The byte read</returns>
|
|
||||
public sbyte ReadSByte() |
|
||||
{ |
|
||||
this.ReadInternal(this.storageBuffer, 1); |
|
||||
return unchecked((sbyte)this.storageBuffer[0]); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Reads a boolean from the stream. 1 byte is read.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The boolean read</returns>
|
|
||||
public bool ReadBoolean() |
|
||||
{ |
|
||||
this.ReadInternal(this.storageBuffer, 1); |
|
||||
|
|
||||
return this.storageBuffer[0] != 0; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Reads a 16-bit signed integer from the stream, using the bit converter
|
|
||||
/// for this reader. 2 bytes are read.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The 16-bit integer read</returns>
|
|
||||
public short ReadInt16() |
|
||||
{ |
|
||||
this.ReadInternal(this.storageBuffer, 2); |
|
||||
|
|
||||
return (this.endianness == Endianness.BigEndian) |
|
||||
? BinaryPrimitives.ReadInt16BigEndian(this.storageBuffer) |
|
||||
: BinaryPrimitives.ReadInt16LittleEndian(this.storageBuffer); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Reads a 32-bit signed integer from the stream, using the bit converter
|
|
||||
/// for this reader. 4 bytes are read.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The 32-bit integer read</returns>
|
|
||||
public int ReadInt32() |
|
||||
{ |
|
||||
this.ReadInternal(this.storageBuffer, 4); |
|
||||
|
|
||||
return (this.endianness == Endianness.BigEndian) |
|
||||
? BinaryPrimitives.ReadInt32BigEndian(this.storageBuffer) |
|
||||
: BinaryPrimitives.ReadInt32LittleEndian(this.storageBuffer); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Reads a 64-bit signed integer from the stream, using the bit converter
|
|
||||
/// for this reader. 8 bytes are read.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The 64-bit integer read</returns>
|
|
||||
public long ReadInt64() |
|
||||
{ |
|
||||
this.ReadInternal(this.storageBuffer, 8); |
|
||||
|
|
||||
return (this.endianness == Endianness.BigEndian) |
|
||||
? BinaryPrimitives.ReadInt64BigEndian(this.storageBuffer) |
|
||||
: BinaryPrimitives.ReadInt64LittleEndian(this.storageBuffer); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Reads a 16-bit unsigned integer from the stream, using the bit converter
|
|
||||
/// for this reader. 2 bytes are read.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The 16-bit unsigned integer read</returns>
|
|
||||
public ushort ReadUInt16() |
|
||||
{ |
|
||||
this.ReadInternal(this.storageBuffer, 2); |
|
||||
|
|
||||
return (this.endianness == Endianness.BigEndian) |
|
||||
? BinaryPrimitives.ReadUInt16BigEndian(this.storageBuffer) |
|
||||
: BinaryPrimitives.ReadUInt16LittleEndian(this.storageBuffer); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Reads a 32-bit unsigned integer from the stream, using the bit converter
|
|
||||
/// for this reader. 4 bytes are read.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The 32-bit unsigned integer read</returns>
|
|
||||
public uint ReadUInt32() |
|
||||
{ |
|
||||
this.ReadInternal(this.storageBuffer, 4); |
|
||||
|
|
||||
return (this.endianness == Endianness.BigEndian) |
|
||||
? BinaryPrimitives.ReadUInt32BigEndian(this.storageBuffer) |
|
||||
: BinaryPrimitives.ReadUInt32LittleEndian(this.storageBuffer); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Reads a 64-bit unsigned integer from the stream, using the bit converter
|
|
||||
/// for this reader. 8 bytes are read.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The 64-bit unsigned integer read</returns>
|
|
||||
public ulong ReadUInt64() |
|
||||
{ |
|
||||
this.ReadInternal(this.storageBuffer, 8); |
|
||||
|
|
||||
return (this.endianness == Endianness.BigEndian) |
|
||||
? BinaryPrimitives.ReadUInt64BigEndian(this.storageBuffer) |
|
||||
: BinaryPrimitives.ReadUInt64LittleEndian(this.storageBuffer); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Reads a single-precision floating-point value from the stream, using the bit converter
|
|
||||
/// for this reader. 4 bytes are read.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The floating point value read</returns>
|
|
||||
public unsafe float ReadSingle() |
|
||||
{ |
|
||||
int intValue = this.ReadInt32(); |
|
||||
|
|
||||
return *((float*)&intValue); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Reads a double-precision floating-point value from the stream, using the bit converter
|
|
||||
/// for this reader. 8 bytes are read.
|
|
||||
/// </summary>
|
|
||||
/// <returns>The floating point value read</returns>
|
|
||||
public unsafe double ReadDouble() |
|
||||
{ |
|
||||
long value = this.ReadInt64(); |
|
||||
|
|
||||
return *((double*)&value); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Reads the specified number of bytes into the given buffer, starting at
|
|
||||
/// the given index.
|
|
||||
/// </summary>
|
|
||||
/// <param name="buffer">The buffer to copy data into</param>
|
|
||||
/// <param name="index">The first index to copy data into</param>
|
|
||||
/// <param name="count">The number of bytes to read</param>
|
|
||||
/// <returns>The number of bytes actually read. This will only be less than
|
|
||||
/// the requested number of bytes if the end of the stream is reached.
|
|
||||
/// </returns>
|
|
||||
public int Read(byte[] buffer, int index, int count) |
|
||||
{ |
|
||||
this.CheckDisposed(); |
|
||||
|
|
||||
Guard.NotNull(this.storageBuffer, nameof(this.storageBuffer)); |
|
||||
Guard.MustBeGreaterThanOrEqualTo(index, 0, nameof(index)); |
|
||||
Guard.MustBeGreaterThanOrEqualTo(count, 0, nameof(count)); |
|
||||
Guard.IsFalse(count + index > buffer.Length, nameof(buffer.Length), "Not enough space in buffer for specified number of bytes starting at specified index."); |
|
||||
|
|
||||
int read = 0; |
|
||||
while (count > 0) |
|
||||
{ |
|
||||
int block = this.BaseStream.Read(buffer, index, count); |
|
||||
if (block == 0) |
|
||||
{ |
|
||||
return read; |
|
||||
} |
|
||||
|
|
||||
index += block; |
|
||||
read += block; |
|
||||
count -= block; |
|
||||
} |
|
||||
|
|
||||
return read; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Reads the specified number of bytes, returning them in a new byte array.
|
|
||||
/// If not enough bytes are available before the end of the stream, this
|
|
||||
/// method will return what is available.
|
|
||||
/// </summary>
|
|
||||
/// <param name="count">The number of bytes to read</param>
|
|
||||
/// <returns>The bytes read</returns>
|
|
||||
public byte[] ReadBytes(int count) |
|
||||
{ |
|
||||
this.CheckDisposed(); |
|
||||
Guard.MustBeGreaterThanOrEqualTo(count, 0, nameof(count)); |
|
||||
|
|
||||
byte[] ret = new byte[count]; |
|
||||
int index = 0; |
|
||||
while (index < count) |
|
||||
{ |
|
||||
int read = this.BaseStream.Read(ret, index, count - index); |
|
||||
|
|
||||
// Stream has finished half way through. That's fine, return what we've got.
|
|
||||
if (read == 0) |
|
||||
{ |
|
||||
byte[] copy = new byte[index]; |
|
||||
Buffer.BlockCopy(ret, 0, copy, 0, index); |
|
||||
return copy; |
|
||||
} |
|
||||
|
|
||||
index += read; |
|
||||
} |
|
||||
|
|
||||
return ret; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Reads the specified number of bytes, returning them in a new byte array.
|
|
||||
/// If not enough bytes are available before the end of the stream, this
|
|
||||
/// method will throw an IOException.
|
|
||||
/// </summary>
|
|
||||
/// <param name="count">The number of bytes to read</param>
|
|
||||
/// <returns>The bytes read</returns>
|
|
||||
public byte[] ReadBytesOrThrow(int count) |
|
||||
{ |
|
||||
byte[] ret = new byte[count]; |
|
||||
this.ReadInternal(ret, count); |
|
||||
return ret; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Disposes of the underlying stream.
|
|
||||
/// </summary>
|
|
||||
public void Dispose() |
|
||||
{ |
|
||||
if (!this.disposed) |
|
||||
{ |
|
||||
this.disposed = true; |
|
||||
((IDisposable)this.BaseStream).Dispose(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Checks whether or not the reader has been disposed, throwing an exception if so.
|
|
||||
/// </summary>
|
|
||||
private void CheckDisposed() |
|
||||
{ |
|
||||
if (this.disposed) |
|
||||
{ |
|
||||
throw new ObjectDisposedException(nameof(EndianBinaryReader)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Reads the given number of bytes from the stream, throwing an exception
|
|
||||
/// if they can't all be read.
|
|
||||
/// </summary>
|
|
||||
/// <param name="data">Buffer to read into</param>
|
|
||||
/// <param name="size">Number of bytes to read</param>
|
|
||||
private void ReadInternal(byte[] data, int size) |
|
||||
{ |
|
||||
this.CheckDisposed(); |
|
||||
int index = 0; |
|
||||
while (index < size) |
|
||||
{ |
|
||||
int read = this.BaseStream.Read(data, index, size - index); |
|
||||
if (read == 0) |
|
||||
{ |
|
||||
throw new EndOfStreamException($"End of stream reached with {size - index} byte{(size - index == 1 ? "s" : string.Empty)} left to read."); |
|
||||
} |
|
||||
|
|
||||
index += read; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Reads the given number of bytes from the stream if possible, returning
|
|
||||
/// the number of bytes actually read, which may be less than requested if
|
|
||||
/// (and only if) the end of the stream is reached.
|
|
||||
/// </summary>
|
|
||||
/// <param name="data">Buffer to read into</param>
|
|
||||
/// <param name="size">Number of bytes to read</param>
|
|
||||
/// <returns>Number of bytes actually read</returns>
|
|
||||
private int TryReadInternal(byte[] data, int size) |
|
||||
{ |
|
||||
this.CheckDisposed(); |
|
||||
int index = 0; |
|
||||
while (index < size) |
|
||||
{ |
|
||||
int read = this.BaseStream.Read(data, index, size - index); |
|
||||
if (read == 0) |
|
||||
{ |
|
||||
return index; |
|
||||
} |
|
||||
|
|
||||
index += read; |
|
||||
} |
|
||||
|
|
||||
return index; |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -1,303 +0,0 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System; |
|
||||
using System.Buffers.Binary; |
|
||||
using System.IO; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.IO |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Equivalent of <see cref="BinaryWriter"/>, but with either endianness
|
|
||||
/// </summary>
|
|
||||
internal class EndianBinaryWriter : IDisposable |
|
||||
{ |
|
||||
/// <summary>
|
|
||||
/// Buffer used for temporary storage during conversion from primitives
|
|
||||
/// </summary>
|
|
||||
private readonly byte[] buffer = new byte[16]; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// The endianness used to write the data
|
|
||||
/// </summary>
|
|
||||
private readonly Endianness endianness; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Whether or not this writer has been disposed yet.
|
|
||||
/// </summary>
|
|
||||
private bool disposed; |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Initializes a new instance of the <see cref="EndianBinaryWriter"/> class
|
|
||||
/// with the given bit converter, writing to the given stream, using the given encoding.
|
|
||||
/// </summary>
|
|
||||
/// <param name="endianness">Endianness to use when writing data</param>
|
|
||||
/// <param name="stream">Stream to write data to</param>
|
|
||||
public EndianBinaryWriter(Endianness endianness, Stream stream) |
|
||||
{ |
|
||||
Guard.NotNull(stream, nameof(stream)); |
|
||||
Guard.IsTrue(stream.CanWrite, nameof(stream), "Stream isn't writable"); |
|
||||
|
|
||||
this.BaseStream = stream; |
|
||||
this.endianness = endianness; |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Gets the underlying stream of the EndianBinaryWriter.
|
|
||||
/// </summary>
|
|
||||
public Stream BaseStream { get; } |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Closes the writer, including the underlying stream.
|
|
||||
/// </summary>
|
|
||||
public void Close() |
|
||||
{ |
|
||||
this.Dispose(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Flushes the underlying stream.
|
|
||||
/// </summary>
|
|
||||
public void Flush() |
|
||||
{ |
|
||||
this.CheckDisposed(); |
|
||||
this.BaseStream.Flush(); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Seeks within the stream.
|
|
||||
/// </summary>
|
|
||||
/// <param name="offset">Offset to seek to.</param>
|
|
||||
/// <param name="origin">Origin of seek operation.</param>
|
|
||||
public void Seek(int offset, SeekOrigin origin) |
|
||||
{ |
|
||||
this.CheckDisposed(); |
|
||||
this.BaseStream.Seek(offset, origin); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Writes a boolean value to the stream. 1 byte is written.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The value to write</param>
|
|
||||
public void Write(bool value) |
|
||||
{ |
|
||||
this.buffer[0] = value ? (byte)1 : (byte)0; |
|
||||
|
|
||||
this.WriteInternal(this.buffer, 1); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Writes a 16-bit signed integer to the stream, using the bit converter
|
|
||||
/// for this writer. 2 bytes are written.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The value to write</param>
|
|
||||
public void Write(short value) |
|
||||
{ |
|
||||
if (this.endianness == Endianness.BigEndian) |
|
||||
{ |
|
||||
BinaryPrimitives.WriteInt16BigEndian(this.buffer, value); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
BinaryPrimitives.WriteInt16LittleEndian(this.buffer, value); |
|
||||
} |
|
||||
|
|
||||
this.WriteInternal(this.buffer, 2); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Writes a 32-bit signed integer to the stream, using the bit converter
|
|
||||
/// for this writer. 4 bytes are written.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The value to write</param>
|
|
||||
public void Write(int value) |
|
||||
{ |
|
||||
if (this.endianness == Endianness.BigEndian) |
|
||||
{ |
|
||||
BinaryPrimitives.WriteInt32BigEndian(this.buffer, value); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
BinaryPrimitives.WriteInt32LittleEndian(this.buffer, value); |
|
||||
} |
|
||||
|
|
||||
this.WriteInternal(this.buffer, 4); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Writes a 64-bit signed integer to the stream, using the bit converter
|
|
||||
/// for this writer. 8 bytes are written.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The value to write</param>
|
|
||||
public void Write(long value) |
|
||||
{ |
|
||||
if (this.endianness == Endianness.BigEndian) |
|
||||
{ |
|
||||
BinaryPrimitives.WriteInt64BigEndian(this.buffer, value); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
BinaryPrimitives.WriteInt64LittleEndian(this.buffer, value); |
|
||||
} |
|
||||
|
|
||||
this.WriteInternal(this.buffer, 8); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Writes a 16-bit unsigned integer to the stream, using the bit converter
|
|
||||
/// for this writer. 2 bytes are written.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The value to write</param>
|
|
||||
public void Write(ushort value) |
|
||||
{ |
|
||||
if (this.endianness == Endianness.BigEndian) |
|
||||
{ |
|
||||
BinaryPrimitives.WriteUInt16BigEndian(this.buffer, value); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
BinaryPrimitives.WriteUInt16LittleEndian(this.buffer, value); |
|
||||
} |
|
||||
|
|
||||
this.WriteInternal(this.buffer, 2); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Writes a 32-bit unsigned integer to the stream, using the bit converter
|
|
||||
/// for this writer. 4 bytes are written.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The value to write</param>
|
|
||||
public void Write(uint value) |
|
||||
{ |
|
||||
if (this.endianness == Endianness.BigEndian) |
|
||||
{ |
|
||||
BinaryPrimitives.WriteUInt32BigEndian(this.buffer, value); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
BinaryPrimitives.WriteUInt32LittleEndian(this.buffer, value); |
|
||||
} |
|
||||
|
|
||||
this.WriteInternal(this.buffer, 4); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Writes a 64-bit unsigned integer to the stream, using the bit converter
|
|
||||
/// for this writer. 8 bytes are written.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The value to write</param>
|
|
||||
public void Write(ulong value) |
|
||||
{ |
|
||||
if (this.endianness == Endianness.BigEndian) |
|
||||
{ |
|
||||
BinaryPrimitives.WriteUInt64BigEndian(this.buffer, value); |
|
||||
} |
|
||||
else |
|
||||
{ |
|
||||
BinaryPrimitives.WriteUInt64LittleEndian(this.buffer, value); |
|
||||
} |
|
||||
|
|
||||
this.WriteInternal(this.buffer, 8); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Writes a single-precision floating-point value to the stream, using the bit converter
|
|
||||
/// for this writer. 4 bytes are written.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The value to write</param>
|
|
||||
public unsafe void Write(float value) |
|
||||
{ |
|
||||
this.Write(*((int*)&value)); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Writes a double-precision floating-point value to the stream, using the bit converter
|
|
||||
/// for this writer. 8 bytes are written.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The value to write</param>
|
|
||||
public unsafe void Write(double value) |
|
||||
{ |
|
||||
this.Write(*((long*)&value)); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Writes a signed byte to the stream.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The value to write</param>
|
|
||||
public void Write(byte value) |
|
||||
{ |
|
||||
this.buffer[0] = value; |
|
||||
this.WriteInternal(this.buffer, 1); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Writes an unsigned byte to the stream.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The value to write</param>
|
|
||||
public void Write(sbyte value) |
|
||||
{ |
|
||||
this.buffer[0] = unchecked((byte)value); |
|
||||
this.WriteInternal(this.buffer, 1); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Writes an array of bytes to the stream.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">The values to write</param>
|
|
||||
/// <exception cref="ArgumentNullException">value is null</exception>
|
|
||||
public void Write(byte[] value) |
|
||||
{ |
|
||||
Guard.NotNull(value, nameof(value)); |
|
||||
this.WriteInternal(value, value.Length); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Writes a portion of an array of bytes to the stream.
|
|
||||
/// </summary>
|
|
||||
/// <param name="value">An array containing the bytes to write</param>
|
|
||||
/// <param name="offset">The index of the first byte to write within the array</param>
|
|
||||
/// <param name="count">The number of bytes to write</param>
|
|
||||
/// <exception cref="ArgumentNullException">value is null</exception>
|
|
||||
public void Write(byte[] value, int offset, int count) |
|
||||
{ |
|
||||
this.CheckDisposed(); |
|
||||
this.BaseStream.Write(value, offset, count); |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Disposes of the underlying stream.
|
|
||||
/// </summary>
|
|
||||
public void Dispose() |
|
||||
{ |
|
||||
if (!this.disposed) |
|
||||
{ |
|
||||
this.Flush(); |
|
||||
this.disposed = true; |
|
||||
((IDisposable)this.BaseStream).Dispose(); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Checks whether or not the writer has been disposed, throwing an exception if so.
|
|
||||
/// </summary>
|
|
||||
private void CheckDisposed() |
|
||||
{ |
|
||||
if (this.disposed) |
|
||||
{ |
|
||||
throw new ObjectDisposedException(nameof(EndianBinaryWriter)); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Writes the specified number of bytes from the start of the given byte array,
|
|
||||
/// after checking whether or not the writer has been disposed.
|
|
||||
/// </summary>
|
|
||||
/// <param name="bytes">The array of bytes to write from</param>
|
|
||||
/// <param name="length">The number of bytes to write</param>
|
|
||||
private void WriteInternal(byte[] bytes, int length) |
|
||||
{ |
|
||||
this.CheckDisposed(); |
|
||||
this.BaseStream.Write(bytes, 0, length); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
@ -0,0 +1,21 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using SixLabors.ImageSharp.Formats.Gif; |
||||
|
|
||||
|
using Xunit; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Tests.Formats.Gif |
||||
|
{ |
||||
|
public class GifGraphicControlExtensionTests |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void TestPackedValue() |
||||
|
{ |
||||
|
Assert.Equal(0, GifGraphicControlExtension.GetPackedValue(DisposalMethod.Unspecified, false, false)); |
||||
|
Assert.Equal(11, GifGraphicControlExtension.GetPackedValue(DisposalMethod.RestoreToBackground, true, true)); |
||||
|
Assert.Equal(4, GifGraphicControlExtension.GetPackedValue(DisposalMethod.NotDispose, false, false)); |
||||
|
Assert.Equal(14, GifGraphicControlExtension.GetPackedValue(DisposalMethod.RestoreToPrevious, true, false)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using SixLabors.ImageSharp.Formats.Gif; |
||||
|
|
||||
|
using Xunit; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Tests.Formats.Gif |
||||
|
{ |
||||
|
public class GifImageDescriptorTests |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void TestPackedValue() |
||||
|
{ |
||||
|
Assert.Equal(128, GifImageDescriptor.GetPackedValue(true, false, false, 1)); // localColorTable
|
||||
|
Assert.Equal(64, GifImageDescriptor.GetPackedValue(false, true, false, 1)); // interfaceFlag
|
||||
|
Assert.Equal(32, GifImageDescriptor.GetPackedValue(false, false, true, 1)); // sortFlag
|
||||
|
Assert.Equal(224, GifImageDescriptor.GetPackedValue(true, true, true, 1)); // all
|
||||
|
Assert.Equal(7, GifImageDescriptor.GetPackedValue(false, false, false, 8)); |
||||
|
Assert.Equal(227, GifImageDescriptor.GetPackedValue(true, true, true, 4)); |
||||
|
Assert.Equal(231, GifImageDescriptor.GetPackedValue(true, true, true, 8)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
// Copyright (c) Six Labors and contributors.
|
||||
|
// Licensed under the Apache License, Version 2.0.
|
||||
|
|
||||
|
using SixLabors.ImageSharp.Formats.Gif; |
||||
|
|
||||
|
using Xunit; |
||||
|
|
||||
|
namespace SixLabors.ImageSharp.Tests.Formats.Gif |
||||
|
{ |
||||
|
public class GifLogicalScreenDescriptorTests |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void TestPackedValue() |
||||
|
{ |
||||
|
Assert.Equal(0, GifLogicalScreenDescriptor.GetPackedValue(false, 0, false, 0)); |
||||
|
Assert.Equal(128, GifLogicalScreenDescriptor.GetPackedValue(true, 0, false, 0)); // globalColorTableFlag
|
||||
|
Assert.Equal(8, GifLogicalScreenDescriptor.GetPackedValue(false, 0, true, 0)); // sortFlag
|
||||
|
Assert.Equal(48, GifLogicalScreenDescriptor.GetPackedValue(false, 3, false, 0)); |
||||
|
Assert.Equal(155, GifLogicalScreenDescriptor.GetPackedValue(true, 1, true, 3)); |
||||
|
Assert.Equal(55, GifLogicalScreenDescriptor.GetPackedValue(false, 3, false, 7)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -1,97 +0,0 @@ |
|||||
// Copyright (c) Six Labors and contributors.
|
|
||||
// Licensed under the Apache License, Version 2.0.
|
|
||||
|
|
||||
using System; |
|
||||
using System.IO; |
|
||||
using SixLabors.ImageSharp.IO; |
|
||||
using Xunit; |
|
||||
|
|
||||
namespace SixLabors.ImageSharp.Tests.IO |
|
||||
{ |
|
||||
public class EndianBinaryReaderWriterTests |
|
||||
{ |
|
||||
[Fact] |
|
||||
public void RoundtripSingles() |
|
||||
{ |
|
||||
foreach ((Endianness endianness, byte[] bytes) in new[] { |
|
||||
(Endianness.BigEndian, new byte[] { 64, 73, 15, 219 }), |
|
||||
(Endianness.LittleEndian, new byte[] { 219, 15, 73, 64 }) |
|
||||
}) |
|
||||
{ |
|
||||
var stream = new MemoryStream(); |
|
||||
|
|
||||
using (var writer = new EndianBinaryWriter(endianness, stream)) |
|
||||
{ |
|
||||
writer.Write((float)Math.PI); |
|
||||
|
|
||||
Assert.Equal(bytes, stream.ToArray()); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
[Fact] |
|
||||
public void RoundtripDoubles() |
|
||||
{ |
|
||||
foreach ((Endianness endianness, byte[] bytes) in new[] { |
|
||||
(Endianness.BigEndian, new byte[] { 64, 9, 33, 251, 84, 68, 45, 24 }), |
|
||||
(Endianness.LittleEndian, new byte[] { 24, 45, 68, 84, 251, 33, 9, 64 }) |
|
||||
}) |
|
||||
{ |
|
||||
var stream = new MemoryStream(); |
|
||||
|
|
||||
using (var writer = new EndianBinaryWriter(endianness, stream)) |
|
||||
{ |
|
||||
writer.Write(Math.PI); |
|
||||
|
|
||||
Assert.Equal(bytes, stream.ToArray()); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/// <summary>
|
|
||||
/// Ensures that the data written through a binary writer can be read back through the reader
|
|
||||
/// </summary>
|
|
||||
[Fact] |
|
||||
public void RoundtripValues() |
|
||||
{ |
|
||||
foreach (Endianness endianness in new[] { Endianness.BigEndian, Endianness.LittleEndian }) |
|
||||
{ |
|
||||
var stream = new MemoryStream(); |
|
||||
|
|
||||
var writer = new EndianBinaryWriter(endianness, stream); |
|
||||
|
|
||||
writer.Write(true); // Bool
|
|
||||
writer.Write((byte)1); // Byte
|
|
||||
writer.Write((short)1); // Int16
|
|
||||
writer.Write(1); // Int32
|
|
||||
writer.Write(1L); // Int64
|
|
||||
writer.Write(1f); // Single
|
|
||||
writer.Write(1d); // Double
|
|
||||
writer.Write((sbyte)1); // SByte
|
|
||||
writer.Write((ushort)1); // UInt16
|
|
||||
writer.Write((uint)1); // UInt32
|
|
||||
writer.Write(1UL); // ULong
|
|
||||
|
|
||||
Assert.Equal(43, stream.Length); |
|
||||
|
|
||||
stream.Position = 0; |
|
||||
|
|
||||
var reader = new EndianBinaryReader(endianness, stream); |
|
||||
|
|
||||
Assert.True(reader.ReadBoolean()); // Bool
|
|
||||
Assert.Equal((byte)1, reader.ReadByte()); // Byte
|
|
||||
Assert.Equal((short)1, reader.ReadInt16()); // Int16
|
|
||||
Assert.Equal(1, reader.ReadInt32()); // Int32
|
|
||||
Assert.Equal(1L, reader.ReadInt64()); // Int64
|
|
||||
Assert.Equal(1f, reader.ReadSingle()); // Single
|
|
||||
Assert.Equal(1d, reader.ReadDouble()); // Double
|
|
||||
Assert.Equal((sbyte)1, reader.ReadSByte()); // SByte
|
|
||||
Assert.Equal((ushort)1, reader.ReadUInt16()); // UInt16
|
|
||||
Assert.Equal((uint)1, reader.ReadUInt32()); // UInt32
|
|
||||
Assert.Equal(1UL, reader.ReadUInt64()); // ULong
|
|
||||
|
|
||||
stream.Dispose(); |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
} |
|
||||
Loading…
Reference in new issue