Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Bitmap Decoder can now decode BitmapArray
  • Loading branch information
brianpopow committed Jun 10, 2019
commit e303d464e753fb2ba641b164239ba6061716b833
53 changes: 53 additions & 0 deletions src/ImageSharp/Formats/Bmp/BmpArrayFileHeader.cs
Original file line number Diff line number Diff line change
@@ -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];
}
}
}
22 changes: 18 additions & 4 deletions src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1186,11 +1186,25 @@ private void ReadFileHeader()
#endif
this.stream.Read(buffer, 0, BmpFileHeader.Size);

this.fileHeader = BmpFileHeader.Parse(buffer);

if (this.fileHeader.Type != BmpConstants.TypeMarkers.Bitmap)
short fileTypeMarker = BinaryPrimitives.ReadInt16LittleEndian(buffer);
switch (fileTypeMarker)
{
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:
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}'.");
}

break;
default:
BmpThrowHelper.ThrowNotSupportedException($"ImageSharp does not support this BMP file. File header bitmap type marker '{this.fileHeader.Type}'.");
break;
}
}

Expand Down
8 changes: 5 additions & 3 deletions src/ImageSharp/Formats/Bmp/BmpImageFormatDetector.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.

using System;
Expand All @@ -22,7 +22,9 @@ public IImageFormat DetectFormat(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);
}
}
}
}
8 changes: 4 additions & 4 deletions src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) Six Labors and contributors.
// Copyright (c) Six Labors and contributors.
// Licensed under the Apache License, Version 2.0.
using System;
using System.Buffers.Binary;
Expand Down Expand Up @@ -272,7 +272,7 @@ public BmpInfoHeader(
/// Parses the BITMAPCOREHEADER (BMP Version 2) consisting of the headerSize, width, height, planes, and bitsPerPixel fields (12 bytes).
/// </summary>
/// <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"/>
public static BmpInfoHeader ParseCore(ReadOnlySpan<byte> data)
{
Expand All @@ -289,7 +289,7 @@ public static BmpInfoHeader ParseCore(ReadOnlySpan<byte> data)
/// are 4 bytes instead of 2, resulting in 16 bytes total.
/// </summary>
/// <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"/>
public static BmpInfoHeader ParseOs22Short(ReadOnlySpan<byte> data)
{
Expand Down Expand Up @@ -457,4 +457,4 @@ internal void VerifyDimensions()
}
}
}
}
}
17 changes: 15 additions & 2 deletions tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -465,8 +465,7 @@ public void BmpDecoder_CanDecode_Os2v2XShortHeader<TPixel>(TestImageProvider<TPi
{
image.DebugSave(provider);

// TODO: Neither System.Drawing not MagickReferenceDecoder
// can correctly decode this file.
// TODO: Neither System.Drawing or MagickReferenceDecoder can correctly decode this file.
// image.CompareToOriginal(provider);
}
}
Expand All @@ -486,5 +485,19 @@ public void BmpDecoder_CanDecode_Os2v2Header<TPixel>(TestImageProvider<TPixel> p
// image.CompareToOriginal(provider, new MagickReferenceDecoder());
}
}

[Theory]
[WithFile(Os2BitmapArray, 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);
}
}
}
}
1 change: 1 addition & 0 deletions tests/ImageSharp.Tests/TestImages.cs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,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 Os2BitmapArray = "Bmp/9S.BMP";
public const string LessThanFullSizedPalette = "Bmp/pal8os2sp.bmp";
public const string Pal8Offset = "Bmp/pal8offs.bmp";
public const string OversizedPalette = "Bmp/pal8oversizepal.bmp";
Expand Down
Binary file added tests/Images/Input/Bmp/9S.BMP
Binary file not shown.