From 892bba1ed1418dce4bfe555695f3814fbae39764 Mon Sep 17 00:00:00 2001 From: Brian Popow <38701097+brianpopow@users.noreply.github.com> Date: Mon, 11 Feb 2019 05:33:03 +0100 Subject: [PATCH] Add support for Decoding BI_ALPHABITFIELDS (#832) * Adds support for BI_ALPHABITFIELDS * Fix for decoding bitmaps with a less than full sized palette --- src/ImageSharp/Formats/Bmp/BmpCompression.cs | 10 +++++-- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 17 ++++++++++-- .../Formats/Bmp/BmpDecoderTests.cs | 26 ++++++++++++++++++ tests/ImageSharp.Tests/TestImages.cs | 2 ++ tests/Images/Input/Bmp/pal8os2sp.bmp | Bin 0 -> 8974 bytes tests/Images/Input/Bmp/rgba32abf.bmp | Bin 0 -> 32582 bytes 6 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 tests/Images/Input/Bmp/pal8os2sp.bmp create mode 100644 tests/Images/Input/Bmp/rgba32abf.bmp diff --git a/src/ImageSharp/Formats/Bmp/BmpCompression.cs b/src/ImageSharp/Formats/Bmp/BmpCompression.cs index 5f14d2243..be275019e 100644 --- a/src/ImageSharp/Formats/Bmp/BmpCompression.cs +++ b/src/ImageSharp/Formats/Bmp/BmpCompression.cs @@ -34,7 +34,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// If the first byte is zero, the record has different meanings, depending /// on the second byte. If the second byte is zero, it is the end of the row, /// if it is one, it is the end of the image. - /// Not supported at the moment. /// RLE4 = 2, @@ -54,6 +53,13 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// The bitmap contains a PNG image. /// Not supported at the moment. /// - PNG = 5 + PNG = 5, + + /// + /// Introduced with Windows CE. + /// Specifies that the bitmap is not compressed and that the color table consists of four DWORD color + /// masks that specify the red, green, blue, and alpha components of each pixel. + /// + BI_ALPHABITFIELDS = 6 } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 496a6df4a..d33ada286 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -75,7 +75,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// The file header containing general information. - /// TODO: Why is this not used? We advance the stream but do not use the values parsed. /// private BmpFileHeader fileHeader; @@ -163,6 +162,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp break; case BmpCompression.BitFields: + case BmpCompression.BI_ALPHABITFIELDS: this.ReadBitFields(pixels, inverted); break; @@ -947,7 +947,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp infoHeaderType = BmpInfoHeaderType.WinVersion3; this.infoHeader = BmpInfoHeader.ParseV3(buffer); - // if the info header is BMP version 3 and the compression type is BITFIELDS, + // If the info header is BMP version 3 and the compression type is BITFIELDS, // color masks for each color channel follow the info header. if (this.infoHeader.Compression == BmpCompression.BitFields) { @@ -958,6 +958,16 @@ namespace SixLabors.ImageSharp.Formats.Bmp this.infoHeader.GreenMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)); this.infoHeader.BlueMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)); } + else if (this.infoHeader.Compression == BmpCompression.BI_ALPHABITFIELDS) + { + byte[] bitfieldsBuffer = new byte[16]; + this.stream.Read(bitfieldsBuffer, 0, 16); + Span data = bitfieldsBuffer.AsSpan(); + this.infoHeader.RedMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)); + this.infoHeader.GreenMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)); + this.infoHeader.BlueMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)); + this.infoHeader.AlphaMask = BinaryPrimitives.ReadInt32LittleEndian(data.Slice(12, 4)); + } } else if (headerSize == BmpInfoHeader.AdobeV3Size) { @@ -1078,6 +1088,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp int colorMapSizeBytes = this.fileHeader.Offset - BmpFileHeader.Size - this.infoHeader.HeaderSize; int colorCountForBitDepth = ImageMaths.GetColorCountForBitDepth(this.infoHeader.BitsPerPixel); bytesPerColorMapEntry = colorMapSizeBytes / colorCountForBitDepth; + + // Edge case for less-than-full-sized palette: bytesPerColorMapEntry should be at least 3. + bytesPerColorMapEntry = Math.Max(bytesPerColorMapEntry, 3); colorMapSize = colorMapSizeBytes; } } diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index e7b5aeaea..ad47ed1e7 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -60,6 +60,20 @@ namespace SixLabors.ImageSharp.Tests } } + [Theory] + [WithFile(RgbaAlphaBitfields, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecodeAlphaBitfields(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider); + + // TODO: Neither System.Drawing nor MagickReferenceDecoder decode this file. + // image.CompareToOriginal(provider); + } + } + [Theory] [WithFile(Bit32Rgba, PixelTypes.Rgba32)] public void BmpDecoder_CanDecodeBitmap_WithAlphaChannel(TestImageProvider provider) @@ -114,6 +128,18 @@ namespace SixLabors.ImageSharp.Tests } } + [Theory] + [WithFile(LessThanFullSizedPalette, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecodeLessThanFullPalete(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider); + image.CompareToOriginal(provider, new MagickReferenceDecoder()); + } + } + [Theory] [WithFile(Rgba32bf56, PixelTypes.Rgba32)] public void BmpDecoder_CanDecodeAdobeBmpv3(TestImageProvider provider) diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 51e2d084c..3a497683a 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -221,6 +221,7 @@ namespace SixLabors.ImageSharp.Tests public const string Bit8Palette4 = "Bmp/pal8-0.bmp"; public const string Os2v2Short = "Bmp/pal8os2v2-16.bmp"; public const string Os2v2 = "Bmp/pal8os2v2.bmp"; + public const string LessThanFullSizedPalette = "Bmp/pal8os2sp.bmp"; public const string Pal8Offset = "Bmp/pal8offs.bmp"; // Bitmap images with compression type BITFIELDS @@ -232,6 +233,7 @@ namespace SixLabors.ImageSharp.Tests public const string Issue735 = "Bmp/issue735.bmp"; public const string Rgba32bf56 = "Bmp/rgba32h56.bmp"; public const string Rgba321010102 = "Bmp/rgba32-1010102.bmp"; + public const string RgbaAlphaBitfields = "Bmp/rgba32abf.bmp"; public static readonly string[] BitFields = { diff --git a/tests/Images/Input/Bmp/pal8os2sp.bmp b/tests/Images/Input/Bmp/pal8os2sp.bmp new file mode 100644 index 0000000000000000000000000000000000000000..e532c89863c0ab16ecd69655235386c3ede893f0 GIT binary patch literal 8974 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 Fe*x!$9?}2+ literal 0 HcmV?d00001 diff --git a/tests/Images/Input/Bmp/rgba32abf.bmp b/tests/Images/Input/Bmp/rgba32abf.bmp new file mode 100644 index 0000000000000000000000000000000000000000..d9bb0189c47f7510a11afd9240eb2796cf37dcc1 GIT binary patch literal 32582 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 ZuHWhR`buT}L4VZ0s-l0>|J0xK{{x0L5l{dC literal 0 HcmV?d00001