Skip to content

Commit

Permalink
Add support for Decoding BI_ALPHABITFIELDS (#832)
Browse files Browse the repository at this point in the history
* Adds support for BI_ALPHABITFIELDS

* Fix for decoding bitmaps with a less than full sized palette
  • Loading branch information
brianpopow authored and JimBobSquarePants committed Feb 11, 2019
1 parent 13ff434 commit 892bba1
Show file tree
Hide file tree
Showing 6 changed files with 51 additions and 4 deletions.
10 changes: 8 additions & 2 deletions src/ImageSharp/Formats/Bmp/BmpCompression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ internal enum BmpCompression : int
/// 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,

Expand All @@ -54,6 +53,13 @@ internal enum BmpCompression : int
/// 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 changes: 15 additions & 2 deletions src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ internal sealed class BmpDecoderCore

/// <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;

Expand Down Expand Up @@ -163,6 +162,7 @@ public Image<TPixel> Decode<TPixel>(Stream stream)
break;

case BmpCompression.BitFields:
case BmpCompression.BI_ALPHABITFIELDS:
this.ReadBitFields(pixels, inverted);

break;
Expand Down Expand Up @@ -947,7 +947,7 @@ private void ReadInfoHeader()
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)
{
Expand All @@ -958,6 +958,16 @@ private void ReadInfoHeader()
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)
{
Expand Down Expand Up @@ -1078,6 +1088,9 @@ private int ReadImageHeaders(Stream stream, out bool inverted, out byte[] palett
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;
}
}
Expand Down
26 changes: 26 additions & 0 deletions tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,20 @@ public void BmpDecoder_CanDecodeBitfields<TPixel>(TestImageProvider<TPixel> prov
}
}

[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)
Expand Down Expand Up @@ -114,6 +128,18 @@ public void BmpDecoder_CanDecodeBmpv3<TPixel>(TestImageProvider<TPixel> provider
}
}

[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)
Expand Down
2 changes: 2 additions & 0 deletions tests/ImageSharp.Tests/TestImages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ public static class Bmp
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
Expand All @@ -232,6 +233,7 @@ public static class Bmp
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
= {
Expand Down
Binary file added tests/Images/Input/Bmp/pal8os2sp.bmp
Binary file not shown.
Binary file added tests/Images/Input/Bmp/rgba32abf.bmp
Binary file not shown.

0 comments on commit 892bba1

Please sign in to comment.