From 1a70b67cc1c955f9c411a819ea6a661de605a433 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Wed, 11 Oct 2017 23:38:02 +1100 Subject: [PATCH 1/5] Ensure can read palette bitmaps --- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 4fda80d72..21d7d8756 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; @@ -369,16 +370,16 @@ namespace SixLabors.ImageSharp.Formats.Bmp } byte[] row = new byte[arrayWidth + padding]; - TPixel color = default(TPixel); + var color = default(TPixel); - Rgba32 rgba = default(Rgba32); + var rgba = default(Rgba32); for (int y = 0; y < height; y++) { int newY = Invert(y, height, inverted); this.currentStream.Read(row, 0, row.Length); int offset = 0; - Span pixelRow = pixels.GetRowSpan(y); + Span pixelRow = pixels.GetRowSpan(newY); // TODO: Could use PixelOperations here! for (int x = 0; x < arrayWidth; x++) @@ -417,10 +418,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 +460,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 +484,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++) { From 8b520e98e83279118a1681300a40cec0c33cc903 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 12 Oct 2017 07:40:32 +1100 Subject: [PATCH 2/5] Add optimisation from #367 --- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 21d7d8756..db9e90adf 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -235,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. @@ -249,17 +249,19 @@ namespace SixLabors.ImageSharp.Formats.Bmp var color = default(TPixel); var rgba = default(Rgba32); - 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; } @@ -277,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; From 6345da1acc2c37320be9a7e15347d4ff57c4cd8a Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 12 Oct 2017 08:08:27 +1100 Subject: [PATCH 3/5] Fix #369 and update test --- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 2 +- tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index db9e90adf..e962d6df6 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -247,7 +247,7 @@ 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 = Buffer2D.CreateClean(width, height)) { 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 { From 395c825215a2d7d5322749b8e67973c65fa41e08 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 12 Oct 2017 09:48:45 +1100 Subject: [PATCH 4/5] Fix alpha channel and add inverted test --- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 45 ++++++++++---------- tests/ImageSharp.Tests/TestImages.cs | 4 +- tests/Images/Input/Bmp/test8-inverted.bmp | 3 ++ tests/Images/Input/Bmp/test8.bmp | 3 ++ 4 files changed, 32 insertions(+), 23 deletions(-) create mode 100644 tests/Images/Input/Bmp/test8-inverted.bmp create mode 100644 tests/Images/Input/Bmp/test8.bmp diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index e962d6df6..39bab442f 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -371,35 +371,36 @@ namespace SixLabors.ImageSharp.Formats.Bmp padding = 4 - padding; } - byte[] row = new byte[arrayWidth + padding]; - var color = default(TPixel); - - var 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(newY); + 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++; + } } } } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 948f519d4..4a0d248e4 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -135,8 +135,10 @@ namespace SixLabors.ImageSharp.Tests public const string CoreHeader = "Bmp/BitmapCoreHeaderQR.bmp"; public const string V5Header = "Bmp/BITMAPV5HEADER.bmp"; public const string RLE = "Bmp/RunLengthEncoded.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, Bit8, Bit8Inverted }; } public static class Gif 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 From aa3ee34e70c8ff136ac438f770f321584ab2067a Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 12 Oct 2017 10:15:32 +1100 Subject: [PATCH 5/5] Add RLE inverted test image --- tests/ImageSharp.Tests/TestImages.cs | 5 ++++- tests/Images/Input/Bmp/RunLengthEncoded-inverted.bmp | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 tests/Images/Input/Bmp/RunLengthEncoded-inverted.bmp diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 4a0d248e4..8be892233 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -129,16 +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, Bit8, Bit8Inverted }; + 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