diff --git a/src/ImageSharp/Formats/Bmp/BmpCompression.cs b/src/ImageSharp/Formats/Bmp/BmpCompression.cs
index 22b12346f..ef063f010 100644
--- a/src/ImageSharp/Formats/Bmp/BmpCompression.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpCompression.cs
@@ -7,7 +7,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Defines how the compression type of the image data
/// in the bitmap file.
///
- internal enum BmpCompression
+ internal enum BmpCompression : int
{
///
/// Each image row has a multiple of four elements. If the
diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
index dfbd44c04..26bd97b81 100644
--- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
@@ -34,29 +34,29 @@ namespace SixLabors.ImageSharp.Formats.Bmp
private const int Rgb16BMask = 0x1F;
///
- /// RLE8 flag value that indicates following byte has special meaning
+ /// RLE8 flag value that indicates following byte has special meaning.
///
private const int RleCommand = 0x00;
///
- /// RLE8 flag value marking end of a scan line
+ /// RLE8 flag value marking end of a scan line.
///
private const int RleEndOfLine = 0x00;
///
- /// RLE8 flag value marking end of bitmap data
+ /// RLE8 flag value marking end of bitmap data.
///
private const int RleEndOfBitmap = 0x01;
///
- /// RLE8 flag value marking the start of [x,y] offset instruction
+ /// RLE8 flag value marking the start of [x,y] offset instruction.
///
private const int RleDelta = 0x02;
///
/// The stream to decode from.
///
- private Stream currentStream;
+ private Stream stream;
///
/// The file header containing general information.
@@ -163,18 +163,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int Invert(int y, int height, bool inverted)
{
- int row;
-
- if (!inverted)
- {
- row = height - y - 1;
- }
- else
- {
- row = y;
- }
-
- return row;
+ return (!inverted) ? height - y - 1 : y;
}
///
@@ -261,7 +250,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
while (count < buffer.Length)
{
- if (this.currentStream.Read(cmd, 0, cmd.Length) != 2)
+ if (this.stream.Read(cmd, 0, cmd.Length) != 2)
{
throw new Exception("Failed to read 2 bytes from stream");
}
@@ -283,27 +272,29 @@ namespace SixLabors.ImageSharp.Formats.Bmp
break;
case RleDelta:
- int dx = this.currentStream.ReadByte();
- int dy = this.currentStream.ReadByte();
+ int dx = this.stream.ReadByte();
+ int dy = this.stream.ReadByte();
count += (w * dy) + dx;
break;
default:
- // If the second byte > 2, signals 'absolute mode'
+ // If the second byte > 2, we are in 'absolute mode'
// Take this number of bytes from the stream as uncompressed data
int length = cmd[1];
- int copyLength = length;
+
+ byte[] run = new byte[length];
+
+ this.stream.Read(run, 0, run.Length);
+
+ run.AsSpan().CopyTo(buffer.Slice(count));
+
+ count += run.Length;
// Absolute mode data is aligned to two-byte word-boundary
- length += length & 1;
+ int padding = length & 1;
- byte[] run = new byte[length];
- this.currentStream.Read(run, 0, run.Length);
- for (int i = 0; i < copyLength; i++)
- {
- buffer[count++] = run[i];
- }
+ this.stream.Skip(padding);
break;
}
@@ -348,7 +339,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
using (IManagedByteBuffer row = this.memoryManager.AllocateCleanManagedByteBuffer(arrayWidth + padding))
{
- var color = default(TPixel);
+ TPixel color = default;
var rgba = new Rgba32(0, 0, 0, 255);
Span rowSpan = row.Span;
@@ -356,7 +347,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
for (int y = 0; y < height; y++)
{
int newY = Invert(y, height, inverted);
- this.currentStream.Read(row.Array, 0, row.Length());
+ this.stream.Read(row.Array, 0, row.Length());
int offset = 0;
Span pixelRow = pixels.GetRowSpan(newY);
@@ -402,7 +393,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{
for (int y = 0; y < height; y++)
{
- this.currentStream.Read(buffer.Array, 0, stride);
+ this.stream.Read(buffer.Array, 0, stride);
int newY = Invert(y, height, inverted);
Span pixelRow = pixels.GetRowSpan(newY);
@@ -440,7 +431,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{
for (int y = 0; y < height; y++)
{
- this.currentStream.Read(row);
+ this.stream.Read(row);
int newY = Invert(y, height, inverted);
Span pixelSpan = pixels.GetRowSpan(newY);
PixelOperations.Instance.PackFromBgr24Bytes(row.Span, pixelSpan, width);
@@ -465,7 +456,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{
for (int y = 0; y < height; y++)
{
- this.currentStream.Read(row);
+ this.stream.Read(row);
int newY = Invert(y, height, inverted);
Span pixelSpan = pixels.GetRowSpan(newY);
PixelOperations.Instance.PackFromBgra32Bytes(row.Span, pixelSpan, width);
@@ -478,98 +469,44 @@ namespace SixLabors.ImageSharp.Formats.Bmp
///
private void ReadInfoHeader()
{
- byte[] data = new byte[BmpInfoHeader.MaxHeaderSize];
+ byte[] buffer = new byte[BmpInfoHeader.MaxHeaderSize];
// read header size
- this.currentStream.Read(data, 0, BmpInfoHeader.HeaderSizeSize);
- int headerSize = BitConverter.ToInt32(data, 0);
- if (headerSize < BmpInfoHeader.BitmapCoreHeaderSize)
+ this.stream.Read(buffer, 0, BmpInfoHeader.HeaderSizeSize);
+
+ int headerSize = BitConverter.ToInt32(buffer, 0);
+ if (headerSize < BmpInfoHeader.CoreSize)
{
- throw new NotSupportedException($"This kind of bitmap files (header size $headerSize) is not supported.");
+ throw new NotSupportedException($"ImageSharp does not support this BMP file. HeaderSize: {headerSize}.");
}
- int skipAmmount = 0;
+ int skipAmount = 0;
if (headerSize > BmpInfoHeader.MaxHeaderSize)
{
- skipAmmount = headerSize - BmpInfoHeader.MaxHeaderSize;
+ skipAmount = headerSize - BmpInfoHeader.MaxHeaderSize;
headerSize = BmpInfoHeader.MaxHeaderSize;
}
// read the rest of the header
- this.currentStream.Read(data, BmpInfoHeader.HeaderSizeSize, headerSize - BmpInfoHeader.HeaderSizeSize);
+ this.stream.Read(buffer, BmpInfoHeader.HeaderSizeSize, headerSize - BmpInfoHeader.HeaderSizeSize);
- switch (headerSize)
+ if (headerSize == BmpInfoHeader.CoreSize)
{
- case BmpInfoHeader.BitmapCoreHeaderSize:
- this.infoHeader = this.ParseBitmapCoreHeader(data);
- break;
- case BmpInfoHeader.BitmapInfoHeaderSize:
- this.infoHeader = this.ParseBitmapInfoHeader(data);
- break;
- default:
- if (headerSize > BmpInfoHeader.BitmapInfoHeaderSize)
- {
- this.infoHeader = this.ParseBitmapInfoHeader(data);
- break;
- }
- else
- {
- throw new NotSupportedException($"This kind of bitmap files (header size $headerSize) is not supported.");
- }
+ // 12 bytes
+ this.infoHeader = BmpInfoHeader.ParseCore(buffer);
}
-
- // skip the remaining header because we can't read those parts
- this.currentStream.Skip(skipAmmount);
- }
-
- ///
- /// Parses the from the stream, assuming it uses the BITMAPCOREHEADER format.
- ///
- /// Header bytes read from the stream
- /// Parsed header
- ///
- private BmpInfoHeader ParseBitmapCoreHeader(byte[] data)
- {
- return new BmpInfoHeader
+ else if (headerSize >= BmpInfoHeader.Size)
{
- HeaderSize = BitConverter.ToInt32(data, 0),
- Width = BitConverter.ToUInt16(data, 4),
- Height = BitConverter.ToUInt16(data, 6),
- Planes = BitConverter.ToInt16(data, 8),
- BitsPerPixel = BitConverter.ToInt16(data, 10),
-
- // the rest is not present in the core header
- ImageSize = 0,
- XPelsPerMeter = 0,
- YPelsPerMeter = 0,
- ClrUsed = 0,
- ClrImportant = 0,
- Compression = BmpCompression.RGB
- };
- }
-
- ///
- /// Parses the from the stream, assuming it uses the BITMAPINFOHEADER format.
- ///
- /// Header bytes read from the stream
- /// Parsed header
- ///
- private BmpInfoHeader ParseBitmapInfoHeader(byte[] data)
- {
- return new BmpInfoHeader
+ // >= 40 bytes
+ this.infoHeader = BmpInfoHeader.Parse(buffer.AsSpan(0, 40));
+ }
+ else
{
- HeaderSize = BitConverter.ToInt32(data, 0),
- Width = BitConverter.ToInt32(data, 4),
- Height = BitConverter.ToInt32(data, 8),
- Planes = BitConverter.ToInt16(data, 12),
- BitsPerPixel = BitConverter.ToInt16(data, 14),
- ImageSize = BitConverter.ToInt32(data, 20),
- XPelsPerMeter = BitConverter.ToInt32(data, 24),
- YPelsPerMeter = BitConverter.ToInt32(data, 28),
- ClrUsed = BitConverter.ToInt32(data, 32),
- ClrImportant = BitConverter.ToInt32(data, 36),
- Compression = (BmpCompression)BitConverter.ToInt32(data, 16)
- };
+ throw new NotSupportedException($"ImageSharp does not support this BMP file. HeaderSize: {headerSize}.");
+ }
+
+ // skip the remaining header because we can't read those parts
+ this.stream.Skip(skipAmount);
}
///
@@ -577,15 +514,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
///
private void ReadFileHeader()
{
- byte[] data = new byte[BmpFileHeader.Size];
+ byte[] buffer = new byte[BmpFileHeader.Size];
- this.currentStream.Read(data, 0, BmpFileHeader.Size);
+ this.stream.Read(buffer, 0, BmpFileHeader.Size);
- this.fileHeader = new BmpFileHeader(
- type: BitConverter.ToInt16(data, 0),
- fileSize: BitConverter.ToInt32(data, 2),
- reserved: BitConverter.ToInt32(data, 6),
- offset: BitConverter.ToInt32(data, 10));
+ this.fileHeader = BmpFileHeader.Parse(buffer);
}
///
@@ -593,66 +526,59 @@ namespace SixLabors.ImageSharp.Formats.Bmp
///
private void ReadImageHeaders(Stream stream, out bool inverted, out byte[] palette)
{
- this.currentStream = stream;
-
- try
+ this.stream = stream;
+
+ this.ReadFileHeader();
+ this.ReadInfoHeader();
+
+ // see http://www.drdobbs.com/architecture-and-design/the-bmp-file-format-part-1/184409517
+ // If the height is negative, then this is a Windows bitmap whose origin
+ // is the upper-left corner and not the lower-left. The inverted flag
+ // indicates a lower-left origin.Our code will be outputting an
+ // upper-left origin pixel array.
+ inverted = false;
+ if (this.infoHeader.Height < 0)
{
- this.ReadFileHeader();
- this.ReadInfoHeader();
-
- // see http://www.drdobbs.com/architecture-and-design/the-bmp-file-format-part-1/184409517
- // If the height is negative, then this is a Windows bitmap whose origin
- // is the upper-left corner and not the lower-left.The inverted flag
- // indicates a lower-left origin.Our code will be outputting an
- // upper-left origin pixel array.
- inverted = false;
- if (this.infoHeader.Height < 0)
- {
- inverted = true;
- this.infoHeader.Height = -this.infoHeader.Height;
- }
+ inverted = true;
+ this.infoHeader.Height = -this.infoHeader.Height;
+ }
- int colorMapSize = -1;
+ int colorMapSize = -1;
- if (this.infoHeader.ClrUsed == 0)
- {
- if (this.infoHeader.BitsPerPixel == 1 ||
- this.infoHeader.BitsPerPixel == 4 ||
- this.infoHeader.BitsPerPixel == 8)
- {
- colorMapSize = (int)Math.Pow(2, this.infoHeader.BitsPerPixel) * 4;
- }
- }
- else
+ if (this.infoHeader.ClrUsed == 0)
+ {
+ if (this.infoHeader.BitsPerPixel == 1 ||
+ this.infoHeader.BitsPerPixel == 4 ||
+ this.infoHeader.BitsPerPixel == 8)
{
- colorMapSize = this.infoHeader.ClrUsed * 4;
+ colorMapSize = (int)Math.Pow(2, this.infoHeader.BitsPerPixel) * 4;
}
+ }
+ else
+ {
+ colorMapSize = this.infoHeader.ClrUsed * 4;
+ }
- palette = null;
+ palette = null;
- if (colorMapSize > 0)
+ if (colorMapSize > 0)
+ {
+ // 256 * 4
+ if (colorMapSize > 1024)
{
- // 256 * 4
- if (colorMapSize > 1024)
- {
- throw new ImageFormatException($"Invalid bmp colormap size '{colorMapSize}'");
- }
-
- palette = new byte[colorMapSize];
-
- this.currentStream.Read(palette, 0, colorMapSize);
+ throw new ImageFormatException($"Invalid bmp colormap size '{colorMapSize}'");
}
- if (this.infoHeader.Width > int.MaxValue || this.infoHeader.Height > int.MaxValue)
- {
- throw new ArgumentOutOfRangeException(
- $"The input bitmap '{this.infoHeader.Width}x{this.infoHeader.Height}' is "
- + $"bigger then the max allowed size '{int.MaxValue}x{int.MaxValue}'");
- }
+ palette = new byte[colorMapSize];
+
+ this.stream.Read(palette, 0, colorMapSize);
}
- catch (IndexOutOfRangeException e)
+
+ if (this.infoHeader.Width > int.MaxValue || this.infoHeader.Height > int.MaxValue)
{
- throw new ImageFormatException("Bitmap does not have a valid format.", e);
+ throw new ArgumentOutOfRangeException(
+ $"The input bmp '{this.infoHeader.Width}x{this.infoHeader.Height}' is "
+ + $"bigger then the max allowed size '{int.MaxValue}x{int.MaxValue}'");
}
}
}
diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
index be7c1d2e5..2b0c90733 100644
--- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
@@ -3,7 +3,6 @@
using System;
using System.IO;
-using SixLabors.ImageSharp.IO;
using SixLabors.ImageSharp.Memory;
using SixLabors.ImageSharp.PixelFormats;
@@ -19,9 +18,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
///
private int padding;
- ///
- /// Gets or sets the number of bits per pixel.
- ///
private readonly BmpBitsPerPixel bitsPerPixel;
private readonly MemoryManager memoryManager;
@@ -54,20 +50,15 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int bytesPerLine = 4 * (((image.Width * bpp) + 31) / 32);
this.padding = bytesPerLine - (image.Width * (int)this.bitsPerPixel);
- // Do not use IDisposable pattern here as we want to preserve the stream.
- var writer = new EndianBinaryWriter(Endianness.LittleEndian, stream);
-
- var infoHeader = new BmpInfoHeader
- {
- HeaderSize = BmpInfoHeader.BitmapInfoHeaderSize,
- Height = image.Height,
- Width = image.Width,
- BitsPerPixel = bpp,
- Planes = 1,
- ImageSize = image.Height * bytesPerLine,
- ClrUsed = 0,
- ClrImportant = 0
- };
+ var infoHeader = new BmpInfoHeader(
+ headerSize: BmpInfoHeader.Size,
+ height: image.Height,
+ width: image.Width,
+ bitsPerPixel: bpp,
+ planes: 1,
+ imageSize: image.Height * bytesPerLine,
+ clrUsed: 0,
+ clrImportant: 0);
var fileHeader = new BmpFileHeader(
type: 19778, // BM
@@ -75,63 +66,30 @@ namespace SixLabors.ImageSharp.Formats.Bmp
reserved: 0,
fileSize: 54 + infoHeader.ImageSize);
- WriteHeader(writer, fileHeader);
- this.WriteInfo(writer, infoHeader);
- this.WriteImage(writer, image.Frames.RootFrame);
+ byte[] buffer = new byte[40]; // TODO: stackalloc
- writer.Flush();
- }
+ fileHeader.WriteTo(buffer);
- ///
- /// Writes the bitmap header data to the binary stream.
- ///
- ///
- /// The containing the stream to write to.
- ///
- ///
- /// The containing the header data.
- ///
- private static void WriteHeader(EndianBinaryWriter writer, in BmpFileHeader fileHeader)
- {
- writer.Write(fileHeader.Type);
- writer.Write(fileHeader.FileSize);
- writer.Write(fileHeader.Reserved);
- writer.Write(fileHeader.Offset);
- }
+ stream.Write(buffer, 0, BmpFileHeader.Size);
- ///
- /// Writes the bitmap information to the binary stream.
- ///
- ///
- /// The containing the stream to write to.
- ///
- ///
- /// The containing the detailed information about the image.
- ///
- private void WriteInfo(EndianBinaryWriter writer, BmpInfoHeader infoHeader)
- {
- writer.Write(infoHeader.HeaderSize);
- writer.Write(infoHeader.Width);
- writer.Write(infoHeader.Height);
- writer.Write(infoHeader.Planes);
- writer.Write(infoHeader.BitsPerPixel);
- writer.Write((int)infoHeader.Compression);
- writer.Write(infoHeader.ImageSize);
- writer.Write(infoHeader.XPelsPerMeter);
- writer.Write(infoHeader.YPelsPerMeter);
- writer.Write(infoHeader.ClrUsed);
- writer.Write(infoHeader.ClrImportant);
+ infoHeader.WriteTo(buffer);
+
+ stream.Write(buffer, 0, 40);
+
+ this.WriteImage(stream, image.Frames.RootFrame);
+
+ stream.Flush();
}
///
/// Writes the pixel data to the binary stream.
///
/// The pixel format.
- /// The containing the stream to write to.
+ /// The to write to.
///
/// The containing pixel data.
///
- private void WriteImage(EndianBinaryWriter writer, ImageFrame image)
+ private void WriteImage(Stream stream, ImageFrame image)
where TPixel : struct, IPixel
{
using (PixelAccessor pixels = image.Lock())
@@ -139,11 +97,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
switch (this.bitsPerPixel)
{
case BmpBitsPerPixel.Pixel32:
- this.Write32Bit(writer, pixels);
+ this.Write32Bit(stream, pixels);
break;
case BmpBitsPerPixel.Pixel24:
- this.Write24Bit(writer, pixels);
+ this.Write24Bit(stream, pixels);
break;
}
}
@@ -158,9 +116,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Writes the 32bit color palette to the stream.
///
/// The pixel format.
- /// The containing the stream to write to.
+ /// The to write to.
/// The containing pixel data.
- private void Write32Bit(EndianBinaryWriter writer, PixelAccessor pixels)
+ private void Write32Bit(Stream stream, PixelAccessor pixels)
where TPixel : struct, IPixel
{
using (IManagedByteBuffer row = this.AllocateRow(pixels.Width, 4))
@@ -169,7 +127,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{
Span pixelSpan = pixels.GetRowSpan(y);
PixelOperations.Instance.ToBgra32Bytes(pixelSpan, row.Span, pixelSpan.Length);
- writer.Write(row.Array, 0, row.Length());
+ stream.Write(row.Array, 0, row.Length());
}
}
}
@@ -178,9 +136,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Writes the 24bit color palette to the stream.
///
/// The pixel format.
- /// The containing the stream to write to.
+ /// The to write to.
/// The containing pixel data.
- private void Write24Bit(EndianBinaryWriter writer, PixelAccessor pixels)
+ private void Write24Bit(Stream stream, PixelAccessor pixels)
where TPixel : struct, IPixel
{
using (IManagedByteBuffer row = this.AllocateRow(pixels.Width, 3))
@@ -189,7 +147,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{
Span pixelSpan = pixels.GetRowSpan(y);
PixelOperations.Instance.ToBgr24Bytes(pixelSpan, row.Span, pixelSpan.Length);
- writer.Write(row.Array, 0, row.Length());
+ stream.Write(row.Array, 0, row.Length());
}
}
}
diff --git a/src/ImageSharp/Formats/Bmp/BmpFileHeader.cs b/src/ImageSharp/Formats/Bmp/BmpFileHeader.cs
index ed17164a2..e39a2af0e 100644
--- a/src/ImageSharp/Formats/Bmp/BmpFileHeader.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpFileHeader.cs
@@ -1,6 +1,10 @@
// 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.Bmp
{
///
@@ -13,6 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// All of the other integer values are stored in little-endian format
/// (i.e. least-significant byte first).
///
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
internal readonly struct BmpFileHeader
{
///
@@ -44,12 +49,24 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Gets any reserved data; actual value depends on the application
/// that creates the image.
///
- public int Reserved { get; }
+ public int Reserved { get; }
///
/// Gets the offset, i.e. starting address, of the byte where
/// the bitmap data can be found.
///
public int Offset { get; }
+
+ public static BmpFileHeader Parse(Span data)
+ {
+ return MemoryMarshal.Cast(data)[0];
+ }
+
+ public unsafe void WriteTo(Span buffer)
+ {
+ ref BmpFileHeader dest = ref Unsafe.As(ref MemoryMarshal.GetReference(buffer));
+
+ dest = this;
+ }
}
}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs b/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs
index b24404cac..a088a9b13 100644
--- a/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs
+++ b/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs
@@ -1,5 +1,10 @@
// Copyright (c) Six Labors and contributors.
// 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.Bmp
{
///
@@ -8,28 +13,55 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// the screen.
///
///
- internal sealed class BmpInfoHeader
+ [StructLayout(LayoutKind.Sequential, Pack = 1)]
+ internal struct BmpInfoHeader
{
///
/// Defines the size of the BITMAPINFOHEADER data structure in the bitmap file.
///
- public const int BitmapInfoHeaderSize = 40;
+ public const int Size = 40;
///
/// Defines the size of the BITMAPCOREHEADER data structure in the bitmap file.
///
- public const int BitmapCoreHeaderSize = 12;
+ public const int CoreSize = 12;
///
/// Defines the size of the biggest supported header data structure in the bitmap file.
///
- public const int MaxHeaderSize = BitmapInfoHeaderSize;
+ public const int MaxHeaderSize = Size;
///
/// Defines the size of the field.
///
public const int HeaderSizeSize = 4;
+ public BmpInfoHeader(
+ int headerSize,
+ int width,
+ int height,
+ short planes,
+ short bitsPerPixel,
+ BmpCompression compression = default,
+ int imageSize = 0,
+ int xPelsPerMeter = 0,
+ int yPelsPerMeter = 0,
+ int clrUsed = 0,
+ int clrImportant = 0)
+ {
+ this.HeaderSize = headerSize;
+ this.Width = width;
+ this.Height = height;
+ this.Planes = planes;
+ this.BitsPerPixel = bitsPerPixel;
+ this.Compression = compression;
+ this.ImageSize = imageSize;
+ this.XPelsPerMeter = xPelsPerMeter;
+ this.YPelsPerMeter = yPelsPerMeter;
+ this.ClrUsed = clrUsed;
+ this.ClrImportant = clrImportant;
+ }
+
///
/// Gets or sets the size of this header
///
@@ -91,5 +123,39 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// or 0 when every color is important{ get; set; } generally ignored.
///
public int ClrImportant { get; set; }
+
+ ///
+ /// Parses the full BITMAPINFOHEADER header (40 bytes).
+ ///
+ /// The data to parse.
+ /// Parsed header
+ ///
+ public static BmpInfoHeader Parse(ReadOnlySpan data)
+ {
+ return MemoryMarshal.Cast(data)[0];
+ }
+
+ ///
+ /// Parses the BITMAPCOREHEADER consisting of the headerSize, width, height, planes, and bitsPerPixel fields (12 bytes).
+ ///
+ /// The data to parse,
+ /// Parsed header
+ ///
+ public static BmpInfoHeader ParseCore(ReadOnlySpan data)
+ {
+ return new BmpInfoHeader(
+ headerSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)),
+ width: BinaryPrimitives.ReadUInt16LittleEndian(data.Slice(4, 2)),
+ height: BinaryPrimitives.ReadUInt16LittleEndian(data.Slice(6, 2)),
+ planes: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(8, 2)),
+ bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(10, 2)));
+ }
+
+ public unsafe void WriteTo(Span buffer)
+ {
+ ref BmpInfoHeader dest = ref Unsafe.As(ref MemoryMarshal.GetReference(buffer));
+
+ dest = this;
+ }
}
-}
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs b/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs
index 920c9ce02..c44ca73f2 100644
--- a/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs
+++ b/src/ImageSharp/Formats/Bmp/IBmpDecoderOptions.cs
@@ -1,11 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using System;
-using System.Collections.Generic;
-using System.IO;
-using SixLabors.ImageSharp.PixelFormats;
-
namespace SixLabors.ImageSharp.Formats.Bmp
{
///
@@ -15,4 +10,4 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{
// added this for consistancy so we can add stuff as required, no options currently availible
}
-}
+}
\ No newline at end of file
diff --git a/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs b/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs
index c4e219889..0bfd6980b 100644
--- a/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs
+++ b/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs
@@ -1,11 +1,6 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using System;
-using System.Collections.Generic;
-using System.IO;
-using SixLabors.ImageSharp.PixelFormats;
-
namespace SixLabors.ImageSharp.Formats.Bmp
{
///
diff --git a/src/ImageSharp/Formats/Bmp/ImageExtensions.cs b/src/ImageSharp/Formats/Bmp/ImageExtensions.cs
index 35e168e27..57e4615ba 100644
--- a/src/ImageSharp/Formats/Bmp/ImageExtensions.cs
+++ b/src/ImageSharp/Formats/Bmp/ImageExtensions.cs
@@ -1,10 +1,8 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
-using System;
using System.IO;
using SixLabors.ImageSharp.Advanced;
-using SixLabors.ImageSharp.Formats;
using SixLabors.ImageSharp.Formats.Bmp;
using SixLabors.ImageSharp.PixelFormats;
diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpFileHeaderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpFileHeaderTests.cs
new file mode 100644
index 000000000..8ad227cfd
--- /dev/null
+++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpFileHeaderTests.cs
@@ -0,0 +1,21 @@
+using System;
+using SixLabors.ImageSharp.Formats.Bmp;
+using Xunit;
+
+namespace SixLabors.ImageSharp.Tests.Formats.Bmp
+{
+ public class BmpFileHeaderTests
+ {
+ [Fact]
+ public void TestWrite()
+ {
+ var header = new BmpFileHeader(1, 2, 3, 4);
+
+ byte[] buffer = new byte[14];
+
+ header.WriteTo(buffer);
+
+ Assert.Equal("AQACAAAAAwAAAAQAAAA=", Convert.ToBase64String(buffer));
+ }
+ }
+}
\ No newline at end of file