diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 817d00f7e..108006ade 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -2,6 +2,7 @@ // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // + namespace ImageSharp.Formats { using System; @@ -135,11 +136,6 @@ namespace ImageSharp.Formats switch (this.infoHeader.Compression) { case BmpCompression.RGB: - if (this.infoHeader.HeaderSize != 40) - { - throw new ImageFormatException($"Header Size value '{this.infoHeader.HeaderSize}' is not valid."); - } - if (this.infoHeader.BitsPerPixel == 32) { this.ReadRgb32(pixels, this.infoHeader.Width, this.infoHeader.Height, inverted); @@ -374,11 +370,69 @@ namespace ImageSharp.Formats /// private void ReadInfoHeader() { - byte[] data = new byte[BmpInfoHeader.Size]; + byte[] data = new byte[BmpInfoHeader.MaxHeaderSize]; + + // read header size + this.currentStream.Read(data, 0, BmpInfoHeader.HeaderSizeSize); + int headerSize = BitConverter.ToInt32(data, 0); + if (headerSize < BmpInfoHeader.HeaderSizeSize || headerSize > BmpInfoHeader.MaxHeaderSize) + { + throw new NotSupportedException($"This kind of bitmap files (header size $headerSize) is not supported."); + } + + // read the rest of the header + this.currentStream.Read(data, BmpInfoHeader.HeaderSizeSize, headerSize - BmpInfoHeader.HeaderSizeSize); + + switch (headerSize) + { + case BmpInfoHeader.BitmapCoreHeaderSize: + this.infoHeader = this.ParseBitmapCoreHeader(data); + break; + + case BmpInfoHeader.BitmapInfoHeaderSize: + this.infoHeader = this.ParseBitmapInfoHeader(data); + break; + + default: + throw new NotSupportedException($"This kind of bitmap files (header size $headerSize) is not supported."); + } + } - this.currentStream.Read(data, 0, BmpInfoHeader.Size); + /// + /// 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 + { + 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 + }; + } - this.infoHeader = new BmpInfoHeader + /// + /// 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 { HeaderSize = BitConverter.ToInt32(data, 0), Width = BitConverter.ToInt32(data, 4), diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index e41c29501..30961646f 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -58,7 +58,7 @@ namespace ImageSharp.Formats BmpInfoHeader infoHeader = new BmpInfoHeader { - HeaderSize = BmpInfoHeader.Size, + HeaderSize = BmpInfoHeader.BitmapInfoHeaderSize, Height = image.Height, Width = image.Width, BitsPerPixel = bpp, diff --git a/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs b/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs index dc6a489d3..c920bfbbe 100644 --- a/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs +++ b/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs @@ -13,12 +13,27 @@ namespace ImageSharp.Formats internal sealed class BmpInfoHeader { /// - /// Defines of the data structure in the bitmap file. + /// Defines the size of the BITMAPINFOHEADER data structure in the bitmap file. /// - public const int Size = 40; + public const int BitmapInfoHeaderSize = 40; /// - /// Gets or sets the size of this header (40 bytes) + /// Defines the size of the BITMAPCOREHEADER data structure in the bitmap file. + /// + public const int BitmapCoreHeaderSize = 12; + + /// + /// Defines the size of the biggest supported header data structure in the bitmap file. + /// + public const int MaxHeaderSize = BitmapInfoHeaderSize; + + /// + /// Defines the size of the field. + /// + public const int HeaderSizeSize = 4; + + /// + /// Gets or sets the size of this header /// public int HeaderSize { get; set; } diff --git a/tests/ImageSharp.Tests/FileTestBase.cs b/tests/ImageSharp.Tests/FileTestBase.cs index 08ed69f3e..f9909de4a 100644 --- a/tests/ImageSharp.Tests/FileTestBase.cs +++ b/tests/ImageSharp.Tests/FileTestBase.cs @@ -77,6 +77,7 @@ namespace ImageSharp.Tests // TestFile.Create(TestImages.Jpeg.Progressive.Bad.BadEOF), // Perf: Enable for local testing only TestFile.Create(TestImages.Bmp.Car), // TestFile.Create(TestImages.Bmp.NegHeight), // Perf: Enable for local testing only + // TestFile.Create(TestImages.Bmp.CoreHeader), // Perf: Enable for local testing only TestFile.Create(TestImages.Png.Splash), // TestFile.Create(TestImages.Png.Cross), // Perf: Enable for local testing only // TestFile.Create(TestImages.Png.Bad.ChunkLength1), // Perf: Enable for local testing only diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index 84edca001..0537ffda0 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -1,17 +1,29 @@ -// +// // Copyright (c) James Jackson-South and contributors. // Licensed under the Apache License, Version 2.0. // -// ReSharper disable InconsistentNaming +using ImageSharp.Formats; + namespace ImageSharp.Tests { - using System.IO; + using ImageSharp.PixelFormats; using Xunit; - public class BmpDecoderTests + public class BmpDecoderTests : FileTestBase { + [Theory] + [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Rgb24)] + public void OpenAllBmpFiles_SaveBmp(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage()) + { + provider.Utility.SaveTestOutputFile(image, "bmp"); + } + } + [Theory] [InlineData(TestImages.Bmp.Car, 24)] [InlineData(TestImages.Bmp.F, 24)] @@ -26,4 +38,4 @@ namespace ImageSharp.Tests } } } -} +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 644e4d3eb..c80fde6d0 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -120,7 +120,8 @@ namespace ImageSharp.Tests public const string F = "Bmp/F.bmp"; public const string Bpp8 = "Bmp/bpp8.bmp"; public const string NegHeight = "Bmp/neg_height.bmp"; - public static readonly string[] All = { Car, F, NegHeight, Bpp8 }; + public const string CoreHeader = "Bmp/BitmapCoreHeaderQR.bmp"; + public static readonly string[] All = { Car, F, NegHeight, CoreHeader, Bpp8 }; } public static class Gif diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Bmp/BitmapCoreHeaderQR.bmp b/tests/ImageSharp.Tests/TestImages/Formats/Bmp/BitmapCoreHeaderQR.bmp new file mode 100644 index 000000000..4c2f26da7 --- /dev/null +++ b/tests/ImageSharp.Tests/TestImages/Formats/Bmp/BitmapCoreHeaderQR.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:04709a3b7e9a73e87f12c5c63f66bc608e3cd61c23fcd38629bb8638bbb1b4de +size 2580