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