Browse Source

Create specialized BigEndianBinaryWriter & LittleEndianBinaryWriter classes

af/merge-core
Jason Nelson 8 years ago
parent
commit
5dacf84c5b
  1. 26
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  2. 18
      src/ImageSharp/Formats/Gif/GifEncoderCore.cs
  3. 87
      src/ImageSharp/IO/BigEndianBinaryWriter.cs
  4. 130
      src/ImageSharp/IO/EndianBinaryWriter.cs
  5. 87
      src/ImageSharp/IO/LittleEndianBinaryWriter.cs
  6. 6
      tests/ImageSharp.Tests/IO/EndianBinaryReaderWriterTests.cs

26
src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs

@ -55,9 +55,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.padding = bytesPerLine - (image.Width * (int)this.bitsPerPixel);
// Do not use IDisposable pattern here as we want to preserve the stream.
EndianBinaryWriter writer = new EndianBinaryWriter(Endianness.LittleEndian, stream);
var writer = new LittleEndianBinaryWriter(stream);
BmpInfoHeader infoHeader = new BmpInfoHeader
var infoHeader = new BmpInfoHeader
{
HeaderSize = BmpInfoHeader.BitmapInfoHeaderSize,
Height = image.Height,
@ -69,7 +69,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
ClrImportant = 0
};
BmpFileHeader fileHeader = new BmpFileHeader
var fileHeader = new BmpFileHeader
{
Type = 19778, // BM
Offset = 54,
@ -87,12 +87,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Writes the bitmap header data to the binary stream.
/// </summary>
/// <param name="writer">
/// The <see cref="EndianBinaryWriter"/> containing the stream to write to.
/// The <see cref="BigEndianBinaryWriter"/> containing the stream to write to.
/// </param>
/// <param name="fileHeader">
/// The <see cref="BmpFileHeader"/> containing the header data.
/// </param>
private static void WriteHeader(EndianBinaryWriter writer, BmpFileHeader fileHeader)
private static void WriteHeader(LittleEndianBinaryWriter writer, BmpFileHeader fileHeader)
{
writer.Write(fileHeader.Type);
writer.Write(fileHeader.FileSize);
@ -104,12 +104,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Writes the bitmap information to the binary stream.
/// </summary>
/// <param name="writer">
/// The <see cref="EndianBinaryWriter"/> containing the stream to write to.
/// The <see cref="BigEndianBinaryWriter"/> containing the stream to write to.
/// </param>
/// <param name="infoHeader">
/// The <see cref="BmpFileHeader"/> containing the detailed information about the image.
/// </param>
private void WriteInfo(EndianBinaryWriter writer, BmpInfoHeader infoHeader)
private void WriteInfo(LittleEndianBinaryWriter writer, BmpInfoHeader infoHeader)
{
writer.Write(infoHeader.HeaderSize);
writer.Write(infoHeader.Width);
@ -128,11 +128,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Writes the pixel data to the binary stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="writer">The <see cref="EndianBinaryWriter"/> containing the stream to write to.</param>
/// <param name="writer">The <see cref="BigEndianBinaryWriter"/> containing the stream to write to.</param>
/// <param name="image">
/// The <see cref="ImageFrame{TPixel}"/> containing pixel data.
/// </param>
private void WriteImage<TPixel>(EndianBinaryWriter writer, ImageFrame<TPixel> image)
private void WriteImage<TPixel>(LittleEndianBinaryWriter writer, ImageFrame<TPixel> image)
where TPixel : struct, IPixel<TPixel>
{
using (PixelAccessor<TPixel> pixels = image.Lock())
@ -159,9 +159,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Writes the 32bit color palette to the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="writer">The <see cref="EndianBinaryWriter"/> containing the stream to write to.</param>
/// <param name="writer">The <see cref="BigEndianBinaryWriter"/> containing the stream to write to.</param>
/// <param name="pixels">The <see cref="PixelAccessor{TPixel}"/> containing pixel data.</param>
private void Write32Bit<TPixel>(EndianBinaryWriter writer, PixelAccessor<TPixel> pixels)
private void Write32Bit<TPixel>(LittleEndianBinaryWriter writer, PixelAccessor<TPixel> pixels)
where TPixel : struct, IPixel<TPixel>
{
using (IManagedByteBuffer row = this.AllocateRow(pixels.Width, 4))
@ -179,9 +179,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Writes the 24bit color palette to the stream.
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="writer">The <see cref="EndianBinaryWriter"/> containing the stream to write to.</param>
/// <param name="writer">The <see cref="BigEndianBinaryWriter"/> containing the stream to write to.</param>
/// <param name="pixels">The <see cref="PixelAccessor{TPixel}"/> containing pixel data.</param>
private void Write24Bit<TPixel>(EndianBinaryWriter writer, PixelAccessor<TPixel> pixels)
private void Write24Bit<TPixel>(LittleEndianBinaryWriter writer, PixelAccessor<TPixel> pixels)
where TPixel : struct, IPixel<TPixel>
{
using (IManagedByteBuffer row = this.AllocateRow(pixels.Width, 3))

18
src/ImageSharp/Formats/Gif/GifEncoderCore.cs

@ -76,7 +76,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
Guard.NotNull(stream, nameof(stream));
// Do not use IDisposable pattern here as we want to preserve the stream.
var writer = new EndianBinaryWriter(Endianness.LittleEndian, stream);
var writer = new LittleEndianBinaryWriter(stream);
this.hasFrames = image.Frames.Count > 1;
@ -155,7 +155,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// Writes the file header signature and version to the stream.
/// </summary>
/// <param name="writer">The writer to write to the stream with.</param>
private void WriteHeader(EndianBinaryWriter writer)
private void WriteHeader(LittleEndianBinaryWriter writer)
{
writer.Write(GifConstants.MagicNumber, 0, GifConstants.MagicNumber.Length);
}
@ -167,7 +167,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <param name="image">The image to encode.</param>
/// <param name="writer">The writer to write to the stream with.</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, LittleEndianBinaryWriter writer, int transparencyIndex)
where TPixel : struct, IPixel<TPixel>
{
var descriptor = new GifLogicalScreenDescriptor
@ -202,7 +202,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <param name="writer">The writer to write to the stream with.</param>
/// <param name="repeatCount">The animated image repeat count.</param>
/// <param name="frames">The number of image frames.</param>
private void WriteApplicationExtension(EndianBinaryWriter writer, ushort repeatCount, int frames)
private void WriteApplicationExtension(LittleEndianBinaryWriter writer, ushort repeatCount, int frames)
{
// Application Extension Header
if (repeatCount != 1 && frames > 0)
@ -231,7 +231,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <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>
private void WriteComments<TPixel>(Image<TPixel> image, EndianBinaryWriter writer)
private void WriteComments<TPixel>(Image<TPixel> image, LittleEndianBinaryWriter writer)
where TPixel : struct, IPixel<TPixel>
{
if (this.ignoreMetadata)
@ -264,7 +264,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <param name="metaData">The metadata of the image or frame.</param>
/// <param name="writer">The stream to write to.</param>
/// <param name="transparencyIndex">The index of the color in the color palette to make transparent.</param>
private void WriteGraphicalControlExtension(ImageFrameMetaData metaData, EndianBinaryWriter writer, int transparencyIndex)
private void WriteGraphicalControlExtension(ImageFrameMetaData metaData, LittleEndianBinaryWriter writer, int transparencyIndex)
{
var extension = new GifGraphicsControlExtension
{
@ -299,7 +299,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <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>
private void WriteImageDescriptor<TPixel>(ImageFrame<TPixel> image, EndianBinaryWriter writer)
private void WriteImageDescriptor<TPixel>(ImageFrame<TPixel> image, LittleEndianBinaryWriter writer)
where TPixel : struct, IPixel<TPixel>
{
writer.Write(GifConstants.ImageDescriptorLabel); // 2c
@ -325,7 +325,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode.</param>
/// <param name="writer">The writer to write to the stream with.</param>
private void WriteColorTable<TPixel>(QuantizedFrame<TPixel> image, EndianBinaryWriter writer)
private void WriteColorTable<TPixel>(QuantizedFrame<TPixel> image, LittleEndianBinaryWriter writer)
where TPixel : struct, IPixel<TPixel>
{
// Grab the palette and write it to the stream.
@ -357,7 +357,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="image">The <see cref="QuantizedFrame{TPixel}"/> containing indexed pixels.</param>
/// <param name="writer">The stream to write to.</param>
private void WriteImageData<TPixel>(QuantizedFrame<TPixel> image, EndianBinaryWriter writer)
private void WriteImageData<TPixel>(QuantizedFrame<TPixel> image, LittleEndianBinaryWriter writer)
where TPixel : struct, IPixel<TPixel>
{
using (var encoder = new LzwEncoder(this.memoryManager, image.Pixels, (byte)this.bitDepth))

87
src/ImageSharp/IO/BigEndianBinaryWriter.cs

@ -0,0 +1,87 @@
// 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>
/// A BigEndian variant of <see cref="BinaryWriter"/>
/// </summary>
internal sealed class BigEndianBinaryWriter : EndianBinaryWriter
{
/// <summary>
/// Initializes a new instance of the <see cref="BigEndianBinaryWriter"/> class
/// </summary>
/// <param name="stream">Stream to write data to</param>
public BigEndianBinaryWriter(Stream stream)
: base(stream)
{
}
/// <inheritdoc />
public override Endianness Endianness => Endianness.BigEndian;
/// <inheritdoc />
public override void Write(short value)
{
BinaryPrimitives.WriteInt16BigEndian(this.buffer, value);
this.WriteInternal(this.buffer, 2);
}
/// <inheritdoc />
public override void Write(int value)
{
BinaryPrimitives.WriteInt32BigEndian(this.buffer, value);
this.WriteInternal(this.buffer, 4);
}
/// <inheritdoc />
public override void Write(long value)
{
BinaryPrimitives.WriteInt64BigEndian(this.buffer, value);
this.WriteInternal(this.buffer, 8);
}
/// <inheritdoc />
public override void Write(ushort value)
{
BinaryPrimitives.WriteUInt16BigEndian(this.buffer, value);
this.WriteInternal(this.buffer, 2);
}
/// <inheritdoc />
public override void Write(uint value)
{
BinaryPrimitives.WriteUInt32BigEndian(this.buffer, value);
this.WriteInternal(this.buffer, 4);
}
/// <inheritdoc />
public override void Write(ulong value)
{
BinaryPrimitives.WriteUInt64BigEndian(this.buffer, value);
this.WriteInternal(this.buffer, 8);
}
/// <inheritdoc />
public override unsafe void Write(float value)
{
this.Write(*((int*)&value));
}
/// <inheritdoc />
public override unsafe void Write(double value)
{
this.Write(*((long*)&value));
}
}
}

130
src/ImageSharp/IO/EndianBinaryWriter.cs

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers.Binary;
using System.IO;
namespace SixLabors.ImageSharp.IO
@ -10,17 +9,12 @@ namespace SixLabors.ImageSharp.IO
/// <summary>
/// Equivalent of <see cref="BinaryWriter"/>, but with either endianness
/// </summary>
internal class EndianBinaryWriter : IDisposable
internal abstract 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;
protected readonly byte[] buffer = new byte[16];
/// <summary>
/// Whether or not this writer has been disposed yet.
@ -29,17 +23,14 @@ namespace SixLabors.ImageSharp.IO
/// <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)
public EndianBinaryWriter(Stream stream)
{
Guard.NotNull(stream, nameof(stream));
Guard.IsTrue(stream.CanWrite, nameof(stream), "Stream isn't writable");
this.BaseStream = stream;
this.endianness = endianness;
}
/// <summary>
@ -47,6 +38,11 @@ namespace SixLabors.ImageSharp.IO
/// </summary>
public Stream BaseStream { get; }
/// <summary>
/// Gets the endianness of the BinaryWriter
/// </summary>
public abstract Endianness Endianness { get; }
/// <summary>
/// Closes the writer, including the underlying stream.
/// </summary>
@ -91,134 +87,56 @@ namespace SixLabors.ImageSharp.IO
/// 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);
}
public abstract void Write(short value);
/// <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);
}
public abstract void Write(int value);
/// <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);
}
public abstract void Write(long value);
/// <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);
}
public abstract void Write(ushort value);
/// <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);
}
public abstract void Write(uint value);
/// <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);
}
public abstract void Write(ulong value);
/// <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));
}
public abstract void Write(float 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));
}
public abstract void Write(double value);
/// <summary>
/// Writes a signed byte to the stream.
@ -284,7 +202,7 @@ namespace SixLabors.ImageSharp.IO
{
if (this.disposed)
{
throw new ObjectDisposedException(nameof(EndianBinaryWriter));
throw new ObjectDisposedException(nameof(BigEndianBinaryWriter));
}
}
@ -294,10 +212,22 @@ namespace SixLabors.ImageSharp.IO
/// </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)
protected void WriteInternal(byte[] bytes, int length)
{
this.CheckDisposed();
this.BaseStream.Write(bytes, 0, length);
}
public static EndianBinaryWriter Create(Endianness endianness, Stream stream)
{
if (endianness == Endianness.BigEndian)
{
return new BigEndianBinaryWriter(stream);
}
else
{
return new LittleEndianBinaryWriter(stream);
}
}
}
}

87
src/ImageSharp/IO/LittleEndianBinaryWriter.cs

@ -0,0 +1,87 @@
// 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>
/// A Little Endian variant of <see cref="BinaryWriter"/>
/// </summary>
internal sealed class LittleEndianBinaryWriter : EndianBinaryWriter
{
/// <summary>
/// Initializes a new instance of the <see cref="LittleEndianBinaryWriter"/> class
/// </summary>
/// <param name="stream">Stream to write data to</param>
public LittleEndianBinaryWriter(Stream stream)
: base(stream)
{
}
/// <inhertdoc />
public override Endianness Endianness => Endianness.LittleEndian;
/// <inheritdoc />
public override void Write(short value)
{
BinaryPrimitives.WriteInt16LittleEndian(this.buffer, value);
this.WriteInternal(this.buffer, 2);
}
/// <inheritdoc />
public override void Write(int value)
{
BinaryPrimitives.WriteInt32LittleEndian(this.buffer, value);
this.WriteInternal(this.buffer, 4);
}
/// <inheritdoc />
public override void Write(long value)
{
BinaryPrimitives.WriteInt64LittleEndian(this.buffer, value);
this.WriteInternal(this.buffer, 8);
}
/// <inheritdoc />
public override void Write(ushort value)
{
BinaryPrimitives.WriteUInt16LittleEndian(this.buffer, value);
this.WriteInternal(this.buffer, 2);
}
/// <inheritdoc />
public override void Write(uint value)
{
BinaryPrimitives.WriteUInt32LittleEndian(this.buffer, value);
this.WriteInternal(this.buffer, 4);
}
/// <inheritdoc />
public override void Write(ulong value)
{
BinaryPrimitives.WriteUInt64LittleEndian(this.buffer, value);
this.WriteInternal(this.buffer, 8);
}
/// <inheritdoc />
public override unsafe void Write(float value)
{
this.Write(*((int*)&value));
}
/// <inheritdoc />
public override unsafe void Write(double value)
{
this.Write(*((long*)&value));
}
}
}

6
tests/ImageSharp.Tests/IO/EndianBinaryReaderWriterTests.cs

@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Tests.IO
{
var stream = new MemoryStream();
using (var writer = new EndianBinaryWriter(endianness, stream))
using (var writer = EndianBinaryWriter.Create(endianness, stream))
{
writer.Write((float)Math.PI);
@ -39,7 +39,7 @@ namespace SixLabors.ImageSharp.Tests.IO
{
var stream = new MemoryStream();
using (var writer = new EndianBinaryWriter(endianness, stream))
using (var writer = EndianBinaryWriter.Create(endianness, stream))
{
writer.Write(Math.PI);
@ -58,7 +58,7 @@ namespace SixLabors.ImageSharp.Tests.IO
{
var stream = new MemoryStream();
var writer = new EndianBinaryWriter(endianness, stream);
var writer = EndianBinaryWriter.Create(endianness, stream);
writer.Write(true); // Bool
writer.Write((byte)1); // Byte

Loading…
Cancel
Save