Browse Source

Add support for Decoding BI_ALPHABITFIELDS (#832)

* Adds support for BI_ALPHABITFIELDS

* Fix for decoding bitmaps with a less than full sized palette
af/merge-core
Brian Popow 7 years ago
committed by James Jackson-South
parent
commit
a2c4b2d5d8
  1. 10
      src/ImageSharp/Formats/Bmp/BmpCompression.cs
  2. 17
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  3. 26
      tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs
  4. 2
      tests/ImageSharp.Tests/TestImages.cs
  5. 3
      tests/Images/Input/Bmp/pal8os2sp.bmp
  6. 3
      tests/Images/Input/Bmp/rgba32abf.bmp

10
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.
/// </summary>
RLE4 = 2,
@ -54,6 +53,13 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// The bitmap contains a PNG image.
/// Not supported at the moment.
/// </summary>
PNG = 5
PNG = 5,
/// <summary>
/// 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.
/// </summary>
BI_ALPHABITFIELDS = 6
}
}

17
src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs

@ -75,7 +75,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <summary>
/// The file header containing general information.
/// TODO: Why is this not used? We advance the stream but do not use the values parsed.
/// </summary>
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<byte> data = bitfieldsBuffer.AsSpan<byte>();
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;
}
}

26
tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs

@ -60,6 +60,20 @@ namespace SixLabors.ImageSharp.Tests
}
}
[Theory]
[WithFile(RgbaAlphaBitfields, PixelTypes.Rgba32)]
public void BmpDecoder_CanDecodeAlphaBitfields<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> 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<TPixel>(TestImageProvider<TPixel> provider)
@ -114,6 +128,18 @@ namespace SixLabors.ImageSharp.Tests
}
}
[Theory]
[WithFile(LessThanFullSizedPalette, PixelTypes.Rgba32)]
public void BmpDecoder_CanDecodeLessThanFullPalete<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(new BmpDecoder()))
{
image.DebugSave(provider);
image.CompareToOriginal(provider, new MagickReferenceDecoder());
}
}
[Theory]
[WithFile(Rgba32bf56, PixelTypes.Rgba32)]
public void BmpDecoder_CanDecodeAdobeBmpv3<TPixel>(TestImageProvider<TPixel> provider)

2
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
= {

3
tests/Images/Input/Bmp/pal8os2sp.bmp

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e58eb53dcbf1c0920ad5af8d07f576ee794c1c5d051e1d950060ab6f6f253f50
size 8974

3
tests/Images/Input/Bmp/rgba32abf.bmp

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:1d2183466e05c768b5a9d2ce5d132d72db2272bb331c7b27560d24f197f4165e
size 32582
Loading…
Cancel
Save