diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index b11f7934d2..68528edcd6 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -513,6 +513,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp // 12 bytes this.infoHeader = BmpInfoHeader.ParseCore(buffer); } + else if (headerSize == BmpInfoHeader.Os22ShortSize) + { + // 16 bytes + this.infoHeader = BmpInfoHeader.ParseOs22Short(buffer); + } else if (headerSize >= BmpInfoHeader.Size) { // >= 40 bytes diff --git a/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs b/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs index 4dd63a9626..5177bc325c 100644 --- a/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs +++ b/src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs @@ -26,6 +26,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// public const int CoreSize = 12; + /// + /// Defines the size of the short variant of the OS22XBITMAPHEADER data structure in the bitmap file. + /// + public const int Os22ShortSize = 16; + /// /// Defines the size of the biggest supported header data structure in the bitmap file. /// @@ -143,7 +148,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// Parses the BITMAPCOREHEADER consisting of the headerSize, width, height, planes, and bitsPerPixel fields (12 bytes). /// - /// The data to parse, + /// The data to parse. /// Parsed header /// public static BmpInfoHeader ParseCore(ReadOnlySpan data) @@ -156,6 +161,23 @@ namespace SixLabors.ImageSharp.Formats.Bmp bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(10, 2))); } + /// + /// Parses a short variant of the OS22XBITMAPHEADER. It is identical to the BITMAPCOREHEADER, except that the width and height + /// are 4 bytes instead of 2. + /// + /// The data to parse. + /// Parsed header + /// + public static BmpInfoHeader ParseOs22Short(ReadOnlySpan data) + { + return new BmpInfoHeader( + headerSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)), + width: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)), + height: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)), + planes: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(12, 2)), + bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(14, 2))); + } + public unsafe void WriteTo(Span buffer) { ref BmpInfoHeader dest = ref Unsafe.As(ref MemoryMarshal.GetReference(buffer)); diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index d60d9d918a..251475567f 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -113,18 +113,17 @@ namespace SixLabors.ImageSharp.Tests } [Theory] - [MemberData(nameof(RatioFiles))] - public void Identify_VerifyRatio(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit) + [WithFile(Os2v2Short, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_Os2v2XShortHeader(TestImageProvider provider) + where TPixel : struct, IPixel { - var testFile = TestFile.Create(imagePath); - using (var stream = new MemoryStream(testFile.Bytes, false)) + using (Image image = provider.GetImage(new BmpDecoder())) { - var decoder = new BmpDecoder(); - IImageInfo image = decoder.Identify(Configuration.Default, stream); - ImageMetaData meta = image.MetaData; - Assert.Equal(xResolution, meta.HorizontalResolution); - Assert.Equal(yResolution, meta.VerticalResolution); - Assert.Equal(resolutionUnit, meta.ResolutionUnits); + image.DebugSave(provider, "png"); + + // TODO: Neither System.Drawing not MagickReferenceDecoder + // can correctly decode this file. + // image.CompareToOriginal(provider); } } } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 9c747dcb8a..e5b93ab77c 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -201,9 +201,11 @@ namespace SixLabors.ImageSharp.Tests public const string Bit16 = "Bmp/test16.bmp"; public const string Bit16Inverted = "Bmp/test16-inverted.bmp"; public const string Bit32Rgb = "Bmp/rgb32.bmp"; + // Note: This format can be called OS/2 BMPv1, or Windows BMPv2 public const string WinBmpv2 = "Bmp/pal8os2v1_winv2.bmp"; public const string Bit8Palette4 = "Bmp/pal8-0.bmp"; + public const string Os2v2Short = "Bmp/pal8os2v2-16.bmp"; public static readonly string[] All = { diff --git a/tests/Images/Input/Bmp/pal8os2v2-16.bmp b/tests/Images/Input/Bmp/pal8os2v2-16.bmp new file mode 100644 index 0000000000..7cc99c56f7 --- /dev/null +++ b/tests/Images/Input/Bmp/pal8os2v2-16.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8085c60533095d9a756009f84c050d01605fc029a980a13d1bdabe943c50de5a +size 9246