From 76b77dafb75864c2b721be56681c21e414045fff Mon Sep 17 00:00:00 2001 From: Mormegil Date: Mon, 21 Aug 2017 23:52:28 +0200 Subject: [PATCH 1/2] Support more variants of BMP headers --- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 70 ++++++++++++++++--- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 2 +- src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs | 21 +++++- tests/ImageSharp.Tests/FileTestBase.cs | 1 + .../Formats/Bmp/BmpDecoderTests.cs | 27 +++++++ tests/ImageSharp.Tests/TestImages.cs | 3 +- .../TestImages/Formats/Bmp/QR_core.bmp | 3 + 7 files changed, 114 insertions(+), 13 deletions(-) create mode 100644 tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs create mode 100644 tests/ImageSharp.Tests/TestImages/Formats/Bmp/QR_core.bmp 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 new file mode 100644 index 000000000..4e10de23f --- /dev/null +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -0,0 +1,27 @@ +// +// Copyright (c) James Jackson-South and contributors. +// Licensed under the Apache License, Version 2.0. +// + +using ImageSharp.Formats; + +namespace ImageSharp.Tests +{ + using ImageSharp.PixelFormats; + + using Xunit; + + 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"); + } + } + } +} \ No newline at end of file diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 46887d721..66a716b27 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -116,7 +116,8 @@ namespace ImageSharp.Tests public const string Car = "Bmp/Car.bmp"; public const string F = "Bmp/F.bmp"; public const string NegHeight = "Bmp/neg_height.bmp"; - public static readonly string[] All = { Car, F, NegHeight }; + public const string CoreHeader = "Bmp/QR_core.bmp"; + public static readonly string[] All = { Car, F, NegHeight, CoreHeader }; } public static class Gif diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Bmp/QR_core.bmp b/tests/ImageSharp.Tests/TestImages/Formats/Bmp/QR_core.bmp new file mode 100644 index 000000000..4c2f26da7 --- /dev/null +++ b/tests/ImageSharp.Tests/TestImages/Formats/Bmp/QR_core.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:04709a3b7e9a73e87f12c5c63f66bc608e3cd61c23fcd38629bb8638bbb1b4de +size 2580 From 209d6181b813f7e00be485aeb5638833bc1da4f3 Mon Sep 17 00:00:00 2001 From: Anton Firszov Date: Tue, 22 Aug 2017 01:01:19 +0200 Subject: [PATCH 2/2] more descriptive name for QR_core.bmp + giving AppVeyor a kick --- tests/ImageSharp.Tests/TestImages.cs | 2 +- .../Formats/Bmp/{QR_core.bmp => BitmapCoreHeaderQR.bmp} | 0 2 files changed, 1 insertion(+), 1 deletion(-) rename tests/ImageSharp.Tests/TestImages/Formats/Bmp/{QR_core.bmp => BitmapCoreHeaderQR.bmp} (100%) diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 66a716b27..da27ab081 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -116,7 +116,7 @@ namespace ImageSharp.Tests public const string Car = "Bmp/Car.bmp"; public const string F = "Bmp/F.bmp"; public const string NegHeight = "Bmp/neg_height.bmp"; - public const string CoreHeader = "Bmp/QR_core.bmp"; + public const string CoreHeader = "Bmp/BitmapCoreHeaderQR.bmp"; public static readonly string[] All = { Car, F, NegHeight, CoreHeader }; } diff --git a/tests/ImageSharp.Tests/TestImages/Formats/Bmp/QR_core.bmp b/tests/ImageSharp.Tests/TestImages/Formats/Bmp/BitmapCoreHeaderQR.bmp similarity index 100% rename from tests/ImageSharp.Tests/TestImages/Formats/Bmp/QR_core.bmp rename to tests/ImageSharp.Tests/TestImages/Formats/Bmp/BitmapCoreHeaderQR.bmp