From 9907d78d3ee5c09eac20c8cf7b655d2a095f1466 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Tue, 11 Sep 2018 15:58:42 +0100 Subject: [PATCH] Preserve BmpBitsPerPixel --- src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs | 6 +-- src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs | 17 +++++-- src/ImageSharp/Formats/Bmp/BmpEncoder.cs | 2 +- src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs | 17 ++++--- src/ImageSharp/Formats/Bmp/BmpMetaData.cs | 7 ++- .../Formats/Bmp/IBmpEncoderOptions.cs | 2 +- .../Formats/Bmp/BmpEncoderTests.cs | 45 +++++++++++++----- tests/ImageSharp.Tests/TestImages.cs | 1 + tests/Images/Input/Bmp/rgb32.bmp | Bin 0 -> 32566 bytes 9 files changed, 68 insertions(+), 29 deletions(-) create mode 100644 tests/Images/Input/Bmp/rgb32.bmp diff --git a/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs b/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs index 0029a6b68d..618999c87d 100644 --- a/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs +++ b/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs @@ -6,16 +6,16 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// Enumerates the available bits per pixel for bitmap. /// - public enum BmpBitsPerPixel + public enum BmpBitsPerPixel : short { /// /// 24 bits per pixel. Each pixel consists of 3 bytes. /// - Pixel24 = 3, + Pixel24 = 24, /// /// 32 bits per pixel. Each pixel consists of 4 bytes. /// - Pixel32 = 4 + Pixel32 = 32 } } \ No newline at end of file diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 385c79896e..3bb44f1d06 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -536,6 +536,17 @@ namespace SixLabors.ImageSharp.Formats.Bmp this.metaData = meta; + short bitsPerPixel = this.infoHeader.BitsPerPixel; + var bmpMetaData = new BmpMetaData(); + this.metaData.AddOrUpdateFormatMetaData(BmpFormat.Instance, bmpMetaData); + + // We can only encode at these bit rates so far. + if (bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel24) + || bitsPerPixel.Equals((short)BmpBitsPerPixel.Pixel32)) + { + bmpMetaData.BitsPerPixel = (BmpBitsPerPixel)bitsPerPixel; + } + // skip the remaining header because we can't read those parts this.stream.Skip(skipAmount); } @@ -581,9 +592,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp if (this.infoHeader.ClrUsed == 0) { - if (this.infoHeader.BitsPerPixel == 1 || - this.infoHeader.BitsPerPixel == 4 || - this.infoHeader.BitsPerPixel == 8) + if (this.infoHeader.BitsPerPixel == 1 + || this.infoHeader.BitsPerPixel == 4 + || this.infoHeader.BitsPerPixel == 8) { colorMapSize = (int)Math.Pow(2, this.infoHeader.BitsPerPixel) * 4; } diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs b/src/ImageSharp/Formats/Bmp/BmpEncoder.cs index 23b01ae9e8..b1a66accec 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoder.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoder.cs @@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// Gets or sets the number of bits per pixel. /// - public BmpBitsPerPixel BitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel24; + public BmpBitsPerPixel? BitsPerPixel { get; set; } /// public void Encode(Image image, Stream stream) diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index b49b8a8959..7a09a47f78 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -21,10 +21,10 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// private int padding; - private readonly BmpBitsPerPixel bitsPerPixel; - private readonly MemoryAllocator memoryAllocator; + private BmpBitsPerPixel? bitsPerPixel; + /// /// Initializes a new instance of the class. /// @@ -48,10 +48,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp Guard.NotNull(image, nameof(image)); Guard.NotNull(stream, nameof(stream)); - // Cast to int will get the bytes per pixel - short bpp = (short)(8 * (int)this.bitsPerPixel); + BmpMetaData bmpMetaData = image.MetaData.GetOrAddFormatMetaData(BmpFormat.Instance); + this.bitsPerPixel = this.bitsPerPixel ?? bmpMetaData.BitsPerPixel; + + short bpp = (short)this.bitsPerPixel; int bytesPerLine = 4 * (((image.Width * bpp) + 31) / 32); - this.padding = bytesPerLine - (image.Width * (int)this.bitsPerPixel); + this.padding = bytesPerLine - (int)(image.Width * (bpp / 8F)); // Set Resolution. ImageMetaData meta = image.MetaData; @@ -145,10 +147,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp } } - private IManagedByteBuffer AllocateRow(int width, int bytesPerPixel) - { - return this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, bytesPerPixel, this.padding); - } + private IManagedByteBuffer AllocateRow(int width, int bytesPerPixel) => this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, bytesPerPixel, this.padding); /// /// Writes the 32bit color palette to the stream. diff --git a/src/ImageSharp/Formats/Bmp/BmpMetaData.cs b/src/ImageSharp/Formats/Bmp/BmpMetaData.cs index aa60f38662..3d678c13e1 100644 --- a/src/ImageSharp/Formats/Bmp/BmpMetaData.cs +++ b/src/ImageSharp/Formats/Bmp/BmpMetaData.cs @@ -8,6 +8,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// public class BmpMetaData { - // TODO: Analyse what properties we would like to preserve. + /// + /// Gets or sets the number of bits per pixel. + /// + public BmpBitsPerPixel BitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel24; + + // TODO: Colors used once we support encoding palette bmps. } } diff --git a/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs b/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs index 56952f0356..f62504d08f 100644 --- a/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs +++ b/src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs @@ -12,6 +12,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp /// /// Gets the number of bits per pixel. /// - BmpBitsPerPixel BitsPerPixel { get; } + BmpBitsPerPixel? BitsPerPixel { get; } } } \ No newline at end of file diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index d887d23ade..c75c656919 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -28,10 +28,14 @@ namespace SixLabors.ImageSharp.Tests { TestImages.Bmp.RLE, 2835, 2835, PixelResolutionUnit.PixelsPerMeter } }; - public BmpEncoderTests(ITestOutputHelper output) + public static readonly TheoryData BmpBitsPerPixelFiles = + new TheoryData { - this.Output = output; - } + { TestImages.Bmp.Car, BmpBitsPerPixel.Pixel24 }, + { TestImages.Bmp.Bit32Rgb, BmpBitsPerPixel.Pixel32 } + }; + + public BmpEncoderTests(ITestOutputHelper output) => this.Output = output; private ITestOutputHelper Output { get; } @@ -61,13 +65,35 @@ namespace SixLabors.ImageSharp.Tests } [Theory] - [WithTestPatternImages(nameof(BitsPerPixel), 24, 24, PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.Rgb24)] - public void Encode_IsNotBoundToSinglePixelType(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) - where TPixel : struct, IPixel + [MemberData(nameof(BmpBitsPerPixelFiles))] + public void Encode_PreserveBitsPerPixel(string imagePath, BmpBitsPerPixel bmpBitsPerPixel) { - TestBmpEncoderCore(provider, bitsPerPixel); + var options = new BmpEncoder(); + + var testFile = TestFile.Create(imagePath); + using (Image input = testFile.CreateImage()) + { + using (var memStream = new MemoryStream()) + { + input.Save(memStream, options); + + memStream.Position = 0; + using (var output = Image.Load(memStream)) + { + BmpMetaData meta = output.MetaData.GetOrAddFormatMetaData(BmpFormat.Instance); + + Assert.Equal(bmpBitsPerPixel, meta.BitsPerPixel); + } + } + } } + + [Theory] + [WithTestPatternImages(nameof(BitsPerPixel), 24, 24, PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.Rgb24)] + public void Encode_IsNotBoundToSinglePixelType(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) + where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel); + [Theory] [WithTestPatternImages(nameof(BitsPerPixel), 48, 24, PixelTypes.Rgba32)] [WithTestPatternImages(nameof(BitsPerPixel), 47, 8, PixelTypes.Rgba32)] @@ -75,10 +101,7 @@ namespace SixLabors.ImageSharp.Tests [WithSolidFilledImages(nameof(BitsPerPixel), 1, 1, 255, 100, 50, 255, PixelTypes.Rgba32)] [WithTestPatternImages(nameof(BitsPerPixel), 7, 5, PixelTypes.Rgba32)] public void Encode_WorksWithDifferentSizes(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) - where TPixel : struct, IPixel - { - TestBmpEncoderCore(provider, bitsPerPixel); - } + where TPixel : struct, IPixel => TestBmpEncoderCore(provider, bitsPerPixel); private static void TestBmpEncoderCore(TestImageProvider provider, BmpBitsPerPixel bitsPerPixel) where TPixel : struct, IPixel diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index df015f7556..acfad042eb 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -178,6 +178,7 @@ namespace SixLabors.ImageSharp.Tests public const string Bit8Inverted = "Bmp/test8-inverted.bmp"; public const string Bit16 = "Bmp/test16.bmp"; public const string Bit16Inverted = "Bmp/test16-inverted.bmp"; + public const string Bit32Rgb = "Bmp/rgb32.bmp"; public static readonly string[] All = { diff --git a/tests/Images/Input/Bmp/rgb32.bmp b/tests/Images/Input/Bmp/rgb32.bmp new file mode 100644 index 0000000000000000000000000000000000000000..5d57eaaea8a503668a94d43dcb62a9c840b5ca39 GIT binary patch literal 32566 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 literal 0 HcmV?d00001