Browse Source

Merge pull request #368 from SixLabors/js/bmp-invert-palette

Fix inverted RGB palette decoding plus 32bit RLE decoding
af/merge-core
James Jackson-South 8 years ago
committed by GitHub
parent
commit
171857235c
  1. 70
      src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs
  2. 2
      tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs
  3. 7
      tests/ImageSharp.Tests/TestImages.cs
  4. 3
      tests/Images/Input/Bmp/RunLengthEncoded-inverted.bmp
  5. 3
      tests/Images/Input/Bmp/test8-inverted.bmp
  6. 3
      tests/Images/Input/Bmp/test8.bmp

70
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<TPixel> image = new Image<TPixel>(this.configuration, this.infoHeader.Width, this.infoHeader.Height);
var image = new Image<TPixel>(this.configuration, this.infoHeader.Width, this.infoHeader.Height);
using (PixelAccessor<TPixel> pixels = image.Lock())
{
switch (this.infoHeader.Compression)
@ -196,6 +196,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <param name="height">The height of the bitmap.</param>
/// <param name="inverted">Whether the bitmap is inverted.</param>
/// <returns>The <see cref="int"/> representing the inverted value.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static int Invert(int y, int height, bool inverted)
{
int row;
@ -234,7 +235,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// <summary>
/// Looks up color values and builds the image from de-compressed RLE8 data.
/// Compresssed RLE8 stream is uncompressed by <see cref="UncompressRle8(int, Buffer{byte})"/>
/// Compresssed RLE8 stream is uncompressed by <see cref="UncompressRle8(int, Span{byte})"/>
/// </summary>
/// <typeparam name="TPixel">The pixel format.</typeparam>
/// <param name="pixels">The <see cref="PixelAccessor{TPixel}"/> to assign the palette to.</param>
@ -246,19 +247,21 @@ namespace SixLabors.ImageSharp.Formats.Bmp
where TPixel : struct, IPixel<TPixel>
{
var color = default(TPixel);
var rgba = default(Rgba32);
var rgba = new Rgba32(0, 0, 0, 255);
using (var buffer = Buffer<byte>.CreateClean(width * height))
using (var buffer = Buffer2D<byte>.CreateClean(width, height))
{
this.UncompressRle8(width, buffer);
for (int y = 0; y < height; y++)
{
int newY = Invert(y, height, inverted);
Span<byte> bufferRow = buffer.GetRowSpan(y);
Span<TPixel> pixelRow = pixels.GetRowSpan(newY);
for (int x = 0; x < width; x++)
{
rgba.Bgr = Unsafe.As<byte, Bgr24>(ref colors[buffer[(y * width) + x] * 4]);
rgba.Bgr = Unsafe.As<byte, Bgr24>(ref colors[bufferRow[x] * 4]);
color.PackFromRgba32(rgba);
pixelRow[x] = color;
}
@ -276,7 +279,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
/// </remarks>
/// <param name="w">The width of the bitmap.</param>
/// <param name="buffer">Buffer for uncompressed data.</param>
private void UncompressRle8(int w, Buffer<byte> buffer)
private void UncompressRle8(int w, Span<byte> buffer)
{
byte[] cmd = new byte[2];
int count = 0;
@ -368,35 +371,36 @@ namespace SixLabors.ImageSharp.Formats.Bmp
padding = 4 - padding;
}
byte[] row = new byte[arrayWidth + padding];
TPixel color = default(TPixel);
Rgba32 rgba = default(Rgba32);
for (int y = 0; y < height; y++)
using (var row = Buffer<byte>.CreateClean(arrayWidth + padding))
{
int newY = Invert(y, height, inverted);
this.currentStream.Read(row, 0, row.Length);
int offset = 0;
Span<TPixel> pixelRow = pixels.GetRowSpan(y);
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<TPixel> 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<byte, Bgr24>(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<byte, Bgr24>(ref colors[colorIndex]);
color.PackFromRgba32(rgba);
pixelRow[newX] = color;
}
offset++;
}
}
}
}
@ -417,10 +421,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<TPixel> row = new PixelArea<TPixel>(width, ComponentOrder.Xyz))
using (var row = new PixelArea<TPixel>(width, ComponentOrder.Xyz))
{
for (int y = 0; y < height; y++)
{
@ -459,7 +463,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
where TPixel : struct, IPixel<TPixel>
{
int padding = CalculatePadding(width, 3);
using (PixelArea<TPixel> row = new PixelArea<TPixel>(width, ComponentOrder.Zyx, padding))
using (var row = new PixelArea<TPixel>(width, ComponentOrder.Zyx, padding))
{
for (int y = 0; y < height; y++)
{
@ -483,7 +487,7 @@ namespace SixLabors.ImageSharp.Formats.Bmp
where TPixel : struct, IPixel<TPixel>
{
int padding = CalculatePadding(width, 4);
using (PixelArea<TPixel> row = new PixelArea<TPixel>(width, ComponentOrder.Zyxw, padding))
using (var row = new PixelArea<TPixel>(width, ComponentOrder.Zyxw, padding))
{
for (int y = 0; y < height; y++)
{

2
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<TPixel>(TestImageProvider<TPixel> provider)
where TPixel : struct, IPixel<TPixel>
{

7
tests/ImageSharp.Tests/TestImages.cs

@ -129,14 +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 };
public static readonly string[] All = { Car, F, NegHeight, CoreHeader, V5Header, RLE, RLEInverted, Bit8, Bit8Inverted };
}
public static class Gif

3
tests/Images/Input/Bmp/RunLengthEncoded-inverted.bmp

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:4113b0ec1a834b902efa18fc8dc050eda1e18f9228cbc6b06b45d6337ad22c88
size 7278

3
tests/Images/Input/Bmp/test8-inverted.bmp

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:407c948e576330f603b3692c3d37248f8df31067f79e424dba6757066c97845e
size 9270

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

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