Browse Source

Preserve BmpBitsPerPixel

af/merge-core
James Jackson-South 8 years ago
parent
commit
551c03f16a
  1. 6
      src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs
  2. 17
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  3. 2
      src/ImageSharp/Formats/Bmp/BmpEncoder.cs
  4. 17
      src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs
  5. 7
      src/ImageSharp/Formats/Bmp/BmpMetaData.cs
  6. 2
      src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs
  7. 45
      tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs
  8. 1
      tests/ImageSharp.Tests/TestImages.cs
  9. 3
      tests/Images/Input/Bmp/rgb32.bmp

6
src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs

@ -6,16 +6,16 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <summary> /// <summary>
/// Enumerates the available bits per pixel for bitmap. /// Enumerates the available bits per pixel for bitmap.
/// </summary> /// </summary>
public enum BmpBitsPerPixel public enum BmpBitsPerPixel : short
{ {
/// <summary> /// <summary>
/// 24 bits per pixel. Each pixel consists of 3 bytes. /// 24 bits per pixel. Each pixel consists of 3 bytes.
/// </summary> /// </summary>
Pixel24 = 3, Pixel24 = 24,
/// <summary> /// <summary>
/// 32 bits per pixel. Each pixel consists of 4 bytes. /// 32 bits per pixel. Each pixel consists of 4 bytes.
/// </summary> /// </summary>
Pixel32 = 4 Pixel32 = 32
} }
} }

17
src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs

@ -536,6 +536,17 @@ namespace SixLabors.ImageSharp.Formats.Bmp
this.metaData = meta; 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 // skip the remaining header because we can't read those parts
this.stream.Skip(skipAmount); this.stream.Skip(skipAmount);
} }
@ -581,9 +592,9 @@ namespace SixLabors.ImageSharp.Formats.Bmp
if (this.infoHeader.ClrUsed == 0) if (this.infoHeader.ClrUsed == 0)
{ {
if (this.infoHeader.BitsPerPixel == 1 || if (this.infoHeader.BitsPerPixel == 1
this.infoHeader.BitsPerPixel == 4 || || this.infoHeader.BitsPerPixel == 4
this.infoHeader.BitsPerPixel == 8) || this.infoHeader.BitsPerPixel == 8)
{ {
colorMapSize = (int)Math.Pow(2, this.infoHeader.BitsPerPixel) * 4; colorMapSize = (int)Math.Pow(2, this.infoHeader.BitsPerPixel) * 4;
} }

2
src/ImageSharp/Formats/Bmp/BmpEncoder.cs

@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <summary> /// <summary>
/// Gets or sets the number of bits per pixel. /// Gets or sets the number of bits per pixel.
/// </summary> /// </summary>
public BmpBitsPerPixel BitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel24; public BmpBitsPerPixel? BitsPerPixel { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
public void Encode<TPixel>(Image<TPixel> image, Stream stream) public void Encode<TPixel>(Image<TPixel> image, Stream stream)

17
src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs

@ -21,10 +21,10 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary> /// </summary>
private int padding; private int padding;
private readonly BmpBitsPerPixel bitsPerPixel;
private readonly MemoryAllocator memoryAllocator; private readonly MemoryAllocator memoryAllocator;
private BmpBitsPerPixel? bitsPerPixel;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="BmpEncoderCore"/> class. /// Initializes a new instance of the <see cref="BmpEncoderCore"/> class.
/// </summary> /// </summary>
@ -48,10 +48,12 @@ namespace SixLabors.ImageSharp.Formats.Bmp
Guard.NotNull(image, nameof(image)); Guard.NotNull(image, nameof(image));
Guard.NotNull(stream, nameof(stream)); Guard.NotNull(stream, nameof(stream));
// Cast to int will get the bytes per pixel BmpMetaData bmpMetaData = image.MetaData.GetOrAddFormatMetaData(BmpFormat.Instance);
short bpp = (short)(8 * (int)this.bitsPerPixel); this.bitsPerPixel = this.bitsPerPixel ?? bmpMetaData.BitsPerPixel;
short bpp = (short)this.bitsPerPixel;
int bytesPerLine = 4 * (((image.Width * bpp) + 31) / 32); 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. // Set Resolution.
ImageMetaData meta = image.MetaData; ImageMetaData meta = image.MetaData;
@ -145,10 +147,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
} }
} }
private IManagedByteBuffer AllocateRow(int width, int bytesPerPixel) private IManagedByteBuffer AllocateRow(int width, int bytesPerPixel) => this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, bytesPerPixel, this.padding);
{
return this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, bytesPerPixel, this.padding);
}
/// <summary> /// <summary>
/// Writes the 32bit color palette to the stream. /// Writes the 32bit color palette to the stream.

7
src/ImageSharp/Formats/Bmp/BmpMetaData.cs

@ -8,6 +8,11 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </summary> /// </summary>
public class BmpMetaData public class BmpMetaData
{ {
// TODO: Analyse what properties we would like to preserve. /// <summary>
/// Gets or sets the number of bits per pixel.
/// </summary>
public BmpBitsPerPixel BitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel24;
// TODO: Colors used once we support encoding palette bmps.
} }
} }

2
src/ImageSharp/Formats/Bmp/IBmpEncoderOptions.cs

@ -12,6 +12,6 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <summary> /// <summary>
/// Gets the number of bits per pixel. /// Gets the number of bits per pixel.
/// </summary> /// </summary>
BmpBitsPerPixel BitsPerPixel { get; } BmpBitsPerPixel? BitsPerPixel { get; }
} }
} }

45
tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs

@ -28,10 +28,14 @@ namespace SixLabors.ImageSharp.Tests
{ TestImages.Bmp.RLE, 2835, 2835, PixelResolutionUnit.PixelsPerMeter } { TestImages.Bmp.RLE, 2835, 2835, PixelResolutionUnit.PixelsPerMeter }
}; };
public BmpEncoderTests(ITestOutputHelper output) public static readonly TheoryData<string, BmpBitsPerPixel> BmpBitsPerPixelFiles =
new TheoryData<string, BmpBitsPerPixel>
{ {
this.Output = output; { TestImages.Bmp.Car, BmpBitsPerPixel.Pixel24 },
} { TestImages.Bmp.Bit32Rgb, BmpBitsPerPixel.Pixel32 }
};
public BmpEncoderTests(ITestOutputHelper output) => this.Output = output;
private ITestOutputHelper Output { get; } private ITestOutputHelper Output { get; }
@ -61,13 +65,35 @@ namespace SixLabors.ImageSharp.Tests
} }
[Theory] [Theory]
[WithTestPatternImages(nameof(BitsPerPixel), 24, 24, PixelTypes.Rgba32 | PixelTypes.Bgra32 | PixelTypes.Rgb24)] [MemberData(nameof(BmpBitsPerPixelFiles))]
public void Encode_IsNotBoundToSinglePixelType<TPixel>(TestImageProvider<TPixel> provider, BmpBitsPerPixel bitsPerPixel) public void Encode_PreserveBitsPerPixel(string imagePath, BmpBitsPerPixel bmpBitsPerPixel)
where TPixel : struct, IPixel<TPixel>
{ {
TestBmpEncoderCore(provider, bitsPerPixel); var options = new BmpEncoder();
var testFile = TestFile.Create(imagePath);
using (Image<Rgba32> input = testFile.CreateImage())
{
using (var memStream = new MemoryStream())
{
input.Save(memStream, options);
memStream.Position = 0;
using (var output = Image.Load<Rgba32>(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<TPixel>(TestImageProvider<TPixel> provider, BmpBitsPerPixel bitsPerPixel)
where TPixel : struct, IPixel<TPixel> => TestBmpEncoderCore(provider, bitsPerPixel);
[Theory] [Theory]
[WithTestPatternImages(nameof(BitsPerPixel), 48, 24, PixelTypes.Rgba32)] [WithTestPatternImages(nameof(BitsPerPixel), 48, 24, PixelTypes.Rgba32)]
[WithTestPatternImages(nameof(BitsPerPixel), 47, 8, 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)] [WithSolidFilledImages(nameof(BitsPerPixel), 1, 1, 255, 100, 50, 255, PixelTypes.Rgba32)]
[WithTestPatternImages(nameof(BitsPerPixel), 7, 5, PixelTypes.Rgba32)] [WithTestPatternImages(nameof(BitsPerPixel), 7, 5, PixelTypes.Rgba32)]
public void Encode_WorksWithDifferentSizes<TPixel>(TestImageProvider<TPixel> provider, BmpBitsPerPixel bitsPerPixel) public void Encode_WorksWithDifferentSizes<TPixel>(TestImageProvider<TPixel> provider, BmpBitsPerPixel bitsPerPixel)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel> => TestBmpEncoderCore(provider, bitsPerPixel);
{
TestBmpEncoderCore(provider, bitsPerPixel);
}
private static void TestBmpEncoderCore<TPixel>(TestImageProvider<TPixel> provider, BmpBitsPerPixel bitsPerPixel) private static void TestBmpEncoderCore<TPixel>(TestImageProvider<TPixel> provider, BmpBitsPerPixel bitsPerPixel)
where TPixel : struct, IPixel<TPixel> where TPixel : struct, IPixel<TPixel>

1
tests/ImageSharp.Tests/TestImages.cs

@ -178,6 +178,7 @@ namespace SixLabors.ImageSharp.Tests
public const string Bit8Inverted = "Bmp/test8-inverted.bmp"; public const string Bit8Inverted = "Bmp/test8-inverted.bmp";
public const string Bit16 = "Bmp/test16.bmp"; public const string Bit16 = "Bmp/test16.bmp";
public const string Bit16Inverted = "Bmp/test16-inverted.bmp"; public const string Bit16Inverted = "Bmp/test16-inverted.bmp";
public const string Bit32Rgb = "Bmp/rgb32.bmp";
public static readonly string[] All public static readonly string[] All
= { = {

3
tests/Images/Input/Bmp/rgb32.bmp

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:b4c79cb8ffd2f1c096af27f9d82b5feaa0aa2cb049c791e0f6251de0435066e5
size 32566
Loading…
Cancel
Save