Browse Source

Add parsing bitmap V5 header

pull/2108/head
Brian Popow 4 years ago
parent
commit
f74dbac80b
  1. 37
      src/ImageSharp/Formats/Bmp/BmpColorSpace.cs
  2. 12
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  3. 5
      src/ImageSharp/Formats/Bmp/BmpFileHeader.cs
  4. 96
      src/ImageSharp/Formats/Bmp/BmpInfoHeader.cs
  5. 37
      src/ImageSharp/Formats/Bmp/BmpRenderingIntent.cs

37
src/ImageSharp/Formats/Bmp/BmpColorSpace.cs

@ -0,0 +1,37 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Bmp
{
/// <summary>
/// Enum for the different color spaces.
/// </summary>
internal enum BmpColorSpace
{
/// <summary>
/// This value implies that endpoints and gamma values are given in the appropriate fields.
/// </summary>
LCS_CALIBRATED_RGB = 0,
/// <summary>
/// The Windows default color space ('Win ').
/// </summary>
LCS_WINDOWS_COLOR_SPACE = 1466527264,
/// <summary>
/// Specifies that the bitmap is in sRGB color space ('sRGB').
/// </summary>
LCS_sRGB = 1934772034,
/// <summary>
/// This value indicates that bV5ProfileData points to the file name of the profile to use (gamma and endpoints values are ignored).
/// </summary>
PROFILE_LINKED = 1279872587,
/// <summary>
/// This value indicates that bV5ProfileData points to a memory buffer that contains the profile to be used (gamma and endpoints values are ignored).
/// </summary>
PROFILE_EMBEDDED = 1296188740
}
}

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

@ -1271,12 +1271,18 @@ namespace SixLabors.ImageSharp.Formats.Bmp
infoHeaderType = BmpInfoHeaderType.Os2Version2;
this.infoHeader = BmpInfoHeader.ParseOs2Version2(buffer);
}
else if (headerSize >= BmpInfoHeader.SizeV4)
else if (headerSize == BmpInfoHeader.SizeV4)
{
// >= 108 bytes
infoHeaderType = headerSize == BmpInfoHeader.SizeV4 ? BmpInfoHeaderType.WinVersion4 : BmpInfoHeaderType.WinVersion5;
// == 108 bytes
infoHeaderType = BmpInfoHeaderType.WinVersion4;
this.infoHeader = BmpInfoHeader.ParseV4(buffer);
}
else if (headerSize > BmpInfoHeader.SizeV4)
{
// > 108 bytes
infoHeaderType = BmpInfoHeaderType.WinVersion5;
this.infoHeader = BmpInfoHeader.ParseV5(buffer);
}
else
{
BmpThrowHelper.ThrowNotSupportedException($"ImageSharp does not support this BMP file. HeaderSize '{headerSize}'.");

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

@ -57,10 +57,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary>
public int Offset { get; }
public static BmpFileHeader Parse(Span<byte> data)
{
return MemoryMarshal.Cast<byte, BmpFileHeader>(data)[0];
}
public static BmpFileHeader Parse(Span<byte> data) => MemoryMarshal.Cast<byte, BmpFileHeader>(data)[0];
public void WriteTo(Span<byte> buffer)
{

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

@ -82,7 +82,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int greenMask = 0,
int blueMask = 0,
int alphaMask = 0,
int csType = 0,
BmpColorSpace csType = 0,
int redX = 0,
int redY = 0,
int redZ = 0,
@ -94,7 +94,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
int blueZ = 0,
int gammeRed = 0,
int gammeGreen = 0,
int gammeBlue = 0)
int gammeBlue = 0,
BmpRenderingIntent intent = BmpRenderingIntent.Invalid,
int profileData = 0,
int profileSize = 0,
int reserved = 0)
{
this.HeaderSize = headerSize;
this.Width = width;
@ -124,6 +128,10 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.GammaRed = gammeRed;
this.GammaGreen = gammeGreen;
this.GammaBlue = gammeBlue;
this.Intent = intent;
this.ProfileData = profileData;
this.ProfileSize = profileSize;
this.Reserved = reserved;
}
/// <summary>
@ -211,7 +219,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <summary>
/// Gets or sets the Color space type. Not used yet.
/// </summary>
public int CsType { get; set; }
public BmpColorSpace CsType { get; set; }
/// <summary>
/// Gets or sets the X coordinate of red endpoint. Not used yet.
@ -273,21 +281,38 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary>
public int GammaBlue { get; set; }
/// <summary>
/// Gets or sets the rendering intent for bitmap.
/// </summary>
public BmpRenderingIntent Intent { get; set; }
/// <summary>
/// Gets or sets the offset, in bytes, from the beginning of the BITMAPV5HEADER structure to the start of the profile data.
/// </summary>
public int ProfileData { get; set; }
/// <summary>
/// Gets or sets the size, in bytes, of embedded profile data.
/// </summary>
public int ProfileSize { get; set; }
/// <summary>
/// Gets or sets the reserved value.
/// </summary>
public int Reserved { get; set; }
/// <summary>
/// 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>The parsed header.</returns>
/// <seealso href="https://msdn.microsoft.com/en-us/library/windows/desktop/dd183372.aspx"/>
public static BmpInfoHeader ParseCore(ReadOnlySpan<byte> data)
{
return new BmpInfoHeader(
public static BmpInfoHeader ParseCore(ReadOnlySpan<byte> data) => new(
headerSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)),
width: BinaryPrimitives.ReadUInt16LittleEndian(data.Slice(4, 2)),
height: BinaryPrimitives.ReadUInt16LittleEndian(data.Slice(6, 2)),
planes: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(8, 2)),
bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(10, 2)));
}
/// <summary>
/// Parses a short variant of the OS22XBITMAPHEADER. It is identical to the BITMAPCOREHEADER, except that the width and height
@ -296,15 +321,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <param name="data">The data to parse.</param>
/// <returns>The parsed header.</returns>
/// <seealso href="https://www.fileformat.info/format/os2bmp/egff.htm"/>
public static BmpInfoHeader ParseOs22Short(ReadOnlySpan<byte> data)
{
return new BmpInfoHeader(
public static BmpInfoHeader ParseOs22Short(ReadOnlySpan<byte> data) => new(
headerSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)),
width: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)),
height: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)),
planes: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(12, 2)),
bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(14, 2)));
}
/// <summary>
/// Parses the full BMP Version 3 BITMAPINFOHEADER header (40 bytes).
@ -312,9 +334,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <param name="data">The data to parse.</param>
/// <returns>The parsed header.</returns>
/// <seealso href="http://www.fileformat.info/format/bmp/egff.htm"/>
public static BmpInfoHeader ParseV3(ReadOnlySpan<byte> data)
{
return new BmpInfoHeader(
public static BmpInfoHeader ParseV3(ReadOnlySpan<byte> data) => new(
headerSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)),
width: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)),
height: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)),
@ -326,7 +346,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
yPelsPerMeter: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(28, 4)),
clrUsed: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(32, 4)),
clrImportant: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(36, 4)));
}
/// <summary>
/// Special case of the BITMAPINFOHEADER V3 used by adobe where the color bitmasks are part of the info header instead of following it.
@ -336,9 +355,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <param name="withAlpha">Indicates, if the alpha bitmask is present.</param>
/// <returns>The parsed header.</returns>
/// <seealso href="https://forums.adobe.com/message/3272950#3272950"/>
public static BmpInfoHeader ParseAdobeV3(ReadOnlySpan<byte> data, bool withAlpha = true)
{
return new BmpInfoHeader(
public static BmpInfoHeader ParseAdobeV3(ReadOnlySpan<byte> data, bool withAlpha = true) => new(
headerSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)),
width: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)),
height: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)),
@ -354,7 +371,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
greenMask: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(44, 4)),
blueMask: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(48, 4)),
alphaMask: withAlpha ? BinaryPrimitives.ReadInt32LittleEndian(data.Slice(52, 4)) : 0);
}
/// <summary>
/// Parses a OS/2 version 2 bitmap header (64 bytes). Only the first 40 bytes are parsed which are
@ -413,11 +429,47 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <param name="data">The data to parse.</param>
/// <returns>The parsed header.</returns>
/// <seealso href="http://www.fileformat.info/format/bmp/egff.htm"/>
public static BmpInfoHeader ParseV4(ReadOnlySpan<byte> data)
public static BmpInfoHeader ParseV4(ReadOnlySpan<byte> data) => new(
headerSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(0, 4)),
width: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(4, 4)),
height: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(8, 4)),
planes: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(12, 2)),
bitsPerPixel: BinaryPrimitives.ReadInt16LittleEndian(data.Slice(14, 2)),
compression: (BmpCompression)BinaryPrimitives.ReadInt32LittleEndian(data.Slice(16, 4)),
imageSize: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(20, 4)),
xPelsPerMeter: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(24, 4)),
yPelsPerMeter: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(28, 4)),
clrUsed: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(32, 4)),
clrImportant: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(36, 4)),
redMask: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(40, 4)),
greenMask: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(44, 4)),
blueMask: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(48, 4)),
alphaMask: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(52, 4)),
csType: (BmpColorSpace)BinaryPrimitives.ReadInt32LittleEndian(data.Slice(56, 4)),
redX: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(60, 4)),
redY: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(64, 4)),
redZ: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(68, 4)),
greenX: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(72, 4)),
greenY: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(76, 4)),
greenZ: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(80, 4)),
blueX: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(84, 4)),
blueY: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(88, 4)),
blueZ: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(92, 4)),
gammeRed: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(96, 4)),
gammeGreen: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(100, 4)),
gammeBlue: BinaryPrimitives.ReadInt32LittleEndian(data.Slice(104, 4)));
/// <summary>
/// Parses the full BMP Version 5 BITMAPINFOHEADER header (124 bytes).
/// </summary>
/// <param name="data">The data to parse.</param>
/// <returns>The parsed header.</returns>
/// <seealso href="https://docs.microsoft.com/de-de/windows/win32/api/wingdi/ns-wingdi-bitmapv5header?redirectedfrom=MSDN"/>
public static BmpInfoHeader ParseV5(ReadOnlySpan<byte> data)
{
if (data.Length < SizeV4)
if (data.Length < SizeV5)
{
throw new ArgumentException(nameof(data), $"Must be {SizeV4} bytes. Was {data.Length} bytes.");
throw new ArgumentException(nameof(data), $"Must be {SizeV5} bytes. Was {data.Length} bytes.");
}
return MemoryMarshal.Cast<byte, BmpInfoHeader>(data)[0];

37
src/ImageSharp/Formats/Bmp/BmpRenderingIntent.cs

@ -0,0 +1,37 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
// ReSharper disable InconsistentNaming
namespace SixLabors.ImageSharp.Formats.Bmp
{
/// <summary>
/// Enum for the different rendering intent's.
/// </summary>
internal enum BmpRenderingIntent
{
/// <summary>
/// Invalid default value.
/// </summary>
Invalid = 0,
/// <summary>
/// TMaintains saturation. Used for business charts and other situations in which undithered colors are required.
/// </summary>
LCS_GM_BUSINESS = 1,
/// <summary>
/// Maintains colorimetric match. Used for graphic designs and named colors.
/// </summary>
LCS_GM_GRAPHICS = 2,
/// <summary>
/// Maintains contrast. Used for photographs and natural images.
/// </summary>
LCS_GM_IMAGES = 4,
/// <summary>
/// Maintains the white point. Matches the colors to their nearest color in the destination gamut.
/// </summary>
LCS_GM_ABS_COLORIMETRIC = 8,
}
}
Loading…
Cancel
Save