Browse Source

Bitmap decoder now can decode bitmap arrays (#930)

* Bitmap Decoder can now decode BitmapArray

* Add tests for bitmap metadata decoing. Fix an issue that a bitmap with a v5 header would be set in the metadata as an v4 header.

* Fixed issue with decoding bitmap arrays: color map size was not determined correctly. Added more test images.

* Refactor colormap size duplicate declaration.

* Fixed an issue, that when an unsupported bitmap is loaded the typ marker was not correctly shown in the error message
af/merge-core
Brian Popow 7 years ago
committed by James Jackson-South
parent
commit
cf61a08b31
  1. 53
      src/ImageSharp/Formats/Bmp/BmpArrayFileHeader.cs
  2. 69
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  3. 6
      src/ImageSharp/Formats/Bmp/BmpFileHeader.cs
  4. 8
      src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs
  5. 17
      src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs
  6. 64
      tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs
  7. 6
      tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs
  8. 32
      tests/ImageSharp.Tests/Formats/Bmp/BmpMetaDataTests.cs
  9. 8
      tests/ImageSharp.Tests/TestImages.cs
  10. 3
      tests/Images/Input/Bmp/9S.BMP
  11. 3
      tests/Images/Input/Bmp/DIAMOND.BMP
  12. 3
      tests/Images/Input/Bmp/GMARBLE.BMP
  13. 3
      tests/Images/Input/Bmp/PINES.BMP
  14. 3
      tests/Images/Input/Bmp/SKATER.BMP
  15. 3
      tests/Images/Input/Bmp/SPADE.BMP
  16. 3
      tests/Images/Input/Bmp/SUNFLOW.BMP
  17. 3
      tests/Images/Input/Bmp/WARPD.BMP

53
src/ImageSharp/Formats/Bmp/BmpArrayFileHeader.cs

@ -0,0 +1,53 @@
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Runtime.InteropServices;
namespace SixLabors.ImageSharp.Formats.Bmp
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
internal readonly struct BmpArrayFileHeader
{
public BmpArrayFileHeader(short type, int size, int offsetToNext, short width, short height)
{
this.Type = type;
this.Size = size;
this.OffsetToNext = offsetToNext;
this.ScreenWidth = width;
this.ScreenHeight = height;
}
/// <summary>
/// Gets the Bitmap identifier.
/// The field used to identify the bitmap file: 0x42 0x41 (Hex code points for B and A).
/// </summary>
public short Type { get; }
/// <summary>
/// Gets the size of this header.
/// </summary>
public int Size { get; }
/// <summary>
/// Gets the offset to next OS2BMPARRAYFILEHEADER.
/// This offset is calculated from the starting byte of the file. A value of zero indicates that this header is for the last image in the array list.
/// </summary>
public int OffsetToNext { get; }
/// <summary>
/// Gets the width of the image display in pixels.
/// </summary>
public short ScreenWidth { get; }
/// <summary>
/// Gets the height of the image display in pixels.
/// </summary>
public short ScreenHeight { get; }
public static BmpArrayFileHeader Parse(Span<byte> data)
{
return MemoryMarshal.Cast<byte, BmpArrayFileHeader>(data)[0];
}
}
}

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

@ -1053,18 +1053,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.stream.Read(buffer, 0, BmpInfoHeader.HeaderSizeSize); this.stream.Read(buffer, 0, BmpInfoHeader.HeaderSizeSize);
int headerSize = BinaryPrimitives.ReadInt32LittleEndian(buffer); int headerSize = BinaryPrimitives.ReadInt32LittleEndian(buffer);
if (headerSize < BmpInfoHeader.CoreSize) if (headerSize < BmpInfoHeader.CoreSize || headerSize > BmpInfoHeader.MaxHeaderSize)
{ {
BmpThrowHelper.ThrowNotSupportedException($"ImageSharp does not support this BMP file. HeaderSize is '{headerSize}'."); BmpThrowHelper.ThrowNotSupportedException($"ImageSharp does not support this BMP file. HeaderSize is '{headerSize}'.");
} }
int skipAmount = 0;
if (headerSize > BmpInfoHeader.MaxHeaderSize)
{
skipAmount = headerSize - BmpInfoHeader.MaxHeaderSize;
headerSize = BmpInfoHeader.MaxHeaderSize;
}
// Read the rest of the header. // Read the rest of the header.
this.stream.Read(buffer, BmpInfoHeader.HeaderSizeSize, headerSize - BmpInfoHeader.HeaderSizeSize); this.stream.Read(buffer, BmpInfoHeader.HeaderSizeSize, headerSize - BmpInfoHeader.HeaderSizeSize);
@ -1169,15 +1162,13 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{ {
this.bmpMetadata.BitsPerPixel = (BmpBitsPerPixel)bitsPerPixel; this.bmpMetadata.BitsPerPixel = (BmpBitsPerPixel)bitsPerPixel;
} }
// Skip the remaining header because we can't read those parts.
this.stream.Skip(skipAmount);
} }
/// <summary> /// <summary>
/// Reads the <see cref="BmpFileHeader"/> from the stream. /// Reads the <see cref="BmpFileHeader"/> from the stream.
/// </summary> /// </summary>
private void ReadFileHeader() /// <returns>The color map size in bytes, if it could be determined by the file header. Otherwise -1.</returns>
private int ReadFileHeader()
{ {
#if NETCOREAPP2_1 #if NETCOREAPP2_1
Span<byte> buffer = stackalloc byte[BmpFileHeader.Size]; Span<byte> buffer = stackalloc byte[BmpFileHeader.Size];
@ -1186,12 +1177,36 @@ namespace SixLabors.ImageSharp.Formats.Bmp
#endif #endif
this.stream.Read(buffer, 0, BmpFileHeader.Size); this.stream.Read(buffer, 0, BmpFileHeader.Size);
this.fileHeader = BmpFileHeader.Parse(buffer); short fileTypeMarker = BinaryPrimitives.ReadInt16LittleEndian(buffer);
switch (fileTypeMarker)
if (this.fileHeader.Type != BmpConstants.TypeMarkers.Bitmap)
{ {
BmpThrowHelper.ThrowNotSupportedException($"ImageSharp does not support this BMP file. File header bitmap type marker '{this.fileHeader.Type}'."); case BmpConstants.TypeMarkers.Bitmap:
this.fileHeader = BmpFileHeader.Parse(buffer);
break;
case BmpConstants.TypeMarkers.BitmapArray:
// The Array file header is followed by the bitmap file header of the first image.
var arrayHeader = BmpArrayFileHeader.Parse(buffer);
this.stream.Read(buffer, 0, BmpFileHeader.Size);
this.fileHeader = BmpFileHeader.Parse(buffer);
if (this.fileHeader.Type != BmpConstants.TypeMarkers.Bitmap)
{
BmpThrowHelper.ThrowNotSupportedException($"Unsupported bitmap file inside a BitmapArray file. File header bitmap type marker '{this.fileHeader.Type}'.");
}
if (arrayHeader.OffsetToNext != 0)
{
int colorMapSizeBytes = arrayHeader.OffsetToNext - arrayHeader.Size;
return colorMapSizeBytes;
}
break;
default:
BmpThrowHelper.ThrowNotSupportedException($"ImageSharp does not support this BMP file. File header bitmap type marker '{fileTypeMarker}'.");
break;
} }
return -1;
} }
/// <summary> /// <summary>
@ -1203,7 +1218,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
{ {
this.stream = stream; this.stream = stream;
this.ReadFileHeader(); int colorMapSizeBytes = this.ReadFileHeader();
this.ReadInfoHeader(); this.ReadInfoHeader();
// see http://www.drdobbs.com/architecture-and-design/the-bmp-file-format-part-1/184409517 // see http://www.drdobbs.com/architecture-and-design/the-bmp-file-format-part-1/184409517
@ -1218,7 +1233,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.infoHeader.Height = -this.infoHeader.Height; this.infoHeader.Height = -this.infoHeader.Height;
} }
int colorMapSize = -1;
int bytesPerColorMapEntry = 4; int bytesPerColorMapEntry = 4;
if (this.infoHeader.ClrUsed == 0) if (this.infoHeader.ClrUsed == 0)
@ -1227,35 +1241,38 @@ namespace SixLabors.ImageSharp.Formats.Bmp
|| this.infoHeader.BitsPerPixel == 4 || this.infoHeader.BitsPerPixel == 4
|| this.infoHeader.BitsPerPixel == 8) || this.infoHeader.BitsPerPixel == 8)
{ {
int colorMapSizeBytes = this.fileHeader.Offset - BmpFileHeader.Size - this.infoHeader.HeaderSize; if (colorMapSizeBytes == -1)
{
colorMapSizeBytes = this.fileHeader.Offset - BmpFileHeader.Size - this.infoHeader.HeaderSize;
}
int colorCountForBitDepth = ImageMaths.GetColorCountForBitDepth(this.infoHeader.BitsPerPixel); int colorCountForBitDepth = ImageMaths.GetColorCountForBitDepth(this.infoHeader.BitsPerPixel);
bytesPerColorMapEntry = colorMapSizeBytes / colorCountForBitDepth; bytesPerColorMapEntry = colorMapSizeBytes / colorCountForBitDepth;
// Edge case for less-than-full-sized palette: bytesPerColorMapEntry should be at least 3. // Edge case for less-than-full-sized palette: bytesPerColorMapEntry should be at least 3.
bytesPerColorMapEntry = Math.Max(bytesPerColorMapEntry, 3); bytesPerColorMapEntry = Math.Max(bytesPerColorMapEntry, 3);
colorMapSize = colorMapSizeBytes;
} }
} }
else else
{ {
colorMapSize = this.infoHeader.ClrUsed * bytesPerColorMapEntry; colorMapSizeBytes = this.infoHeader.ClrUsed * bytesPerColorMapEntry;
} }
palette = null; palette = null;
if (colorMapSize > 0) if (colorMapSizeBytes > 0)
{ {
// Usually the color palette is 1024 byte (256 colors * 4), but the documentation does not mention a size limit. // Usually the color palette is 1024 byte (256 colors * 4), but the documentation does not mention a size limit.
// Make sure, that we will not read pass the bitmap offset (starting position of image data). // Make sure, that we will not read pass the bitmap offset (starting position of image data).
if ((this.stream.Position + colorMapSize) > this.fileHeader.Offset) if ((this.stream.Position + colorMapSizeBytes) > this.fileHeader.Offset)
{ {
BmpThrowHelper.ThrowImageFormatException( BmpThrowHelper.ThrowImageFormatException(
$"Reading the color map would read beyond the bitmap offset. Either the color map size of '{colorMapSize}' is invalid or the bitmap offset."); $"Reading the color map would read beyond the bitmap offset. Either the color map size of '{colorMapSizeBytes}' is invalid or the bitmap offset.");
} }
palette = new byte[colorMapSize]; palette = new byte[colorMapSizeBytes];
this.stream.Read(palette, 0, colorMapSize); this.stream.Read(palette, 0, colorMapSizeBytes);
} }
this.infoHeader.VerifyDimensions(); this.infoHeader.VerifyDimensions();

6
src/ImageSharp/Formats/Bmp/BmpFileHeader.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
internal readonly struct BmpFileHeader internal readonly struct BmpFileHeader
{ {
/// <summary> /// <summary>
/// Defines of the data structure in the bitmap file. /// Defines the size of the data structure in the bitmap file.
/// </summary> /// </summary>
public const int Size = 14; public const int Size = 14;
@ -69,4 +69,4 @@ namespace SixLabors.ImageSharp.Formats.Bmp
dest = this; dest = this;
} }
} }
} }

8
src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
@ -22,7 +22,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
private bool IsSupportedFileFormat(ReadOnlySpan<byte> header) private bool IsSupportedFileFormat(ReadOnlySpan<byte> header)
{ {
return header.Length >= this.HeaderSize && BinaryPrimitives.ReadInt16LittleEndian(header) == BmpConstants.TypeMarkers.Bitmap; short fileTypeMarker = BinaryPrimitives.ReadInt16LittleEndian(header);
return header.Length >= this.HeaderSize &&
(fileTypeMarker == BmpConstants.TypeMarkers.Bitmap || fileTypeMarker == BmpConstants.TypeMarkers.BitmapArray);
} }
} }
} }

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

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System; using System;
using System.Buffers.Binary; using System.Buffers.Binary;
@ -51,10 +51,15 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary> /// </summary>
public const int SizeV4 = 108; public const int SizeV4 = 108;
/// <summary>
/// Defines the size of the BITMAPINFOHEADER (BMP Version 5) data structure in the bitmap file.
/// </summary>
public const int SizeV5 = 124;
/// <summary> /// <summary>
/// Defines the size of the biggest supported header data structure in the bitmap file. /// Defines the size of the biggest supported header data structure in the bitmap file.
/// </summary> /// </summary>
public const int MaxHeaderSize = SizeV4; public const int MaxHeaderSize = SizeV5;
/// <summary> /// <summary>
/// Defines the size of the <see cref="HeaderSize"/> field. /// Defines the size of the <see cref="HeaderSize"/> field.
@ -272,7 +277,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// Parses the BITMAPCOREHEADER (BMP Version 2) consisting of the headerSize, width, height, planes, and bitsPerPixel fields (12 bytes). /// Parses the BITMAPCOREHEADER (BMP Version 2) consisting of the headerSize, width, height, planes, and bitsPerPixel fields (12 bytes).
/// </summary> /// </summary>
/// <param name="data">The data to parse.</param> /// <param name="data">The data to parse.</param>
/// <returns>Parsed header</returns> /// <returns>The parsed header.</returns>
/// <seealso href="https://msdn.microsoft.com/en-us/library/windows/desktop/dd183372.aspx"/> /// <seealso href="https://msdn.microsoft.com/en-us/library/windows/desktop/dd183372.aspx"/>
public static BmpInfoHeader ParseCore(ReadOnlySpan<byte> data) public static BmpInfoHeader ParseCore(ReadOnlySpan<byte> data)
{ {
@ -289,7 +294,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// are 4 bytes instead of 2, resulting in 16 bytes total. /// are 4 bytes instead of 2, resulting in 16 bytes total.
/// </summary> /// </summary>
/// <param name="data">The data to parse.</param> /// <param name="data">The data to parse.</param>
/// <returns>Parsed header</returns> /// <returns>The parsed header.</returns>
/// <seealso href="https://www.fileformat.info/format/os2bmp/egff.htm"/> /// <seealso href="https://www.fileformat.info/format/os2bmp/egff.htm"/>
public static BmpInfoHeader ParseOs22Short(ReadOnlySpan<byte> data) public static BmpInfoHeader ParseOs22Short(ReadOnlySpan<byte> data)
{ {
@ -406,7 +411,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <seealso href="http://www.fileformat.info/format/bmp/egff.htm"/> /// <seealso href="http://www.fileformat.info/format/bmp/egff.htm"/>
public static BmpInfoHeader ParseV4(ReadOnlySpan<byte> data) public static BmpInfoHeader ParseV4(ReadOnlySpan<byte> data)
{ {
if (data.Length != SizeV4) if (data.Length < SizeV4)
{ {
throw new ArgumentException(nameof(data), $"Must be {SizeV4} bytes. Was {data.Length} bytes."); throw new ArgumentException(nameof(data), $"Must be {SizeV4} bytes. Was {data.Length} bytes.");
} }
@ -457,4 +462,4 @@ namespace SixLabors.ImageSharp.Formats.Bmp
} }
} }
} }
} }

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

@ -343,7 +343,6 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp
} }
[Theory] [Theory]
[WithFile(Rgba32bf56AdobeV3, PixelTypes.Rgba32)]
[WithFile(Rgb32h52AdobeV3, PixelTypes.Rgba32)] [WithFile(Rgb32h52AdobeV3, PixelTypes.Rgba32)]
public void BmpDecoder_CanDecodeAdobeBmpv3<TPixel>(TestImageProvider<TPixel> provider) public void BmpDecoder_CanDecodeAdobeBmpv3<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>
@ -355,6 +354,18 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp
} }
} }
[Theory]
[WithFile(Rgba32bf56AdobeV3, PixelTypes.Rgba32)]
public void BmpDecoder_CanDecodeAdobeBmpv3_WithAlpha<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] [Theory]
[WithFile(WinBmpv4, PixelTypes.Rgba32)] [WithFile(WinBmpv4, PixelTypes.Rgba32)]
public void BmpDecoder_CanDecodeBmpv4<TPixel>(TestImageProvider<TPixel> provider) public void BmpDecoder_CanDecodeBmpv4<TPixel>(TestImageProvider<TPixel> provider)
@ -429,12 +440,35 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp
[InlineData(Bit4, 4)] [InlineData(Bit4, 4)]
[InlineData(Bit1, 1)] [InlineData(Bit1, 1)]
[InlineData(Bit1Pal1, 1)] [InlineData(Bit1Pal1, 1)]
public void Identify(string imagePath, int expectedPixelSize) public void Identify_DetectsCorrectPixelType(string imagePath, int expectedPixelSize)
{ {
var testFile = TestFile.Create(imagePath); var testFile = TestFile.Create(imagePath);
using (var stream = new MemoryStream(testFile.Bytes, false)) using (var stream = new MemoryStream(testFile.Bytes, false))
{ {
Assert.Equal(expectedPixelSize, Image.Identify(stream)?.PixelType?.BitsPerPixel); IImageInfo imageInfo = Image.Identify(stream);
Assert.NotNull(imageInfo);
Assert.Equal(expectedPixelSize, imageInfo.PixelType?.BitsPerPixel);
}
}
[Theory]
[InlineData(Bit32Rgb, 127, 64)]
[InlineData(Car, 600, 450)]
[InlineData(Bit16, 127, 64)]
[InlineData(Bit16Inverted, 127, 64)]
[InlineData(Bit8, 127, 64)]
[InlineData(Bit8Inverted, 127, 64)]
[InlineData(RLE8, 491, 272)]
[InlineData(RLE8Inverted, 491, 272)]
public void Identify_DetectsCorrectWidthAndHeight(string imagePath, int expectedWidth, int expectedHeight)
{
var testFile = TestFile.Create(imagePath);
using (var stream = new MemoryStream(testFile.Bytes, false))
{
IImageInfo imageInfo = Image.Identify(stream);
Assert.NotNull(imageInfo);
Assert.Equal(expectedWidth, imageInfo.Width);
Assert.Equal(expectedHeight, imageInfo.Height);
} }
} }
@ -465,8 +499,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp
{ {
image.DebugSave(provider); image.DebugSave(provider);
// TODO: Neither System.Drawing not MagickReferenceDecoder // TODO: Neither System.Drawing or MagickReferenceDecoder can correctly decode this file.
// can correctly decode this file.
// image.CompareToOriginal(provider); // image.CompareToOriginal(provider);
} }
} }
@ -486,5 +519,26 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp
// image.CompareToOriginal(provider, new MagickReferenceDecoder()); // image.CompareToOriginal(provider, new MagickReferenceDecoder());
} }
} }
[Theory]
[WithFile(Os2BitmapArray9s, PixelTypes.Rgba32)]
[WithFile(Os2BitmapArrayDiamond, PixelTypes.Rgba32)]
[WithFile(Os2BitmapArraySkater, PixelTypes.Rgba32)]
[WithFile(Os2BitmapArraySpade, PixelTypes.Rgba32)]
[WithFile(Os2BitmapArraySunflower, PixelTypes.Rgba32)]
[WithFile(Os2BitmapArrayMarble, PixelTypes.Rgba32)]
[WithFile(Os2BitmapArrayWarpd, PixelTypes.Rgba32)]
[WithFile(Os2BitmapArrayPines, PixelTypes.Rgba32)]
public void BmpDecoder_CanDecode_Os2BitmapArray<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{
using (Image<TPixel> image = provider.GetImage(new BmpDecoder()))
{
image.DebugSave(provider);
// TODO: Neither System.Drawing or MagickReferenceDecoder can correctly decode this file.
// image.CompareToOriginal(provider);
}
}
} }
} }

6
tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs

@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.IO; using System.IO;
@ -14,6 +14,8 @@ using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Formats.Bmp namespace SixLabors.ImageSharp.Tests.Formats.Bmp
{ {
using static TestImages.Bmp; using static TestImages.Bmp;
@ -262,4 +264,4 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp
} }
} }
} }
} }

32
tests/ImageSharp.Tests/Formats/Bmp/BmpMetaDataTests.cs

@ -1,11 +1,17 @@
// Copyright (c) Six Labors and contributors. // Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0. // Licensed under the Apache License, Version 2.0.
using System.IO;
using SixLabors.ImageSharp.Formats.Bmp; using SixLabors.ImageSharp.Formats.Bmp;
using Xunit; using Xunit;
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Tests.Formats.Bmp namespace SixLabors.ImageSharp.Tests.Formats.Bmp
{ {
using static TestImages.Bmp;
public class BmpMetaDataTests public class BmpMetaDataTests
{ {
[Fact] [Fact]
@ -18,5 +24,27 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp
Assert.False(meta.BitsPerPixel.Equals(clone.BitsPerPixel)); Assert.False(meta.BitsPerPixel.Equals(clone.BitsPerPixel));
} }
[Theory]
[InlineData(WinBmpv2, BmpInfoHeaderType.WinVersion2)]
[InlineData(WinBmpv3, BmpInfoHeaderType.WinVersion3)]
[InlineData(WinBmpv4, BmpInfoHeaderType.WinVersion4)]
[InlineData(WinBmpv5, BmpInfoHeaderType.WinVersion5)]
[InlineData(Os2v2Short, BmpInfoHeaderType.Os2Version2Short)]
[InlineData(Rgb32h52AdobeV3, BmpInfoHeaderType.AdobeVersion3)]
[InlineData(Rgba32bf56AdobeV3, BmpInfoHeaderType.AdobeVersion3WithAlpha)]
[InlineData(Os2v2, BmpInfoHeaderType.Os2Version2)]
public void Identify_DetectsCorrectBitmapInfoHeaderType(string imagePath, BmpInfoHeaderType expectedInfoHeaderType)
{
var testFile = TestFile.Create(imagePath);
using (var stream = new MemoryStream(testFile.Bytes, false))
{
IImageInfo imageInfo = Image.Identify(stream);
Assert.NotNull(imageInfo);
BmpMetadata bitmapMetaData = imageInfo.Metadata.GetFormatMetadata(BmpFormat.Instance);
Assert.NotNull(bitmapMetaData);
Assert.Equal(expectedInfoHeaderType, bitmapMetaData.InfoHeaderType);
}
}
} }
} }

8
tests/ImageSharp.Tests/TestImages.cs

@ -261,6 +261,14 @@ namespace SixLabors.ImageSharp.Tests
public const string Bit8Palette4 = "Bmp/pal8-0.bmp"; public const string Bit8Palette4 = "Bmp/pal8-0.bmp";
public const string Os2v2Short = "Bmp/pal8os2v2-16.bmp"; public const string Os2v2Short = "Bmp/pal8os2v2-16.bmp";
public const string Os2v2 = "Bmp/pal8os2v2.bmp"; public const string Os2v2 = "Bmp/pal8os2v2.bmp";
public const string Os2BitmapArray9s = "Bmp/9S.BMP";
public const string Os2BitmapArrayDiamond = "Bmp/DIAMOND.BMP";
public const string Os2BitmapArrayMarble = "Bmp/GMARBLE.BMP";
public const string Os2BitmapArraySkater = "Bmp/SKATER.BMP";
public const string Os2BitmapArraySpade = "Bmp/SPADE.BMP";
public const string Os2BitmapArraySunflower = "Bmp/SUNFLOW.BMP";
public const string Os2BitmapArrayWarpd = "Bmp/WARPD.BMP";
public const string Os2BitmapArrayPines = "Bmp/PINES.BMP";
public const string LessThanFullSizedPalette = "Bmp/pal8os2sp.bmp"; public const string LessThanFullSizedPalette = "Bmp/pal8os2sp.bmp";
public const string Pal8Offset = "Bmp/pal8offs.bmp"; public const string Pal8Offset = "Bmp/pal8offs.bmp";
public const string OversizedPalette = "Bmp/pal8oversizepal.bmp"; public const string OversizedPalette = "Bmp/pal8oversizepal.bmp";

3
tests/Images/Input/Bmp/9S.BMP

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

3
tests/Images/Input/Bmp/DIAMOND.BMP

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

3
tests/Images/Input/Bmp/GMARBLE.BMP

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

3
tests/Images/Input/Bmp/PINES.BMP

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

3
tests/Images/Input/Bmp/SKATER.BMP

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

3
tests/Images/Input/Bmp/SPADE.BMP

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

3
tests/Images/Input/Bmp/SUNFLOW.BMP

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

3
tests/Images/Input/Bmp/WARPD.BMP

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