From 6c4208e006d19e5260fa0622427ad8bf6327af72 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 | Bin 0 -> 2580 bytes 7 files changed, 111 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 0000000000000000000000000000000000000000..b5c58f8cb9e57932b64b88e7f790b04d08a22f60 GIT binary patch literal 2580 zcmeH_OHuSM^G=pMT4gxecB%wYVxa(-=iloMp`AR6xX>1*3TGvyUV5Ns=Gy9 zpRKu_yB6@2=w(KM%B-Cf8MY-^FeR!K_I%XG<6_kwl16WZVO;^JDM1-cc@ZkJ>BULo zl-TYt``j2qOgaru-w;l=c^_iti4S!AaRKW4As{$FN$0DO7+TrqZV~WRxw=6l(LeW zTn;kP%9?h;6WPo7vhb`lBf*p_nE;_7PPCh0#QUg3mEwGVo$1 S0EqH4Ui_dg=l#pMt=$1>v`uRO literal 0 HcmV?d00001 From 9d689314f66cb6845c979cf9a6d311b1140db0d1 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 +- .../Bmp/{QR_core.bmp => BitmapCoreHeaderQR.bmp} | Bin 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