Browse Source

Preserve BmpBitsPerPixel

pull/693/head
James Jackson-South 8 years ago
parent
commit
9907d78d3e
  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. BIN
      tests/Images/Input/Bmp/rgb32.bmp

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

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

17
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;
}

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

@ -16,7 +16,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <summary>
/// Gets or sets the number of bits per pixel.
/// </summary>
public BmpBitsPerPixel BitsPerPixel { get; set; } = BmpBitsPerPixel.Pixel24;
public BmpBitsPerPixel? BitsPerPixel { get; set; }
/// <inheritdoc/>
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>
private int padding;
private readonly BmpBitsPerPixel bitsPerPixel;
private readonly MemoryAllocator memoryAllocator;
private BmpBitsPerPixel? bitsPerPixel;
/// <summary>
/// Initializes a new instance of the <see cref="BmpEncoderCore"/> class.
/// </summary>
@ -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);
/// <summary>
/// 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>
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>
/// Gets the number of bits per pixel.
/// </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 }
};
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; }
@ -61,13 +65,35 @@ namespace SixLabors.ImageSharp.Tests
}
[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>
[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<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]
[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<TPixel>(TestImageProvider<TPixel> provider, BmpBitsPerPixel bitsPerPixel)
where TPixel : struct, IPixel<TPixel>
{
TestBmpEncoderCore(provider, bitsPerPixel);
}
where TPixel : struct, IPixel<TPixel> => TestBmpEncoderCore(provider, bitsPerPixel);
private static void TestBmpEncoderCore<TPixel>(TestImageProvider<TPixel> provider, BmpBitsPerPixel bitsPerPixel)
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 Bit16 = "Bmp/test16.bmp";
public const string Bit16Inverted = "Bmp/test16-inverted.bmp";
public const string Bit32Rgb = "Bmp/rgb32.bmp";
public static readonly string[] All
= {

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Loading…
Cancel
Save