From ab8840bcb7998adc01f31361fc271faec0331767 Mon Sep 17 00:00:00 2001 From: James South Date: Tue, 28 Apr 2015 20:51:49 +0100 Subject: [PATCH] Import and cleanup bitmap format. Former-commit-id: b068ba10fe3487ab7c2a50b3733f7b1d563451bf Former-commit-id: c7b54a166c121a834289325fa94b33501a625e7e Former-commit-id: 845101507ecbd543bd8de8ba0e0fdebb471444d8 --- .../Formats/Bmp/BmpCompression.cs | 69 +++ src/ImageProcessor/Formats/Bmp/BmpDecoder.cs | 94 ++++ .../Formats/Bmp/BmpDecoderCore.cs | 422 ++++++++++++++++++ src/ImageProcessor/Formats/Bmp/BmpEncoder.cs | 180 ++++++++ .../Formats/Bmp/BmpFileHeader.cs | 55 +++ .../Formats/Bmp/BmpInfoHeader.cs | 91 ++++ .../Formats/Png/PngDecoderCore.cs | 7 +- src/ImageProcessor/Image.cs | 4 +- src/ImageProcessor/ImageProcessor.csproj | 6 + .../ImageProcessor.csproj.DotSettings | 1 + src/ImageProcessor/Settings.StyleCop | 1 + .../Formats/EncoderDecoderTests.cs | 1 + .../Formats/Bmp/Car.bmp.REMOVED.git-id | 1 + 13 files changed, 927 insertions(+), 5 deletions(-) create mode 100644 src/ImageProcessor/Formats/Bmp/BmpCompression.cs create mode 100644 src/ImageProcessor/Formats/Bmp/BmpDecoder.cs create mode 100644 src/ImageProcessor/Formats/Bmp/BmpDecoderCore.cs create mode 100644 src/ImageProcessor/Formats/Bmp/BmpEncoder.cs create mode 100644 src/ImageProcessor/Formats/Bmp/BmpFileHeader.cs create mode 100644 src/ImageProcessor/Formats/Bmp/BmpInfoHeader.cs create mode 100644 tests/ImageProcessor.Tests/TestImages/Formats/Bmp/Car.bmp.REMOVED.git-id diff --git a/src/ImageProcessor/Formats/Bmp/BmpCompression.cs b/src/ImageProcessor/Formats/Bmp/BmpCompression.cs new file mode 100644 index 000000000..2db5e7534 --- /dev/null +++ b/src/ImageProcessor/Formats/Bmp/BmpCompression.cs @@ -0,0 +1,69 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// +// +// Defines how the compression type of the image data +// in the bitmap file. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessor.Formats +{ + /// + /// Defines how the compression type of the image data + /// in the bitmap file. + /// + internal enum BmpCompression + { + /// + /// 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. + /// + RGB = 0, + + /// + /// 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. + /// + RLE8 = 1, + + /// + /// 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. + /// + RLE4 = 2, + + /// + /// 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. + /// + BitFields = 3, + + /// + /// The bitmap contains a JPG image. + /// Not supported at the moment. + /// + JPEG = 4, + + /// + /// The bitmap contains a PNG image. + /// Not supported at the moment. + /// + PNG = 5 + } +} diff --git a/src/ImageProcessor/Formats/Bmp/BmpDecoder.cs b/src/ImageProcessor/Formats/Bmp/BmpDecoder.cs new file mode 100644 index 000000000..01f5ba5c3 --- /dev/null +++ b/src/ImageProcessor/Formats/Bmp/BmpDecoder.cs @@ -0,0 +1,94 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// +// +// Image decoder for generating an image out of a Windows bitmap stream. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessor.Formats +{ + using System; + using System.IO; + + /// + /// Image decoder for generating an image out of a Windows bitmap stream. + /// + /// + /// Does not support the following formats at the moment: + /// + /// JPG + /// PNG + /// RLE4 + /// RLE8 + /// BitFields + /// + /// Formats will be supported in a later releases. We advise always + /// to use only 24 Bit Windows bitmaps. + /// + public class BmpDecoder : IImageDecoder + { + /// + /// Gets the size of the header for this image type. + /// + /// The size of the header. + public int HeaderSize + { + get + { + return 2; + } + } + + /// + /// Returns a value indicating whether the supports the specified + /// file header. + /// + /// The containing the file extension. + /// + /// True if the decoder supports the file extension; otherwise, false. + /// + 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); + } + + /// + /// Returns a value indicating whether the supports the specified + /// file header. + /// + /// The containing the file header. + /// + /// True if the decoder supports the file header; otherwise, false. + /// + public bool IsSupportedFileFormat(byte[] header) + { + bool isBmp = false; + if (header.Length >= 2) + { + isBmp = + header[0] == 0x42 && // B + header[1] == 0x4D; // M + } + + return isBmp; + } + + /// + /// Decodes the image from the specified stream to the . + /// + /// The to decode to. + /// The containing image data. + public void Decode(Image image, Stream stream) + { + new BmpDecoderCore().Decode(image, stream); + } + } +} diff --git a/src/ImageProcessor/Formats/Bmp/BmpDecoderCore.cs b/src/ImageProcessor/Formats/Bmp/BmpDecoderCore.cs new file mode 100644 index 000000000..4e45c92e8 --- /dev/null +++ b/src/ImageProcessor/Formats/Bmp/BmpDecoderCore.cs @@ -0,0 +1,422 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// +// +// Performs the bmp decoding operation. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessor.Formats +{ + using System; + using System.IO; + + /// + /// Performs the bmp decoding operation. + /// + internal class BmpDecoderCore + { + /// + /// The mask for the red part of the color for 16 bit rgb bitmaps. + /// + private const int Rgb16RMask = 0x00007C00; + + /// + /// The mask for the green part of the color for 16 bit rgb bitmaps. + /// + private const int Rgb16GMask = 0x000003E0; + + /// + /// The mask for the blue part of the color for 16 bit rgb bitmaps. + /// + private const int Rgb16BMask = 0x0000001F; + + #region Fields + + /// + /// The stream to decode from. + /// + private Stream currentStream; + + /// + /// The file header containing general information. + /// TODO: Why is this not used? We advance the stream but do not use the values parsed. + /// + private BmpFileHeader fileHeader; + + /// + /// The info header containing detailed information about the bitmap. + /// + private BmpInfoHeader infoHeader; + + #endregion + + #region IImageDecoder Members + + /// + /// Decodes the image from the specified this._stream and sets + /// the data to image. + /// + /// The image, where the data should be set to. + /// Cannot be null (Nothing in Visual Basic). + /// The this._stream, where the image should be + /// decoded from. Cannot be null (Nothing in Visual Basic). + /// + /// is null. + /// - or - + /// is null. + /// + 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); + } + } + + /// + /// Returns the y- value based on the given height. + /// + /// The y- value representing the current row. + /// The height of the bitmap. + /// The representing the inverted value. + private static int Invert(int y, int height) + { + int row; + + if (height > 0) + { + row = height - y - 1; + } + else + { + row = y; + } + + return row; + } + + /// + /// Reads the color palette from the stream. + /// + /// The image data to assign the palette to. + /// The containing the colors. + /// The width of the bitmap. + /// The height of the bitmap. + /// The number of bits per pixel. + 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; + } + } + } + } + + /// + /// Reads the 16 bit color palette from the stream + /// + /// The image data to assign the palette to. + /// The width of the bitmap. + /// The height of the bitmap. + 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; + } + } + } + + /// + /// Reads the 24 bit color palette from the stream + /// + /// The image data to assign the palette to. + /// The width of the bitmap. + /// The height of the bitmap. + 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; + } + } + } + + /// + /// Reads the 32 bit color palette from the stream + /// + /// The image data to assign the palette to. + /// The width of the bitmap. + /// The height of the bitmap. + 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? + } + } + } + + /// + /// Returns a containing the pixels for the current bitmap. + /// + /// The width of the bitmap. + /// The height. + /// The number of bytes per pixel. + /// The alignment of the pixels. + /// + /// The containing the pixels. + /// + 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; + } + + /// + /// Reads the from the stream. + /// + 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) + }; + } + + /// + /// Reads the from the stream. + /// + 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 + } +} diff --git a/src/ImageProcessor/Formats/Bmp/BmpEncoder.cs b/src/ImageProcessor/Formats/Bmp/BmpEncoder.cs new file mode 100644 index 000000000..4c2f77cfd --- /dev/null +++ b/src/ImageProcessor/Formats/Bmp/BmpEncoder.cs @@ -0,0 +1,180 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// +// +// Image encoder for writing an image to a stream as a Windows bitmap. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessor.Formats +{ + using System; + using System.IO; + + /// + /// Image encoder for writing an image to a stream as a Windows bitmap. + /// + /// The encoder can currently only write 24-bit rgb images to streams. + public class BmpEncoder : IImageEncoder + { + /// + /// Gets or sets the quality of output for images. + /// + /// Png is a lossless format so this is not used in this encoder. + public int Quality { get; set; } + + /// + /// Gets the default file extension for this encoder. + /// + public string Extension + { + get { return "BMP"; } + } + + /// + /// Returns a value indicating whether the supports the specified + /// file header. + /// + /// The containing the file extension. + /// + /// True if the decoder supports the file extension; otherwise, false. + /// + 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); + } + + /// + /// Encodes the image to the specified stream from the . + /// + /// The to encode from. + /// The to encode the image data to. + 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(); + } + + /// + /// Writes the pixel data to the binary stream. + /// + /// + /// The containing the stream to write to. + /// + /// + /// The containing pixel data. + /// + 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); + } + } + } + + /// + /// Writes the bitmap header data to the binary stream. + /// + /// + /// The containing the stream to write to. + /// + /// + /// The containing the header data. + /// + private static void WriteHeader(BinaryWriter writer, BmpFileHeader fileHeader) + { + writer.Write(fileHeader.Type); + writer.Write(fileHeader.FileSize); + writer.Write(fileHeader.Reserved); + writer.Write(fileHeader.Offset); + } + + /// + /// Writes the bitmap information to the binary stream. + /// + /// + /// The containing the stream to write to. + /// + /// + /// The containing the detailed information about the image. + /// + 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); + } + } +} diff --git a/src/ImageProcessor/Formats/Bmp/BmpFileHeader.cs b/src/ImageProcessor/Formats/Bmp/BmpFileHeader.cs new file mode 100644 index 000000000..f6a525da2 --- /dev/null +++ b/src/ImageProcessor/Formats/Bmp/BmpFileHeader.cs @@ -0,0 +1,55 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// +// +// Stores general information about the Bitmap file. +// +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessor.Formats +{ + /// + /// Stores general information about the Bitmap file. + /// + /// + /// + /// 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). + /// + internal class BmpFileHeader + { + /// + /// Defines of the data structure in the bitmap file. + /// + public const int Size = 14; + + /// + /// Gets or sets the Bitmap identifier. + /// The field used to identify the bitmap file: 0x42 0x4D + /// (Hex code points for B and M) + /// + public short Type { get; set; } + + /// + /// Gets or sets the size of the bitmap file in bytes. + /// + public int FileSize { get; set; } + + /// + /// Gets or sets any reserved data; actual value depends on the application + /// that creates the image. + /// + public int Reserved { get; set; } + + /// + /// Gets or sets the offset, i.e. starting address, of the byte where + /// the bitmap data can be found. + /// + public int Offset { get; set; } + } +} diff --git a/src/ImageProcessor/Formats/Bmp/BmpInfoHeader.cs b/src/ImageProcessor/Formats/Bmp/BmpInfoHeader.cs new file mode 100644 index 000000000..22886bf9b --- /dev/null +++ b/src/ImageProcessor/Formats/Bmp/BmpInfoHeader.cs @@ -0,0 +1,91 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright © James South and contributors. +// Licensed under the Apache License, Version 2.0. +// +// +// This block of bytes tells the application detailed information +// about the image, which will be used to display the image on +// the screen. +// +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace ImageProcessor.Formats +{ + /// + /// This block of bytes tells the application detailed information + /// about the image, which will be used to display the image on + /// the screen. + /// + /// + internal class BmpInfoHeader + { + /// + /// Defines of the data structure in the bitmap file. + /// + public const int Size = 40; + + /// + /// Gets or sets the size of this header (40 bytes) + /// + public int HeaderSize { get; set; } + + /// + /// Gets or sets the bitmap width in pixels (signed integer). + /// + public int Width { get; set; } + + /// + /// Gets or sets the bitmap height in pixels (signed integer). + /// + public int Height { get; set; } + + /// + /// Gets or sets the number of color planes being used. Must be set to 1. + /// + public short Planes { get; set; } + + /// + /// 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. + /// + public short BitsPerPixel { get; set; } + + /// + /// Gets or sets the compression method being used. + /// See the next table for a list of possible values. + /// + public BmpCompression Compression { get; set; } + + /// + /// 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. + /// + public int ImageSize { get; set; } + + /// + /// Gets or sets the horizontal resolution of the image. + /// (pixel per meter, signed integer) + /// + public int XPelsPerMeter { get; set; } + + /// + /// Gets or sets the vertical resolution of the image. + /// (pixel per meter, signed integer) + /// + public int YPelsPerMeter { get; set; } + + /// + /// Gets or sets the number of colors in the color palette, + /// or 0 to default to 2^n. + /// + public int ClrUsed { get; set; } + + /// + /// Gets or sets the number of important colors used, + /// or 0 when every color is important{ get; set; } generally ignored. + /// + public int ClrImportant { get; set; } + } +} diff --git a/src/ImageProcessor/Formats/Png/PngDecoderCore.cs b/src/ImageProcessor/Formats/Png/PngDecoderCore.cs index c7d81cd19..3ebe49bbb 100644 --- a/src/ImageProcessor/Formats/Png/PngDecoderCore.cs +++ b/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; /// - /// The stream to decode to. + /// The stream to decode from. /// 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)); } diff --git a/src/ImageProcessor/Image.cs b/src/ImageProcessor/Image.cs index 48fbffeb8..eeadc2a09 100644 --- a/src/ImageProcessor/Image.cs +++ b/src/ImageProcessor/Image.cs @@ -49,7 +49,7 @@ namespace ImageProcessor private static readonly Lazy> DefaultDecoders = new Lazy>(() => new List { - // new BmpDecoder(), + new BmpDecoder(), // new JpegDecoder(), new PngDecoder(), // new GifDecoder(), @@ -61,7 +61,7 @@ namespace ImageProcessor private static readonly Lazy> DefaultEncoders = new Lazy>(() => new List { - // new BmpEncoder(), + new BmpEncoder(), // new JpegEncoder(), new PngEncoder(), }); diff --git a/src/ImageProcessor/ImageProcessor.csproj b/src/ImageProcessor/ImageProcessor.csproj index eec1241b1..233fc5644 100644 --- a/src/ImageProcessor/ImageProcessor.csproj +++ b/src/ImageProcessor/ImageProcessor.csproj @@ -45,6 +45,12 @@ + + + + + + diff --git a/src/ImageProcessor/ImageProcessor.csproj.DotSettings b/src/ImageProcessor/ImageProcessor.csproj.DotSettings index 00ba06120..cd4ba94c1 100644 --- a/src/ImageProcessor/ImageProcessor.csproj.DotSettings +++ b/src/ImageProcessor/ImageProcessor.csproj.DotSettings @@ -5,6 +5,7 @@ True True True + True True True True diff --git a/src/ImageProcessor/Settings.StyleCop b/src/ImageProcessor/Settings.StyleCop index 62bf37157..0e93ffff4 100644 --- a/src/ImageProcessor/Settings.StyleCop +++ b/src/ImageProcessor/Settings.StyleCop @@ -5,6 +5,7 @@ cr EX png + rgb scanline scanlines tEXt diff --git a/tests/ImageProcessor.Tests/Formats/EncoderDecoderTests.cs b/tests/ImageProcessor.Tests/Formats/EncoderDecoderTests.cs index 77bb36d0e..7cb38d1c3 100644 --- a/tests/ImageProcessor.Tests/Formats/EncoderDecoderTests.cs +++ b/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) { diff --git a/tests/ImageProcessor.Tests/TestImages/Formats/Bmp/Car.bmp.REMOVED.git-id b/tests/ImageProcessor.Tests/TestImages/Formats/Bmp/Car.bmp.REMOVED.git-id new file mode 100644 index 000000000..e6132fd8c --- /dev/null +++ b/tests/ImageProcessor.Tests/TestImages/Formats/Bmp/Car.bmp.REMOVED.git-id @@ -0,0 +1 @@ +edd8ac1feb2c4649e6f5aa80af8d4cf33642a546 \ No newline at end of file