From 1f4497f87fd0e6a5cfd2b2943851d03022b6edeb Mon Sep 17 00:00:00 2001 From: popow Date: Sun, 16 Dec 2018 19:06:08 +0100 Subject: [PATCH 1/5] fix for Windows 2.0 or OS/2 1.x bitmaps only use 3 bytes per color palette entry --- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 29 ++++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index ef3ca24ee..b11f7934d 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -108,7 +108,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp { try { - this.ReadImageHeaders(stream, out bool inverted, out byte[] palette); + int bytesPerColorMapEntry = this.ReadImageHeaders(stream, out bool inverted, out byte[] palette); var image = new Image(this.configuration, this.infoHeader.Width, this.infoHeader.Height, this.metaData); @@ -137,6 +137,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp this.infoHeader.Width, this.infoHeader.Height, this.infoHeader.BitsPerPixel, + bytesPerColorMapEntry, inverted); } @@ -329,18 +330,20 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// The containing the colors. /// The width of the bitmap. /// The height of the bitmap. - /// The number of bits per pixel. + /// The number of bits per pixel. + /// Usually 4 bytes, but in case of Windows 2.x bitmaps or OS/2 1.x bitmaps + /// the bytes per color palette entry's can be 3 bytes instead of 4. /// Whether the bitmap is inverted. - private void ReadRgbPalette(Buffer2D pixels, byte[] colors, int width, int height, int bits, bool inverted) + private void ReadRgbPalette(Buffer2D pixels, byte[] colors, int width, int height, int bitsPerPixel, int bytesPerColorMapEntry, bool inverted) where TPixel : struct, IPixel { // Pixels per byte (bits per pixel) - int ppb = 8 / bits; + int ppb = 8 / bitsPerPixel; int arrayWidth = (width + ppb - 1) / ppb; // Bit mask - int mask = 0xFF >> (8 - bits); + int mask = 0xFF >> (8 - bitsPerPixel); // Rows are aligned on 4 byte boundaries int padding = arrayWidth % 4; @@ -366,7 +369,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp int colOffset = x * ppb; for (int shift = 0, newX = colOffset; shift < ppb && newX < width; shift++, newX++) { - int colorIndex = ((rowSpan[offset] >> (8 - bits - (shift * bits))) & mask) * 4; + int colorIndex = ((rowSpan[offset] >> (8 - bitsPerPixel - (shift * bitsPerPixel))) & mask) * bytesPerColorMapEntry; color.FromBgr24(Unsafe.As(ref colors[colorIndex])); pixelRow[newX] = color; @@ -571,7 +574,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// Reads the and from the stream and sets the corresponding fields. /// - private void ReadImageHeaders(Stream stream, out bool inverted, out byte[] palette) + /// Bytes per color palette entry. Usually 4 bytes, but in case of Windows 2.x bitmaps or OS/2 1.x bitmaps + /// the bytes per color palette entry's can be 3 bytes instead of 4. + private int ReadImageHeaders(Stream stream, out bool inverted, out byte[] palette) { this.stream = stream; @@ -591,6 +596,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp } int colorMapSize = -1; + int bytesPerColorMapEntry = 4; if (this.infoHeader.ClrUsed == 0) { @@ -598,12 +604,15 @@ namespace SixLabors.ImageSharp.Formats.Bmp || this.infoHeader.BitsPerPixel == 4 || this.infoHeader.BitsPerPixel == 8) { - colorMapSize = ImageMaths.GetColorCountForBitDepth(this.infoHeader.BitsPerPixel) * 4; + int colorMapSizeBytes = this.fileHeader.Offset - BmpFileHeader.Size - this.infoHeader.HeaderSize; + int colorCountForBitDepth = ImageMaths.GetColorCountForBitDepth(this.infoHeader.BitsPerPixel); + bytesPerColorMapEntry = colorMapSizeBytes / colorCountForBitDepth; + colorMapSize = colorMapSizeBytes; } } else { - colorMapSize = this.infoHeader.ClrUsed * 4; + colorMapSize = this.infoHeader.ClrUsed * bytesPerColorMapEntry; } palette = null; @@ -622,6 +631,8 @@ namespace SixLabors.ImageSharp.Formats.Bmp } this.infoHeader.VerifyDimensions(); + + return bytesPerColorMapEntry; } } } \ No newline at end of file From a6919d4b151f79e58d56a035b531eff5e3618453 Mon Sep 17 00:00:00 2001 From: popow Date: Tue, 18 Dec 2018 19:34:28 +0100 Subject: [PATCH 2/5] added Test for decoding windows BMPv2 and one for a bitmap which has 4 bytes per color palette --- .../Formats/Bmp/BmpDecoderTests.cs | 24 +++++++++++++++++++ tests/ImageSharp.Tests/TestImages.cs | 3 +++ tests/Images/Input/Bmp/pal8-0.bmp | 3 +++ tests/Images/Input/Bmp/pal8os2v1_winv2.bmp | 3 +++ 4 files changed, 33 insertions(+) create mode 100644 tests/Images/Input/Bmp/pal8-0.bmp create mode 100644 tests/Images/Input/Bmp/pal8os2v1_winv2.bmp diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index 5f2de9f51..d60d9d918 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -55,6 +55,30 @@ namespace SixLabors.ImageSharp.Tests } } + [Theory] + [WithFile(WinBmpv2, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecodeBmpv2(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider, "png"); + image.CompareToOriginal(provider); + } + } + + [Theory] + [WithFile(Bit8Palette4, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode4BytePerEntryPalette(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new BmpDecoder())) + { + image.DebugSave(provider, "png"); + image.CompareToOriginal(provider); + } + } + [Theory] [InlineData(Car, 24)] [InlineData(F, 24)] diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 1144a3f7c..9c747dcb8 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -201,6 +201,9 @@ namespace SixLabors.ImageSharp.Tests public const string Bit16 = "Bmp/test16.bmp"; public const string Bit16Inverted = "Bmp/test16-inverted.bmp"; public const string Bit32Rgb = "Bmp/rgb32.bmp"; + // Note: This format can be called OS/2 BMPv1, or Windows BMPv2 + public const string WinBmpv2 = "Bmp/pal8os2v1_winv2.bmp"; + public const string Bit8Palette4 = "Bmp/pal8-0.bmp"; public static readonly string[] All = { diff --git a/tests/Images/Input/Bmp/pal8-0.bmp b/tests/Images/Input/Bmp/pal8-0.bmp new file mode 100644 index 000000000..a5565d59f --- /dev/null +++ b/tests/Images/Input/Bmp/pal8-0.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5ae0f808fea31352b8667fdaf897d057c7e02483c43d43cd735f92f4f149fe8d +size 9270 diff --git a/tests/Images/Input/Bmp/pal8os2v1_winv2.bmp b/tests/Images/Input/Bmp/pal8os2v1_winv2.bmp new file mode 100644 index 000000000..42440b6df --- /dev/null +++ b/tests/Images/Input/Bmp/pal8os2v1_winv2.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2bfe739377020722872f7fa3ba3ff3ff0daa17f2f202c80e25cf66c4ec30e506 +size 8986 From 076d38754ee6aa7cf28cf6a586b473df8638566d Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 20 Dec 2018 20:59:38 +1100 Subject: [PATCH 3/5] Use MagickReferenceDecoder for bmp. --- .../TestUtilities/TestEnvironment.Formats.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs index 334b6552a..34a075ac7 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs @@ -34,8 +34,7 @@ namespace SixLabors.ImageSharp.Tests { string extension = Path.GetExtension(filePath); - IImageFormat format = Configuration.ImageFormatsManager.FindFormatByFileExtension(extension); - return format; + return Configuration.ImageFormatsManager.FindFormatByFileExtension(extension); } private static void ConfigureCodecs( @@ -69,7 +68,7 @@ namespace SixLabors.ImageSharp.Tests cfg.ConfigureCodecs( BmpFormat.Instance, - SystemDrawingReferenceDecoder.Instance, + MagickReferenceDecoder.Instance, bmpEncoder, new BmpImageFormatDetector()); From 29203023da0b435554190ac9e9add2efb60319e3 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 20 Dec 2018 21:54:59 +1100 Subject: [PATCH 4/5] Fix reference tests --- .../TestUtilities/Tests/TestEnvironmentTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs index 30bb16c2a..8abbd94bd 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs @@ -101,7 +101,7 @@ namespace SixLabors.ImageSharp.Tests [Theory] [InlineData("lol/foo.png", typeof(MagickReferenceDecoder))] - [InlineData("lol/Rofl.bmp", typeof(SystemDrawingReferenceDecoder))] + [InlineData("lol/Rofl.bmp", typeof(MagickReferenceDecoder))] [InlineData("lol/Baz.JPG", typeof(JpegDecoder))] [InlineData("lol/Baz.gif", typeof(GifDecoder))] public void GetReferenceDecoder_ReturnsCorrectDecoders_Windows(string fileName, Type expectedDecoderType) @@ -127,7 +127,7 @@ namespace SixLabors.ImageSharp.Tests [Theory] [InlineData("lol/foo.png", typeof(MagickReferenceDecoder))] - [InlineData("lol/Rofl.bmp", typeof(SystemDrawingReferenceDecoder))] + [InlineData("lol/Rofl.bmp", typeof(MagickReferenceDecoder))] [InlineData("lol/Baz.JPG", typeof(JpegDecoder))] [InlineData("lol/Baz.gif", typeof(GifDecoder))] public void GetReferenceDecoder_ReturnsCorrectDecoders_Linux(string fileName, Type expectedDecoderType) From b8656bf40507504b17c5f55784d8ba16a5710f24 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Thu, 20 Dec 2018 23:25:23 +1100 Subject: [PATCH 5/5] Use S.D in Windows. --- tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs | 2 +- .../TestUtilities/Tests/TestEnvironmentTests.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs index 34a075ac7..7d0684722 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestEnvironment.Formats.cs @@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Tests cfg.ConfigureCodecs( BmpFormat.Instance, - MagickReferenceDecoder.Instance, + IsWindows ? (IImageDecoder)SystemDrawingReferenceDecoder.Instance : MagickReferenceDecoder.Instance, bmpEncoder, new BmpImageFormatDetector()); diff --git a/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs b/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs index 8abbd94bd..122234ae8 100644 --- a/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs +++ b/tests/ImageSharp.Tests/TestUtilities/Tests/TestEnvironmentTests.cs @@ -101,7 +101,7 @@ namespace SixLabors.ImageSharp.Tests [Theory] [InlineData("lol/foo.png", typeof(MagickReferenceDecoder))] - [InlineData("lol/Rofl.bmp", typeof(MagickReferenceDecoder))] + [InlineData("lol/Rofl.bmp", typeof(SystemDrawingReferenceDecoder))] [InlineData("lol/Baz.JPG", typeof(JpegDecoder))] [InlineData("lol/Baz.gif", typeof(GifDecoder))] public void GetReferenceDecoder_ReturnsCorrectDecoders_Windows(string fileName, Type expectedDecoderType)