diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 4fda80d72..39bab442f 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -148,7 +148,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp + $"bigger then the max allowed size '{int.MaxValue}x{int.MaxValue}'"); } - Image image = new Image(this.configuration, this.infoHeader.Width, this.infoHeader.Height); + var image = new Image(this.configuration, this.infoHeader.Width, this.infoHeader.Height); using (PixelAccessor pixels = image.Lock()) { switch (this.infoHeader.Compression) @@ -196,6 +196,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// The height of the bitmap. /// Whether the bitmap is inverted. /// The representing the inverted value. + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static int Invert(int y, int height, bool inverted) { int row; @@ -234,7 +235,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// Looks up color values and builds the image from de-compressed RLE8 data. - /// Compresssed RLE8 stream is uncompressed by + /// Compresssed RLE8 stream is uncompressed by /// /// The pixel format. /// The to assign the palette to. @@ -246,19 +247,21 @@ namespace SixLabors.ImageSharp.Formats.Bmp where TPixel : struct, IPixel { var color = default(TPixel); - var rgba = default(Rgba32); + var rgba = new Rgba32(0, 0, 0, 255); - using (var buffer = Buffer.CreateClean(width * height)) + using (var buffer = Buffer2D.CreateClean(width, height)) { this.UncompressRle8(width, buffer); for (int y = 0; y < height; y++) { int newY = Invert(y, height, inverted); + Span bufferRow = buffer.GetRowSpan(y); Span pixelRow = pixels.GetRowSpan(newY); + for (int x = 0; x < width; x++) { - rgba.Bgr = Unsafe.As(ref colors[buffer[(y * width) + x] * 4]); + rgba.Bgr = Unsafe.As(ref colors[bufferRow[x] * 4]); color.PackFromRgba32(rgba); pixelRow[x] = color; } @@ -276,7 +279,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// The width of the bitmap. /// Buffer for uncompressed data. - private void UncompressRle8(int w, Buffer buffer) + private void UncompressRle8(int w, Span buffer) { byte[] cmd = new byte[2]; int count = 0; @@ -368,35 +371,36 @@ namespace SixLabors.ImageSharp.Formats.Bmp padding = 4 - padding; } - byte[] row = new byte[arrayWidth + padding]; - TPixel color = default(TPixel); - - Rgba32 rgba = default(Rgba32); - - for (int y = 0; y < height; y++) + using (var row = Buffer.CreateClean(arrayWidth + padding)) { - int newY = Invert(y, height, inverted); - this.currentStream.Read(row, 0, row.Length); - int offset = 0; - Span pixelRow = pixels.GetRowSpan(y); + var color = default(TPixel); + var rgba = new Rgba32(0, 0, 0, 255); - // TODO: Could use PixelOperations here! - for (int x = 0; x < arrayWidth; x++) + for (int y = 0; y < height; y++) { - int colOffset = x * ppb; + int newY = Invert(y, height, inverted); + this.currentStream.Read(row.Array, 0, row.Length); + int offset = 0; + Span pixelRow = pixels.GetRowSpan(newY); - for (int shift = 0; shift < ppb && (x + shift) < width; shift++) + // TODO: Could use PixelOperations here! + for (int x = 0; x < arrayWidth; x++) { - int colorIndex = ((row[offset] >> (8 - bits - (shift * bits))) & mask) * 4; - int newX = colOffset + shift; + int colOffset = x * ppb; - // Stored in b-> g-> r order. - rgba.Bgr = Unsafe.As(ref colors[colorIndex]); - color.PackFromRgba32(rgba); - pixelRow[newX] = color; - } + for (int shift = 0; shift < ppb && (x + shift) < width; shift++) + { + int colorIndex = ((row[offset] >> (8 - bits - (shift * bits))) & mask) * 4; + int newX = colOffset + shift; - offset++; + // Stored in b-> g-> r order. + rgba.Bgr = Unsafe.As(ref colors[colorIndex]); + color.PackFromRgba32(rgba); + pixelRow[newX] = color; + } + + offset++; + } } } } @@ -417,10 +421,10 @@ namespace SixLabors.ImageSharp.Formats.Bmp const int ScaleG = 4; // 256/64 const int ComponentCount = 2; - TPixel color = default(TPixel); - Rgba32 rgba = new Rgba32(0, 0, 0, 255); + var color = default(TPixel); + var rgba = new Rgba32(0, 0, 0, 255); - using (PixelArea row = new PixelArea(width, ComponentOrder.Xyz)) + using (var row = new PixelArea(width, ComponentOrder.Xyz)) { for (int y = 0; y < height; y++) { @@ -459,7 +463,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp where TPixel : struct, IPixel { int padding = CalculatePadding(width, 3); - using (PixelArea row = new PixelArea(width, ComponentOrder.Zyx, padding)) + using (var row = new PixelArea(width, ComponentOrder.Zyx, padding)) { for (int y = 0; y < height; y++) { @@ -483,7 +487,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp where TPixel : struct, IPixel { int padding = CalculatePadding(width, 4); - using (PixelArea row = new PixelArea(width, ComponentOrder.Zyxw, padding)) + using (var row = new PixelArea(width, ComponentOrder.Zyxw, padding)) { for (int y = 0; y < height; y++) { diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index 8a50b760e..8f4b05108 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -13,7 +13,7 @@ namespace SixLabors.ImageSharp.Tests public class BmpDecoderTests : FileTestBase { [Theory] - [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Rgb24)] + [WithFileCollection(nameof(AllBmpFiles), PixelTypes.Rgba32)] public void DecodeBmp(TestImageProvider provider) where TPixel : struct, IPixel { diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 948f519d4..8be892233 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -129,14 +129,19 @@ namespace SixLabors.ImageSharp.Tests public static class Bmp { + // Note: The inverted images have been generated by altering the BitmapInfoHeader using a hex editor. + // As such, the expected pixel output will be the reverse of the unaltered equivalent images. public const string Car = "Bmp/Car.bmp"; public const string F = "Bmp/F.bmp"; public const string NegHeight = "Bmp/neg_height.bmp"; public const string CoreHeader = "Bmp/BitmapCoreHeaderQR.bmp"; public const string V5Header = "Bmp/BITMAPV5HEADER.bmp"; public const string RLE = "Bmp/RunLengthEncoded.bmp"; + public const string RLEInverted = "Bmp/RunLengthEncoded-inverted.bmp"; + public const string Bit8 = "Bmp/test8.bmp"; + public const string Bit8Inverted = "Bmp/test8-inverted.bmp"; - public static readonly string[] All = { Car, F, NegHeight, CoreHeader, V5Header, RLE }; + public static readonly string[] All = { Car, F, NegHeight, CoreHeader, V5Header, RLE, RLEInverted, Bit8, Bit8Inverted }; } public static class Gif diff --git a/tests/Images/Input/Bmp/RunLengthEncoded-inverted.bmp b/tests/Images/Input/Bmp/RunLengthEncoded-inverted.bmp new file mode 100644 index 000000000..f773daba7 --- /dev/null +++ b/tests/Images/Input/Bmp/RunLengthEncoded-inverted.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4113b0ec1a834b902efa18fc8dc050eda1e18f9228cbc6b06b45d6337ad22c88 +size 7278 diff --git a/tests/Images/Input/Bmp/test8-inverted.bmp b/tests/Images/Input/Bmp/test8-inverted.bmp new file mode 100644 index 000000000..4ec260b66 --- /dev/null +++ b/tests/Images/Input/Bmp/test8-inverted.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:407c948e576330f603b3692c3d37248f8df31067f79e424dba6757066c97845e +size 9270 diff --git a/tests/Images/Input/Bmp/test8.bmp b/tests/Images/Input/Bmp/test8.bmp new file mode 100644 index 000000000..157c96879 --- /dev/null +++ b/tests/Images/Input/Bmp/test8.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3551fbb385b6d7d54e2f0372f79e5701ca9215e4efdddb429ec5d7942367107f +size 9270