Browse Source

Import and cleanup bitmap format.

Former-commit-id: b068ba10fe3487ab7c2a50b3733f7b1d563451bf
Former-commit-id: c7b54a166c121a834289325fa94b33501a625e7e
Former-commit-id: 845101507ecbd543bd8de8ba0e0fdebb471444d8
pull/17/head
James South 11 years ago
parent
commit
ab8840bcb7
  1. 69
      src/ImageProcessor/Formats/Bmp/BmpCompression.cs
  2. 94
      src/ImageProcessor/Formats/Bmp/BmpDecoder.cs
  3. 422
      src/ImageProcessor/Formats/Bmp/BmpDecoderCore.cs
  4. 180
      src/ImageProcessor/Formats/Bmp/BmpEncoder.cs
  5. 55
      src/ImageProcessor/Formats/Bmp/BmpFileHeader.cs
  6. 91
      src/ImageProcessor/Formats/Bmp/BmpInfoHeader.cs
  7. 7
      src/ImageProcessor/Formats/Png/PngDecoderCore.cs
  8. 4
      src/ImageProcessor/Image.cs
  9. 6
      src/ImageProcessor/ImageProcessor.csproj
  10. 1
      src/ImageProcessor/ImageProcessor.csproj.DotSettings
  11. 1
      src/ImageProcessor/Settings.StyleCop
  12. 1
      tests/ImageProcessor.Tests/Formats/EncoderDecoderTests.cs
  13. 1
      tests/ImageProcessor.Tests/TestImages/Formats/Bmp/Car.bmp.REMOVED.git-id

69
src/ImageProcessor/Formats/Bmp/BmpCompression.cs

@ -0,0 +1,69 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="BmpCompression.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Defines how the compression type of the image data
// in the bitmap file.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats
{
/// <summary>
/// Defines how the compression type of the image data
/// in the bitmap file.
/// </summary>
internal enum BmpCompression
{
/// <summary>
/// Each image row has a multiple of four elements. If the
/// row has less elements, zeros will be added at the right side.
/// The format depends on the number of bits, stored in the info header.
/// If the number of bits are one, four or eight each pixel data is
/// a index to the palette. If the number of bits are sixteen,
/// twenty-four or thirty-two each pixel contains a color.
/// </summary>
RGB = 0,
/// <summary>
/// Two bytes are one data record. If the first byte is not zero, the
/// next two half bytes will be repeated as much as the value of the first byte.
/// 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>
RLE8 = 1,
/// <summary>
/// Two bytes are one data record. If the first byte is not zero, the
/// next byte will be repeated as much as the value of the first byte.
/// 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,
/// <summary>
/// Each image row has a multiple of four elements. If the
/// row has less elements, zeros will be added at the right side.
/// Not supported at the moment.
/// </summary>
BitFields = 3,
/// <summary>
/// The bitmap contains a JPG image.
/// Not supported at the moment.
/// </summary>
JPEG = 4,
/// <summary>
/// The bitmap contains a PNG image.
/// Not supported at the moment.
/// </summary>
PNG = 5
}
}

94
src/ImageProcessor/Formats/Bmp/BmpDecoder.cs

@ -0,0 +1,94 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="BmpDecoder.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Image decoder for generating an image out of a Windows bitmap stream.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats
{
using System;
using System.IO;
/// <summary>
/// Image decoder for generating an image out of a Windows bitmap stream.
/// </summary>
/// <remarks>
/// Does not support the following formats at the moment:
/// <list type="bullet">
/// <item>JPG</item>
/// <item>PNG</item>
/// <item>RLE4</item>
/// <item>RLE8</item>
/// <item>BitFields</item>
/// </list>
/// Formats will be supported in a later releases. We advise always
/// to use only 24 Bit Windows bitmaps.
/// </remarks>
public class BmpDecoder : IImageDecoder
{
/// <summary>
/// Gets the size of the header for this image type.
/// </summary>
/// <value>The size of the header.</value>
public int HeaderSize
{
get
{
return 2;
}
}
/// <summary>
/// Returns a value indicating whether the <see cref="IImageDecoder"/> supports the specified
/// file header.
/// </summary>
/// <param name="extension">The <see cref="string"/> containing the file extension.</param>
/// <returns>
/// True if the decoder supports the file extension; otherwise, false.
/// </returns>
public bool IsSupportedFileExtension(string extension)
{
Guard.NotNullOrEmpty(extension, "extension");
extension = extension.StartsWith(".") ? extension.Substring(1) : extension;
return extension.Equals("BMP", StringComparison.OrdinalIgnoreCase)
|| extension.Equals("DIP", StringComparison.OrdinalIgnoreCase);
}
/// <summary>
/// Returns a value indicating whether the <see cref="IImageDecoder"/> supports the specified
/// file header.
/// </summary>
/// <param name="header">The <see cref="T:byte[]"/> containing the file header.</param>
/// <returns>
/// True if the decoder supports the file header; otherwise, false.
/// </returns>
public bool IsSupportedFileFormat(byte[] header)
{
bool isBmp = false;
if (header.Length >= 2)
{
isBmp =
header[0] == 0x42 && // B
header[1] == 0x4D; // M
}
return isBmp;
}
/// <summary>
/// Decodes the image from the specified stream to the <see cref="ImageBase"/>.
/// </summary>
/// <param name="image">The <see cref="ImageBase"/> to decode to.</param>
/// <param name="stream">The <see cref="Stream"/> containing image data.</param>
public void Decode(Image image, Stream stream)
{
new BmpDecoderCore().Decode(image, stream);
}
}
}

422
src/ImageProcessor/Formats/Bmp/BmpDecoderCore.cs

@ -0,0 +1,422 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="BmpDecoderCore.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Performs the bmp decoding operation.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats
{
using System;
using System.IO;
/// <summary>
/// Performs the bmp decoding operation.
/// </summary>
internal class BmpDecoderCore
{
/// <summary>
/// The mask for the red part of the color for 16 bit rgb bitmaps.
/// </summary>
private const int Rgb16RMask = 0x00007C00;
/// <summary>
/// The mask for the green part of the color for 16 bit rgb bitmaps.
/// </summary>
private const int Rgb16GMask = 0x000003E0;
/// <summary>
/// The mask for the blue part of the color for 16 bit rgb bitmaps.
/// </summary>
private const int Rgb16BMask = 0x0000001F;
#region Fields
/// <summary>
/// The stream to decode from.
/// </summary>
private Stream currentStream;
/// <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;
/// <summary>
/// The info header containing detailed information about the bitmap.
/// </summary>
private BmpInfoHeader infoHeader;
#endregion
#region IImageDecoder Members
/// <summary>
/// Decodes the image from the specified this._stream and sets
/// the data to image.
/// </summary>
/// <param name="image">The image, where the data should be set to.
/// Cannot be null (Nothing in Visual Basic).</param>
/// <param name="stream">The this._stream, where the image should be
/// decoded from. Cannot be null (Nothing in Visual Basic).</param>
/// <exception cref="ArgumentNullException">
/// <para><paramref name="image"/> is null.</para>
/// <para>- or -</para>
/// <para><paramref name="stream"/> is null.</para>
/// </exception>
public void Decode(Image image, Stream stream)
{
this.currentStream = stream;
try
{
this.ReadFileHeader();
this.ReadInfoHeader();
int colorMapSize = -1;
if (this.infoHeader.ClrUsed == 0)
{
if (this.infoHeader.BitsPerPixel == 1 ||
this.infoHeader.BitsPerPixel == 4 ||
this.infoHeader.BitsPerPixel == 8)
{
colorMapSize = (int)Math.Pow(2, this.infoHeader.BitsPerPixel) * 4;
}
}
else
{
colorMapSize = this.infoHeader.ClrUsed * 4;
}
byte[] palette = null;
if (colorMapSize > 0)
{
if (colorMapSize > 255 * 4)
{
throw new ImageFormatException(
string.Format("Invalid bmp colormap size '{0}'", colorMapSize));
}
palette = new byte[colorMapSize];
this.currentStream.Read(palette, 0, colorMapSize);
}
if (this.infoHeader.Width > ImageBase.MaxWidth || this.infoHeader.Height > ImageBase.MaxHeight)
{
throw new ArgumentOutOfRangeException(
string.Format(
"The input bitmap '{0}x{1}' is bigger then the max allowed size '{2}x{3}'",
this.infoHeader.Width,
this.infoHeader.Height,
ImageBase.MaxWidth,
ImageBase.MaxHeight));
}
byte[] imageData = new byte[this.infoHeader.Width * this.infoHeader.Height * 4];
switch (this.infoHeader.Compression)
{
case BmpCompression.RGB:
if (this.infoHeader.HeaderSize != 40)
{
throw new ImageFormatException(
string.Format("Header Size value '{0}' is not valid.", this.infoHeader.HeaderSize));
}
if (this.infoHeader.BitsPerPixel == 32)
{
this.ReadRgb32(imageData, this.infoHeader.Width, this.infoHeader.Height);
}
else if (this.infoHeader.BitsPerPixel == 24)
{
this.ReadRgb24(imageData, this.infoHeader.Width, this.infoHeader.Height);
}
else if (this.infoHeader.BitsPerPixel == 16)
{
this.ReadRgb16(imageData, this.infoHeader.Width, this.infoHeader.Height);
}
else if (this.infoHeader.BitsPerPixel <= 8)
{
this.ReadRgbPalette(imageData, palette,
this.infoHeader.Width,
this.infoHeader.Height,
this.infoHeader.BitsPerPixel);
}
break;
default:
throw new NotSupportedException("Does not support this kind of bitmap files.");
}
image.SetPixels(this.infoHeader.Width, this.infoHeader.Height, imageData);
}
catch (IndexOutOfRangeException e)
{
throw new ImageFormatException("Bitmap does not have a valid format.", e);
}
}
/// <summary>
/// Returns the y- value based on the given height.
/// </summary>
/// <param name="y">The y- value representing the current row.</param>
/// <param name="height">The height of the bitmap.</param>
/// <returns>The <see cref="int"/> representing the inverted value.</returns>
private static int Invert(int y, int height)
{
int row;
if (height > 0)
{
row = height - y - 1;
}
else
{
row = y;
}
return row;
}
/// <summary>
/// Reads the color palette from the stream.
/// </summary>
/// <param name="imageData">The <see cref="T:byte[]"/> image data to assign the palette to.</param>
/// <param name="colors">The <see cref="T:byte[]"/> containing the colors.</param>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param>
/// <param name="bits">The number of bits per pixel.</param>
private void ReadRgbPalette(byte[] imageData, byte[] colors, int width, int height, int bits)
{
// Pixels per byte (bits per pixel)
int ppb = 8 / bits;
int arrayWidth = (width + ppb - 1) / ppb;
// Bit mask
int mask = 0xFF >> (8 - bits);
byte[] data = new byte[(arrayWidth * height)];
this.currentStream.Read(data, 0, data.Length);
// Rows are aligned on 4 byte boundaries
int alignment = arrayWidth % 4;
if (alignment != 0)
{
alignment = 4 - alignment;
}
for (int y = 0; y < height; y++)
{
int rowOffset = y * (arrayWidth + alignment);
for (int x = 0; x < arrayWidth; x++)
{
int offset = rowOffset + x;
// Revert the y value, because bitmaps are saved from down to top
int row = Invert(y, height);
int colOffset = x * ppb;
for (int shift = 0; shift < ppb && (colOffset + shift) < width; shift++)
{
int colorIndex = (data[offset] >> (8 - bits - (shift * bits))) & mask;
int arrayOffset = ((row * width) + (colOffset + shift)) * 4;
imageData[arrayOffset + 0] = colors[colorIndex * 4];
imageData[arrayOffset + 1] = colors[(colorIndex * 4) + 1];
imageData[arrayOffset + 2] = colors[(colorIndex * 4) + 2];
imageData[arrayOffset + 3] = 255;
}
}
}
}
/// <summary>
/// Reads the 16 bit color palette from the stream
/// </summary>
/// <param name="imageData">The <see cref="T:byte[]"/> image data to assign the palette to.</param>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param>
private void ReadRgb16(byte[] imageData, int width, int height)
{
const int ScaleR = 256 / 32;
const int ScaleG = 256 / 64;
int alignment;
byte[] data = this.GetImageArray(width, height, 2, out alignment);
for (int y = 0; y < height; y++)
{
int rowOffset = y * ((width * 2) + alignment);
// Revert the y value, because bitmaps are saved from down to top
int row = Invert(y, height);
for (int x = 0; x < width; x++)
{
int offset = rowOffset + (x * 2);
short temp = BitConverter.ToInt16(data, offset);
byte r = (byte)(((temp & Rgb16RMask) >> 11) * ScaleR);
byte g = (byte)(((temp & Rgb16GMask) >> 5) * ScaleG);
byte b = (byte)((temp & Rgb16BMask) * ScaleR);
int arrayOffset = ((row * width) + x) * 4;
imageData[arrayOffset + 0] = b;
imageData[arrayOffset + 1] = g;
imageData[arrayOffset + 2] = r;
imageData[arrayOffset + 3] = 255;
}
}
}
/// <summary>
/// Reads the 24 bit color palette from the stream
/// </summary>
/// <param name="imageData">The <see cref="T:byte[]"/> image data to assign the palette to.</param>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param>
private void ReadRgb24(byte[] imageData, int width, int height)
{
int alignment;
byte[] data = this.GetImageArray(width, height, 3, out alignment);
for (int y = 0; y < height; y++)
{
int rowOffset = y * ((width * 3) + alignment);
// Revert the y value, because bitmaps are saved from down to top
int row = Invert(y, height);
for (int x = 0; x < width; x++)
{
int offset = rowOffset + (x * 3);
int arrayOffset = ((row * width) + x) * 4;
imageData[arrayOffset + 0] = data[offset + 0];
imageData[arrayOffset + 1] = data[offset + 1];
imageData[arrayOffset + 2] = data[offset + 2];
imageData[arrayOffset + 3] = 255;
}
}
}
/// <summary>
/// Reads the 32 bit color palette from the stream
/// </summary>
/// <param name="imageData">The <see cref="T:byte[]"/> image data to assign the palette to.</param>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height of the bitmap.</param>
private void ReadRgb32(byte[] imageData, int width, int height)
{
int alignment;
byte[] data = this.GetImageArray(width, height, 4, out alignment);
for (int y = 0; y < height; y++)
{
int rowOffset = y * ((width * 4) + alignment);
// Revert the y value, because bitmaps are saved from down to top
int row = Invert(y, height);
for (int x = 0; x < width; x++)
{
int offset = rowOffset + (x * 4);
var arrayOffset = ((row * width) + x) * 4;
imageData[arrayOffset + 0] = data[offset + 0];
imageData[arrayOffset + 1] = data[offset + 1];
imageData[arrayOffset + 2] = data[offset + 2];
imageData[arrayOffset + 3] = 255; // Can we get alpha here?
}
}
}
/// <summary>
/// Returns a <see cref="T:byte[]"/> containing the pixels for the current bitmap.
/// </summary>
/// <param name="width">The width of the bitmap.</param>
/// <param name="height">The height.</param>
/// <param name="bytes">The number of bytes per pixel.</param>
/// <param name="alignment">The alignment of the pixels.</param>
/// <returns>
/// The <see cref="T:byte[]"/> containing the pixels.
/// </returns>
private byte[] GetImageArray(int width, int height, int bytes, out int alignment)
{
int dataWidth = width;
alignment = (width * bytes) % 4;
if (alignment != 0)
{
alignment = 4 - alignment;
}
int size = ((dataWidth * bytes) + alignment) * height;
byte[] data = new byte[size];
this.currentStream.Read(data, 0, size);
return data;
}
/// <summary>
/// Reads the <see cref="BmpInfoHeader"/> from the stream.
/// </summary>
private void ReadInfoHeader()
{
byte[] data = new byte[BmpInfoHeader.Size];
this.currentStream.Read(data, 0, BmpInfoHeader.Size);
this.infoHeader = new BmpInfoHeader
{
HeaderSize = BitConverter.ToInt32(data, 0),
Width = BitConverter.ToInt32(data, 4),
Height = BitConverter.ToInt32(data, 8),
Planes = BitConverter.ToInt16(data, 12),
BitsPerPixel = BitConverter.ToInt16(data, 14),
ImageSize = BitConverter.ToInt32(data, 20),
XPelsPerMeter = BitConverter.ToInt32(data, 24),
YPelsPerMeter = BitConverter.ToInt32(data, 28),
ClrUsed = BitConverter.ToInt32(data, 32),
ClrImportant = BitConverter.ToInt32(data, 36),
Compression = (BmpCompression)BitConverter.ToInt32(data, 16)
};
}
/// <summary>
/// Reads the <see cref="BmpFileHeader"/> from the stream.
/// </summary>
private void ReadFileHeader()
{
byte[] data = new byte[BmpFileHeader.Size];
this.currentStream.Read(data, 0, BmpFileHeader.Size);
this.fileHeader = new BmpFileHeader
{
Type = BitConverter.ToInt16(data, 0),
FileSize = BitConverter.ToInt32(data, 2),
Reserved = BitConverter.ToInt32(data, 6),
Offset = BitConverter.ToInt32(data, 10)
};
}
#endregion
}
}

180
src/ImageProcessor/Formats/Bmp/BmpEncoder.cs

@ -0,0 +1,180 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="BmpEncoder.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Image encoder for writing an image to a stream as a Windows bitmap.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats
{
using System;
using System.IO;
/// <summary>
/// Image encoder for writing an image to a stream as a Windows bitmap.
/// </summary>
/// <remarks>The encoder can currently only write 24-bit rgb images to streams.</remarks>
public class BmpEncoder : IImageEncoder
{
/// <summary>
/// Gets or sets the quality of output for images.
/// </summary>
/// <remarks>Png is a lossless format so this is not used in this encoder.</remarks>
public int Quality { get; set; }
/// <summary>
/// Gets the default file extension for this encoder.
/// </summary>
public string Extension
{
get { return "BMP"; }
}
/// <summary>
/// Returns a value indicating whether the <see cref="IImageEncoder"/> supports the specified
/// file header.
/// </summary>
/// <param name="extension">The <see cref="string"/> containing the file extension.</param>
/// <returns>
/// True if the decoder supports the file extension; otherwise, false.
/// </returns>
public bool IsSupportedFileExtension(string extension)
{
Guard.NotNullOrEmpty(extension, "extension");
extension = extension.StartsWith(".") ? extension.Substring(1) : extension;
return extension.Equals("BMP", StringComparison.OrdinalIgnoreCase)
|| extension.Equals("DIP", StringComparison.OrdinalIgnoreCase);
}
/// <summary>
/// Encodes the image to the specified stream from the <see cref="ImageBase"/>.
/// </summary>
/// <param name="image">The <see cref="ImageBase"/> to encode from.</param>
/// <param name="stream">The <see cref="Stream"/> to encode the image data to.</param>
public void Encode(ImageBase image, Stream stream)
{
Guard.NotNull(image, "image");
Guard.NotNull(stream, "stream");
int rowWidth = image.Width;
int amount = (image.Width * 3) % 4;
if (amount != 0)
{
rowWidth += 4 - amount;
}
BinaryWriter writer = new BinaryWriter(stream);
BmpFileHeader fileHeader = new BmpFileHeader
{
Type = 19778,
Offset = 54,
FileSize = 54 + (image.Height * rowWidth * 3)
};
WriteHeader(writer, fileHeader);
BmpInfoHeader infoHeader = new BmpInfoHeader
{
HeaderSize = 40,
Height = image.Height,
Width = image.Width,
BitsPerPixel = 24,
Planes = 1,
Compression = BmpCompression.RGB,
ImageSize = image.Height * rowWidth * 3,
ClrUsed = 0,
ClrImportant = 0
};
WriteInfo(writer, infoHeader);
WriteImage(writer, image);
writer.Flush();
}
/// <summary>
/// Writes the pixel data to the binary stream.
/// </summary>
/// <param name="writer">
/// The <see cref="BinaryWriter"/> containing the stream to write to.
/// </param>
/// <param name="image">
/// The <see cref="ImageBase"/> containing pixel data.
/// </param>
private static void WriteImage(BinaryWriter writer, ImageBase image)
{
int amount = (image.Width * 3) % 4;
if (amount != 0)
{
amount = 4 - amount;
}
byte[] data = image.Pixels;
for (int y = image.Height - 1; y >= 0; y--)
{
for (int x = 0; x < image.Width; x++)
{
int offset = ((y * image.Width) + x) * 4;
writer.Write(data[offset + 0]);
writer.Write(data[offset + 1]);
writer.Write(data[offset + 2]);
}
for (int i = 0; i < amount; i++)
{
writer.Write((byte)0);
}
}
}
/// <summary>
/// Writes the bitmap header data to the binary stream.
/// </summary>
/// <param name="writer">
/// The <see cref="BinaryWriter"/> containing the stream to write to.
/// </param>
/// <param name="fileHeader">
/// The <see cref="BmpFileHeader"/> containing the header data.
/// </param>
private static void WriteHeader(BinaryWriter writer, BmpFileHeader fileHeader)
{
writer.Write(fileHeader.Type);
writer.Write(fileHeader.FileSize);
writer.Write(fileHeader.Reserved);
writer.Write(fileHeader.Offset);
}
/// <summary>
/// Writes the bitmap information to the binary stream.
/// </summary>
/// <param name="writer">
/// The <see cref="BinaryWriter"/> containing the stream to write to.
/// </param>
/// <param name="infoHeader">
/// The <see cref="BmpFileHeader"/> containing the detailed information about the image.
/// </param>
private static void WriteInfo(BinaryWriter writer, BmpInfoHeader infoHeader)
{
writer.Write(infoHeader.HeaderSize);
writer.Write(infoHeader.Width);
writer.Write(infoHeader.Height);
writer.Write(infoHeader.Planes);
writer.Write(infoHeader.BitsPerPixel);
writer.Write((int)infoHeader.Compression);
writer.Write(infoHeader.ImageSize);
writer.Write(infoHeader.XPelsPerMeter);
writer.Write(infoHeader.YPelsPerMeter);
writer.Write(infoHeader.ClrUsed);
writer.Write(infoHeader.ClrImportant);
}
}
}

55
src/ImageProcessor/Formats/Bmp/BmpFileHeader.cs

@ -0,0 +1,55 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="BmpFileHeader.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// Stores general information about the Bitmap file.
// <see href="https://en.wikipedia.org/wiki/BMP_file_format" />
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats
{
/// <summary>
/// Stores general information about the Bitmap file.
/// <see href="https://en.wikipedia.org/wiki/BMP_file_format" />
/// </summary>
/// <remarks>
/// The first two bytes of the Bitmap file format
/// (thus the Bitmap header) are stored in big-endian order.
/// All of the other integer values are stored in little-endian format
/// (i.e. least-significant byte first).
/// </remarks>
internal class BmpFileHeader
{
/// <summary>
/// Defines of the data structure in the bitmap file.
/// </summary>
public const int Size = 14;
/// <summary>
/// Gets or sets the Bitmap identifier.
/// The field used to identify the bitmap file: 0x42 0x4D
/// (Hex code points for B and M)
/// </summary>
public short Type { get; set; }
/// <summary>
/// Gets or sets the size of the bitmap file in bytes.
/// </summary>
public int FileSize { get; set; }
/// <summary>
/// Gets or sets any reserved data; actual value depends on the application
/// that creates the image.
/// </summary>
public int Reserved { get; set; }
/// <summary>
/// Gets or sets the offset, i.e. starting address, of the byte where
/// the bitmap data can be found.
/// </summary>
public int Offset { get; set; }
}
}

91
src/ImageProcessor/Formats/Bmp/BmpInfoHeader.cs

@ -0,0 +1,91 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="BmpInfoHeader.cs" company="James South">
// Copyright © James South and contributors.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// This block of bytes tells the application detailed information
// about the image, which will be used to display the image on
// the screen.
// <see href="https://en.wikipedia.org/wiki/BMP_file_format" />
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Formats
{
/// <summary>
/// This block of bytes tells the application detailed information
/// about the image, which will be used to display the image on
/// the screen.
/// <see href="https://en.wikipedia.org/wiki/BMP_file_format"/>
/// </summary>
internal class BmpInfoHeader
{
/// <summary>
/// Defines of the data structure in the bitmap file.
/// </summary>
public const int Size = 40;
/// <summary>
/// Gets or sets the size of this header (40 bytes)
/// </summary>
public int HeaderSize { get; set; }
/// <summary>
/// Gets or sets the bitmap width in pixels (signed integer).
/// </summary>
public int Width { get; set; }
/// <summary>
/// Gets or sets the bitmap height in pixels (signed integer).
/// </summary>
public int Height { get; set; }
/// <summary>
/// Gets or sets the number of color planes being used. Must be set to 1.
/// </summary>
public short Planes { get; set; }
/// <summary>
/// Gets or sets the number of bits per pixel, which is the color depth of the image.
/// Typical values are 1, 4, 8, 16, 24 and 32.
/// </summary>
public short BitsPerPixel { get; set; }
/// <summary>
/// Gets or sets the compression method being used.
/// See the next table for a list of possible values.
/// </summary>
public BmpCompression Compression { get; set; }
/// <summary>
/// Gets or sets the image size. This is the size of the raw bitmap data (see below),
/// and should not be confused with the file size.
/// </summary>
public int ImageSize { get; set; }
/// <summary>
/// Gets or sets the horizontal resolution of the image.
/// (pixel per meter, signed integer)
/// </summary>
public int XPelsPerMeter { get; set; }
/// <summary>
/// Gets or sets the vertical resolution of the image.
/// (pixel per meter, signed integer)
/// </summary>
public int YPelsPerMeter { get; set; }
/// <summary>
/// Gets or sets the number of colors in the color palette,
/// or 0 to default to 2^n.
/// </summary>
public int ClrUsed { get; set; }
/// <summary>
/// Gets or sets the number of important colors used,
/// or 0 when every color is important{ get; set; } generally ignored.
/// </summary>
public int ClrImportant { get; set; }
}
}

7
src/ImageProcessor/Formats/Png/PngDecoderCore.cs

@ -14,6 +14,7 @@ namespace ImageProcessor.Formats
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using ICSharpCode.SharpZipLib.Checksums;
using ICSharpCode.SharpZipLib.Zip.Compression.Streams;
@ -40,7 +41,7 @@ namespace ImageProcessor.Formats
private Image currentImage;
/// <summary>
/// The stream to decode to.
/// The stream to decode from.
/// </summary>
private Stream currentStream;
@ -360,8 +361,8 @@ namespace ImageProcessor.Formats
}
}
string name = System.Text.Encoding.Unicode.GetString(data, 0, zeroIndex);
string value = System.Text.Encoding.Unicode.GetString(data, zeroIndex + 1, data.Length - zeroIndex - 1);
string name = Encoding.Unicode.GetString(data, 0, zeroIndex);
string value = Encoding.Unicode.GetString(data, zeroIndex + 1, data.Length - zeroIndex - 1);
this.currentImage.Properties.Add(new ImageProperty(name, value));
}

4
src/ImageProcessor/Image.cs

@ -49,7 +49,7 @@ namespace ImageProcessor
private static readonly Lazy<List<IImageDecoder>> DefaultDecoders =
new Lazy<List<IImageDecoder>>(() => new List<IImageDecoder>
{
// new BmpDecoder(),
new BmpDecoder(),
// new JpegDecoder(),
new PngDecoder(),
// new GifDecoder(),
@ -61,7 +61,7 @@ namespace ImageProcessor
private static readonly Lazy<List<IImageEncoder>> DefaultEncoders =
new Lazy<List<IImageEncoder>>(() => new List<IImageEncoder>
{
// new BmpEncoder(),
new BmpEncoder(),
// new JpegEncoder(),
new PngEncoder(),
});

6
src/ImageProcessor/ImageProcessor.csproj

@ -45,6 +45,12 @@
<Compile Include="Common\Extensions\ByteExtensions.cs" />
<Compile Include="Common\Extensions\ComparableExtensions.cs" />
<Compile Include="Common\Helpers\Utils.cs" />
<Compile Include="Formats\Bmp\BmpCompression.cs" />
<Compile Include="Formats\Bmp\BmpFileHeader.cs" />
<Compile Include="Formats\Bmp\BmpInfoHeader.cs" />
<Compile Include="Formats\Bmp\BmpDecoder.cs" />
<Compile Include="Formats\Bmp\BmpDecoderCore.cs" />
<Compile Include="Formats\Bmp\BmpEncoder.cs" />
<Compile Include="Formats\Png\PngDecoder.cs" />
<Compile Include="Formats\Png\PngDecoderCore.cs" />
<Compile Include="Formats\Png\PngEncoder.cs" />

1
src/ImageProcessor/ImageProcessor.csproj.DotSettings

@ -5,6 +5,7 @@
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=common_005Cextensions/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=common_005Chelpers/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=encoders_005Cgif/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=formats_005Cbmp/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=formats_005Cgif/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=formats_005Cjpg/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=formats_005Cpng/@EntryIndexedValue">True</s:Boolean>

1
src/ImageProcessor/Settings.StyleCop

@ -5,6 +5,7 @@
<Value>cr</Value>
<Value>EX</Value>
<Value>png</Value>
<Value>rgb</Value>
<Value>scanline</Value>
<Value>scanlines</Value>
<Value>tEXt</Value>

1
tests/ImageProcessor.Tests/Formats/EncoderDecoderTests.cs

@ -15,6 +15,7 @@
//[InlineData("TestImages/Portrait.png")]
//[InlineData("TestImages/Backdrop.jpg")]
//[InlineData("TestImages/Windmill.gif")]
[InlineData("../../TestImages/Formats/Bmp/Car.bmp")]
[InlineData("../../TestImages/Formats/Png/cmyk.png")]
public void DecodeThenEncodeImageFromStreamShouldSucceed(string filename)
{

1
tests/ImageProcessor.Tests/TestImages/Formats/Bmp/Car.bmp.REMOVED.git-id

@ -0,0 +1 @@
edd8ac1feb2c4649e6f5aa80af8d4cf33642a546
Loading…
Cancel
Save