diff --git a/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs b/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs index 1b73d8b189..f66883c203 100644 --- a/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs +++ b/src/ImageSharp/Formats/Bmp/BmpBitsPerPixel.cs @@ -13,6 +13,11 @@ public enum BmpBitsPerPixel : short /// Pixel1 = 1, + /// + /// 2 bits per pixel. + /// + Pixel2 = 2, + /// /// 4 bits per pixel. /// diff --git a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs index 1adc34849e..350823decb 100644 --- a/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs @@ -836,7 +836,11 @@ private void ReadRgbPalette(Buffer2D pixels, byte[] colors, int for (int y = 0; y < height; y++) { int newY = Invert(y, height, inverted); - this.stream.Read(rowSpan); + if (this.stream.Read(rowSpan) == 0) + { + BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); + } + int offset = 0; Span pixelRow = pixels.DangerousGetRowSpan(newY); @@ -888,7 +892,11 @@ private void ReadRgb16(Buffer2D pixels, int width, int height, b for (int y = 0; y < height; y++) { - this.stream.Read(bufferSpan); + if (this.stream.Read(bufferSpan) == 0) + { + BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); + } + int newY = Invert(y, height, inverted); Span pixelRow = pixels.DangerousGetRowSpan(newY); @@ -943,7 +951,11 @@ private void ReadRgb24(Buffer2D pixels, int width, int height, b for (int y = 0; y < height; y++) { - this.stream.Read(rowSpan); + if (this.stream.Read(rowSpan) == 0) + { + BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); + } + int newY = Invert(y, height, inverted); Span pixelSpan = pixels.DangerousGetRowSpan(newY); PixelOperations.Instance.FromBgr24Bytes( @@ -971,7 +983,11 @@ private void ReadRgb32Fast(Buffer2D pixels, int width, int heigh for (int y = 0; y < height; y++) { - this.stream.Read(rowSpan); + if (this.stream.Read(rowSpan) == 0) + { + BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); + } + int newY = Invert(y, height, inverted); Span pixelSpan = pixels.DangerousGetRowSpan(newY); PixelOperations.Instance.FromBgra32Bytes( @@ -1007,7 +1023,10 @@ private void ReadRgb32Slow(Buffer2D pixels, int width, int heigh // actually a BGRA image, and change tactics accordingly. for (int y = 0; y < height; y++) { - this.stream.Read(rowSpan); + if (this.stream.Read(rowSpan) == 0) + { + BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); + } PixelOperations.Instance.FromBgra32Bytes( this.configuration, @@ -1040,7 +1059,10 @@ private void ReadRgb32Slow(Buffer2D pixels, int width, int heigh { for (int y = 0; y < height; y++) { - this.stream.Read(rowSpan); + if (this.stream.Read(rowSpan) == 0) + { + BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); + } int newY = Invert(y, height, inverted); Span pixelSpan = pixels.DangerousGetRowSpan(newY); @@ -1058,7 +1080,11 @@ private void ReadRgb32Slow(Buffer2D pixels, int width, int heigh // Slow path. We need to set each alpha component value to fully opaque. for (int y = 0; y < height; y++) { - this.stream.Read(rowSpan); + if (this.stream.Read(rowSpan) == 0) + { + BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); + } + PixelOperations.Instance.FromBgra32Bytes( this.configuration, rowSpan, @@ -1119,7 +1145,11 @@ private void ReadRgb32BitFields(Buffer2D pixels, int width, int for (int y = 0; y < height; y++) { - this.stream.Read(bufferSpan); + if (this.stream.Read(bufferSpan) == 0) + { + BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for a pixel row!"); + } + int newY = Invert(y, height, inverted); Span pixelRow = pixels.DangerousGetRowSpan(newY); @@ -1391,7 +1421,7 @@ private int ReadImageHeaders(BufferedReadStream stream, out bool inverted, out b int colorMapSizeBytes = -1; if (this.infoHeader.ClrUsed == 0) { - if (this.infoHeader.BitsPerPixel is 1 or 4 or 8) + if (this.infoHeader.BitsPerPixel is 1 or 2 or 4 or 8) { switch (this.fileMarkerType) { @@ -1435,7 +1465,10 @@ private int ReadImageHeaders(BufferedReadStream stream, out bool inverted, out b palette = new byte[colorMapSizeBytes]; - this.stream.Read(palette, 0, colorMapSizeBytes); + if (this.stream.Read(palette, 0, colorMapSizeBytes) == 0) + { + BmpThrowHelper.ThrowInvalidImageContentException("Could not read enough data for the palette!"); + } } this.infoHeader.VerifyDimensions(); diff --git a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs index f71275b7cc..257159bd23 100644 --- a/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs +++ b/src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs @@ -57,6 +57,11 @@ internal sealed class BmpEncoderCore : IImageEncoderInternals /// private const int ColorPaletteSize4Bit = 64; + /// + /// The color palette for an 2 bit image will have 4 entry's with 4 bytes for each entry. + /// + private const int ColorPaletteSize2Bit = 16; + /// /// The color palette for an 1 bit image will have 2 entry's with 4 bytes for each entry. /// @@ -125,19 +130,14 @@ public void Encode(Image image, Stream stream, CancellationToken int bytesPerLine = 4 * (((image.Width * bpp) + 31) / 32); this.padding = bytesPerLine - (int)(image.Width * (bpp / 8F)); - int colorPaletteSize = 0; - if (this.bitsPerPixel == BmpBitsPerPixel.Pixel8) - { - colorPaletteSize = ColorPaletteSize8Bit; - } - else if (this.bitsPerPixel == BmpBitsPerPixel.Pixel4) - { - colorPaletteSize = ColorPaletteSize4Bit; - } - else if (this.bitsPerPixel == BmpBitsPerPixel.Pixel1) + int colorPaletteSize = this.bitsPerPixel switch { - colorPaletteSize = ColorPaletteSize1Bit; - } + BmpBitsPerPixel.Pixel8 => ColorPaletteSize8Bit, + BmpBitsPerPixel.Pixel4 => ColorPaletteSize4Bit, + BmpBitsPerPixel.Pixel2 => ColorPaletteSize2Bit, + BmpBitsPerPixel.Pixel1 => ColorPaletteSize1Bit, + _ => 0 + }; byte[] iccProfileData = null; int iccProfileSize = 0; @@ -322,27 +322,31 @@ private void WriteImage(Stream stream, ImageFrame image) switch (this.bitsPerPixel) { case BmpBitsPerPixel.Pixel32: - this.Write32Bit(stream, pixels); + this.Write32BitPixelData(stream, pixels); break; case BmpBitsPerPixel.Pixel24: - this.Write24Bit(stream, pixels); + this.Write24BitPixelData(stream, pixels); break; case BmpBitsPerPixel.Pixel16: - this.Write16Bit(stream, pixels); + this.Write16BitPixelData(stream, pixels); break; case BmpBitsPerPixel.Pixel8: - this.Write8Bit(stream, image); + this.Write8BitPixelData(stream, image); break; case BmpBitsPerPixel.Pixel4: - this.Write4BitColor(stream, image); + this.Write4BitPixelData(stream, image); + break; + + case BmpBitsPerPixel.Pixel2: + this.Write2BitPixelData(stream, image); break; case BmpBitsPerPixel.Pixel1: - this.Write1BitColor(stream, image); + this.Write1BitPixelData(stream, image); break; } } @@ -351,12 +355,12 @@ private IMemoryOwner AllocateRow(int width, int bytesPerPixel) => this.memoryAllocator.AllocatePaddedPixelRowBuffer(width, bytesPerPixel, this.padding); /// - /// Writes the 32bit color palette to the stream. + /// Writes 32-bit data with a color palette to the stream. /// /// The pixel format. /// The to write to. /// The containing pixel data. - private void Write32Bit(Stream stream, Buffer2D pixels) + private void Write32BitPixelData(Stream stream, Buffer2D pixels) where TPixel : unmanaged, IPixel { using IMemoryOwner row = this.AllocateRow(pixels.Width, 4); @@ -375,12 +379,12 @@ private void Write32Bit(Stream stream, Buffer2D pixels) } /// - /// Writes the 24bit color palette to the stream. + /// Writes 24-bit pixel data with a color palette to the stream. /// /// The pixel format. /// The to write to. /// The containing pixel data. - private void Write24Bit(Stream stream, Buffer2D pixels) + private void Write24BitPixelData(Stream stream, Buffer2D pixels) where TPixel : unmanaged, IPixel { int width = pixels.Width; @@ -401,12 +405,12 @@ private void Write24Bit(Stream stream, Buffer2D pixels) } /// - /// Writes the 16bit color palette to the stream. + /// Writes 16-bit pixel data with a color palette to the stream. /// /// The type of the pixel. /// The to write to. /// The containing pixel data. - private void Write16Bit(Stream stream, Buffer2D pixels) + private void Write16BitPixelData(Stream stream, Buffer2D pixels) where TPixel : unmanaged, IPixel { int width = pixels.Width; @@ -429,12 +433,12 @@ private void Write16Bit(Stream stream, Buffer2D pixels) } /// - /// Writes an 8 bit image with a color palette. The color palette has 256 entry's with 4 bytes for each entry. + /// Writes 8 bit pixel data with a color palette. The color palette has 256 entry's with 4 bytes for each entry. /// /// The type of the pixel. /// The to write to. /// The containing pixel data. - private void Write8Bit(Stream stream, ImageFrame image) + private void Write8BitPixelData(Stream stream, ImageFrame image) where TPixel : unmanaged, IPixel { bool isL8 = typeof(TPixel) == typeof(L8); @@ -443,7 +447,7 @@ private void Write8Bit(Stream stream, ImageFrame image) if (isL8) { - this.Write8BitGray(stream, image, colorPalette); + this.Write8BitPixelData(stream, image, colorPalette); } else { @@ -480,13 +484,13 @@ private void Write8BitColor(Stream stream, ImageFrame image, Spa } /// - /// Writes an 8 bit gray image with a color palette. The color palette has 256 entry's with 4 bytes for each entry. + /// Writes 8 bit gray pixel data with a color palette. The color palette has 256 entry's with 4 bytes for each entry. /// /// The type of the pixel. /// The to write to. /// The containing pixel data. /// A byte span of size 1024 for the color palette. - private void Write8BitGray(Stream stream, ImageFrame image, Span colorPalette) + private void Write8BitPixelData(Stream stream, ImageFrame image, Span colorPalette) where TPixel : unmanaged, IPixel { // Create a color palette with 256 different gray values. @@ -518,12 +522,12 @@ private void Write8BitGray(Stream stream, ImageFrame image, Span } /// - /// Writes an 4 bit color image with a color palette. The color palette has 16 entry's with 4 bytes for each entry. + /// Writes 4 bit pixel data with a color palette. The color palette has 16 entry's with 4 bytes for each entry. /// /// The type of the pixel. /// The to write to. /// The containing pixel data. - private void Write4BitColor(Stream stream, ImageFrame image) + private void Write4BitPixelData(Stream stream, ImageFrame image) where TPixel : unmanaged, IPixel { using IQuantizer frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer(this.configuration, new QuantizerOptions() @@ -562,12 +566,65 @@ private void Write4BitColor(Stream stream, ImageFrame image) } /// - /// Writes a 1 bit image with a color palette. The color palette has 2 entry's with 4 bytes for each entry. + /// Writes 2 bit pixel data with a color palette. The color palette has 4 entry's with 4 bytes for each entry. + /// + /// The type of the pixel. + /// The to write to. + /// The containing pixel data. + private void Write2BitPixelData(Stream stream, ImageFrame image) + where TPixel : unmanaged, IPixel + { + using IQuantizer frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer(this.configuration, new QuantizerOptions() + { + MaxColors = 4 + }); + using IndexedImageFrame quantized = frameQuantizer.BuildPaletteAndQuantizeFrame(image, image.Bounds()); + using IMemoryOwner colorPaletteBuffer = this.memoryAllocator.Allocate(ColorPaletteSize2Bit, AllocationOptions.Clean); + + Span colorPalette = colorPaletteBuffer.GetSpan(); + ReadOnlySpan quantizedColorPalette = quantized.Palette.Span; + this.WriteColorPalette(stream, quantizedColorPalette, colorPalette); + + ReadOnlySpan pixelRowSpan = quantized.DangerousGetRowSpan(0); + int rowPadding = pixelRowSpan.Length % 4 != 0 ? this.padding - 1 : this.padding; + for (int y = image.Height - 1; y >= 0; y--) + { + pixelRowSpan = quantized.DangerousGetRowSpan(y); + + int endIdx = pixelRowSpan.Length % 4 == 0 ? pixelRowSpan.Length : pixelRowSpan.Length - 4; + int i = 0; + for (i = 0; i < endIdx; i += 4) + { + stream.WriteByte((byte)((pixelRowSpan[i] << 6) | (pixelRowSpan[i + 1] << 4) | (pixelRowSpan[i + 2] << 2) | pixelRowSpan[i + 3])); + } + + if (pixelRowSpan.Length % 4 != 0) + { + int shift = 6; + byte pixelData = 0; + for (; i < pixelRowSpan.Length; i++) + { + pixelData = (byte)(pixelData | (pixelRowSpan[i] << shift)); + shift -= 2; + } + + stream.WriteByte(pixelData); + } + + for (i = 0; i < rowPadding; i++) + { + stream.WriteByte(0); + } + } + } + + /// + /// Writes 1 bit pixel data with a color palette. The color palette has 2 entry's with 4 bytes for each entry. /// /// The type of the pixel. /// The to write to. /// The containing pixel data. - private void Write1BitColor(Stream stream, ImageFrame image) + private void Write1BitPixelData(Stream stream, ImageFrame image) where TPixel : unmanaged, IPixel { using IQuantizer frameQuantizer = this.quantizer.CreatePixelSpecificQuantizer(this.configuration, new QuantizerOptions() @@ -622,7 +679,7 @@ private void WriteColorPalette(Stream stream, ReadOnlySpan quant Span colorPaletteAsUInt = MemoryMarshal.Cast(colorPalette); for (int i = 0; i < colorPaletteAsUInt.Length; i++) { - colorPaletteAsUInt[i] = colorPaletteAsUInt[i] & 0x00FFFFFF; // Padding byte, always 0. + colorPaletteAsUInt[i] &= 0x00FFFFFF; // Padding byte, always 0. } stream.Write(colorPalette); diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs index e303cb5164..248c7c41cf 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpDecoderTests.cs @@ -121,6 +121,19 @@ public void BmpDecoder_CanDecode_1Bit(TestImageProvider provider image.CompareToOriginal(provider, new SystemDrawingReferenceDecoder()); } + [Theory] + [WithFile(Bit2, PixelTypes.Rgba32)] + [WithFile(Bit2Color, PixelTypes.Rgba32)] + public void BmpDecoder_CanDecode_2Bit(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(BmpDecoder); + image.DebugSave(provider); + + // Reference decoder cant decode 2-bit, compare to reference output instead. + image.CompareToReferenceOutput(provider, extension: "png"); + } + [Theory] [WithFile(Bit4, PixelTypes.Rgba32)] public void BmpDecoder_CanDecode_4Bit(TestImageProvider provider) @@ -266,8 +279,9 @@ public void BmpDecoder_CanDecode_RunLengthEncoded_24Bit(TestImageProvide using Image image = provider.GetImage(BmpDecoder, options); image.DebugSave(provider); - // TODO: Neither System.Drawing nor MagickReferenceDecoder decode this file. - // image.CompareToOriginal(provider); + // Neither System.Drawing nor MagickReferenceDecoder decode this file. + // Compare to reference output instead. + image.CompareToReferenceOutput(provider, extension: "png"); } [Theory] @@ -278,8 +292,9 @@ public void BmpDecoder_CanDecodeAlphaBitfields(TestImageProvider using Image image = provider.GetImage(BmpDecoder); image.DebugSave(provider); - // TODO: Neither System.Drawing nor MagickReferenceDecoder decode this file. - // image.CompareToOriginal(provider); + // Neither System.Drawing nor MagickReferenceDecoder decode this file. + // Compare to reference output instead. + image.CompareToReferenceOutput(provider, extension: "png"); } [Theory] @@ -512,8 +527,9 @@ public void BmpDecoder_CanDecode_Os2v2XShortHeader(TestImageProvider image = provider.GetImage(BmpDecoder); image.DebugSave(provider); - // TODO: Neither System.Drawing or MagickReferenceDecoder can correctly decode this file. - // image.CompareToOriginal(provider); + // Neither System.Drawing or MagickReferenceDecoder can correctly decode this file. + // Compare to reference output instead. + image.CompareToReferenceOutput(provider, extension: "png"); } [Theory] @@ -524,10 +540,9 @@ public void BmpDecoder_CanDecode_Os2v2Header(TestImageProvider p using Image image = provider.GetImage(BmpDecoder); image.DebugSave(provider); - // TODO: System.Drawing can not decode this image. MagickReferenceDecoder can decode it, - // but i think incorrectly. I have loaded the image with GIMP and exported as PNG. - // The results are the same as the image sharp implementation. - // image.CompareToOriginal(provider, new MagickReferenceDecoder()); + // System.Drawing can not decode this image. MagickReferenceDecoder can decode it, + // Compare to reference output instead. + image.CompareToReferenceOutput(provider, extension: "png"); } [Theory] @@ -546,8 +561,9 @@ public void BmpDecoder_CanDecode_Os2BitmapArray(TestImageProvider image = provider.GetImage(BmpDecoder); image.DebugSave(provider); - // TODO: Neither System.Drawing or MagickReferenceDecoder can correctly decode this file. - // image.CompareToOriginal(provider); + // Neither System.Drawing or MagickReferenceDecoder can correctly decode this file. + // Compare to reference output instead. + image.CompareToReferenceOutput(provider, extension: "png"); } } } diff --git a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs index 5dd712c1ff..bb88cc4629 100644 --- a/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Bmp/BmpEncoderTests.cs @@ -20,6 +20,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.Bmp [Trait("Format", "Bmp")] public class BmpEncoderTests { + private static BmpDecoder BmpDecoder => new(); + + private static BmpEncoder BmpEncoder => new(); + public static readonly TheoryData BitsPerPixel = new() { @@ -39,6 +43,7 @@ public class BmpEncoderTests new() { { Bit1, BmpBitsPerPixel.Pixel1 }, + { Bit2, BmpBitsPerPixel.Pixel2 }, { Bit4, BmpBitsPerPixel.Pixel4 }, { Bit8, BmpBitsPerPixel.Pixel8 }, { Rgb16, BmpBitsPerPixel.Pixel16 }, @@ -50,12 +55,10 @@ public class BmpEncoderTests [MemberData(nameof(RatioFiles))] public void Encode_PreserveRatio(string imagePath, int xResolution, int yResolution, PixelResolutionUnit resolutionUnit) { - var options = new BmpEncoder(); - var testFile = TestFile.Create(imagePath); using Image input = testFile.CreateRgba32Image(); using var memStream = new MemoryStream(); - input.Save(memStream, options); + input.Save(memStream, BmpEncoder); memStream.Position = 0; using var output = Image.Load(memStream); @@ -69,12 +72,10 @@ public void Encode_PreserveRatio(string imagePath, int xResolution, int yResolut [MemberData(nameof(BmpBitsPerPixelFiles))] public void Encode_PreserveBitsPerPixel(string imagePath, BmpBitsPerPixel bmpBitsPerPixel) { - var options = new BmpEncoder(); - var testFile = TestFile.Create(imagePath); using Image input = testFile.CreateRgba32Image(); using var memStream = new MemoryStream(); - input.Save(memStream, options); + input.Save(memStream, BmpEncoder); memStream.Position = 0; using var output = Image.Load(memStream); @@ -192,6 +193,50 @@ public void Encode_4Bit_WithV4Header_Works( TestBmpEncoderCore(provider, bitsPerPixel, supportTransparency: true, customComparer: comparer); } + [Theory] + [WithFile(Bit2, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel2)] + public void Encode_2Bit_WithV3Header_Works( + TestImageProvider provider, + BmpBitsPerPixel bitsPerPixel) + where TPixel : unmanaged, IPixel + { + // arrange + var encoder = new BmpEncoder() { BitsPerPixel = bitsPerPixel }; + using var memoryStream = new MemoryStream(); + using Image input = provider.GetImage(BmpDecoder); + + // act + encoder.Encode(input, memoryStream); + memoryStream.Position = 0; + + // assert + using var actual = Image.Load(memoryStream); + ImageSimilarityReport similarityReport = ImageComparer.Exact.CompareImagesOrFrames(input, actual); + Assert.True(similarityReport.IsEmpty, "encoded image does not match reference image"); + } + + [Theory] + [WithFile(Bit2, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel2)] + public void Encode_2Bit_WithV4Header_Works( + TestImageProvider provider, + BmpBitsPerPixel bitsPerPixel) + where TPixel : unmanaged, IPixel + { + // arrange + var encoder = new BmpEncoder() { BitsPerPixel = bitsPerPixel }; + using var memoryStream = new MemoryStream(); + using Image input = provider.GetImage(BmpDecoder); + + // act + encoder.Encode(input, memoryStream); + memoryStream.Position = 0; + + // assert + using var actual = Image.Load(memoryStream); + ImageSimilarityReport similarityReport = ImageComparer.Exact.CompareImagesOrFrames(input, actual); + Assert.True(similarityReport.IsEmpty, "encoded image does not match reference image"); + } + [Theory] [WithFile(Bit1, PixelTypes.Rgba32, BmpBitsPerPixel.Pixel1)] public void Encode_1Bit_WithV3Header_Works( @@ -320,7 +365,8 @@ private static void TestBmpEncoderCore( BmpBitsPerPixel bitsPerPixel, bool supportTransparency = true, // if set to true, will write a V4 header, otherwise a V3 header. IQuantizer quantizer = null, - ImageComparer customComparer = null) + ImageComparer customComparer = null, + IImageDecoder referenceDecoder = null) where TPixel : unmanaged, IPixel { using Image image = provider.GetImage(); diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index ddaf672b43..0f9479a764 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -347,6 +347,8 @@ public static class Bmp public const string RLE4Delta = "Bmp/pal4rletrns.bmp"; public const string Rle4Delta320240 = "Bmp/rle4-delta-320x240.bmp"; public const string Bit1 = "Bmp/pal1.bmp"; + public const string Bit2 = "Bmp/pal2.bmp"; + public const string Bit2Color = "Bmp/pal2color.bmp"; public const string Bit1Pal1 = "Bmp/pal1p1.bmp"; public const string Bit4 = "Bmp/pal4.bmp"; public const string Bit8 = "Bmp/test8.bmp"; diff --git a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparer.cs b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparer.cs index cea2784b67..d2750c31c5 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparer.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImageComparison/ImageComparer.cs @@ -19,10 +19,8 @@ public abstract class ImageComparer /// A ImageComparer instance. public static ImageComparer Tolerant( float imageThreshold = TolerantImageComparer.DefaultImageThreshold, - int perPixelManhattanThreshold = 0) - { - return new TolerantImageComparer(imageThreshold, perPixelManhattanThreshold); - } + int perPixelManhattanThreshold = 0) => + new TolerantImageComparer(imageThreshold, perPixelManhattanThreshold); /// /// Returns Tolerant(imageThresholdInPercents/100) @@ -45,10 +43,7 @@ public static ImageSimilarityReport CompareImagesOrFrames expected, Image actual) where TPixelA : unmanaged, IPixel - where TPixelB : unmanaged, IPixel - { - return comparer.CompareImagesOrFrames(expected.Frames.RootFrame, actual.Frames.RootFrame); - } + where TPixelB : unmanaged, IPixel => comparer.CompareImagesOrFrames(expected.Frames.RootFrame, actual.Frames.RootFrame); public static IEnumerable> CompareImages( this ImageComparer comparer, diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecodeAlphaBitfields_Rgba32_rgba32abf.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecodeAlphaBitfields_Rgba32_rgba32abf.png new file mode 100644 index 0000000000..40613ca7e5 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecodeAlphaBitfields_Rgba32_rgba32abf.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1c7c5d24cf8ba473a22d1c12dcd196f626d2ef056a35bb3ff54b5c84516544bf +size 14547 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_2Bit_Rgba32_pal2.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_2Bit_Rgba32_pal2.png new file mode 100644 index 0000000000..4a1ac40887 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_2Bit_Rgba32_pal2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2c3e8b87af737c40d7be02e55a2aec93bb0e7bd123cd1f3e3b74482a0c7d18bd +size 2376 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_2Bit_Rgba32_pal2color.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_2Bit_Rgba32_pal2color.png new file mode 100644 index 0000000000..6f7bd68696 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_2Bit_Rgba32_pal2color.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c3da68e15f4edf6ce5da76360f3704d52baff5292ee12efe5415540b5788dda5 +size 2578 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_9S.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_9S.png new file mode 100644 index 0000000000..0319b97182 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_9S.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8597af653507fb625a8f387ce01ab900603086892f046b7b92e6fcf60a636295 +size 884 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_DIAMOND.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_DIAMOND.png new file mode 100644 index 0000000000..630b44ecd2 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_DIAMOND.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0a6989978a0fe36399a774000ee04336d090a4e6a2b63bcbfcd45312ccac4dab +size 648 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_GMARBLE.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_GMARBLE.png new file mode 100644 index 0000000000..ff484218d3 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_GMARBLE.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:951d9d48a5b5df5b70a8c217e2a3d94f4b2c8e8cc63d70cb807627b8e98b8b1d +size 20567 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_PINES.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_PINES.png new file mode 100644 index 0000000000..78ffbe76c5 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_PINES.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:35dc46a1f19f3f0a91948bee9b173f6ce264ade69754c01b688e2a878f1374a9 +size 21406 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_SKATER.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_SKATER.png new file mode 100644 index 0000000000..7b6ec01dcf --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_SKATER.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3191c0ac33c1749f770f96814c0585715aa1c0b085f02256317cedeabc531c12 +size 636 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_SPADE.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_SPADE.png new file mode 100644 index 0000000000..89bf24a228 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_SPADE.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:50c5f1adb8b9f0f9a111fdd4b04df023d4239d409f93e2ab5823352c02761118 +size 802 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_SUNFLOW.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_SUNFLOW.png new file mode 100644 index 0000000000..5e7a2c071a --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_SUNFLOW.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8f0f9b6a5f1a36596fbe8ac1416e69af82e24c5892a8012a6b68206b6e467bec +size 14190 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_WARPD.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_WARPD.png new file mode 100644 index 0000000000..6a62cc9c71 --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_WARPD.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:452e8aeca41c0899f4e7a4f0458f7cf2dd8002e42a752708d7dd308e040641a0 +size 103892 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_ba-bm.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_ba-bm.png new file mode 100644 index 0000000000..2c9fab29fe --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2BitmapArray_Rgba32_ba-bm.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9e1fc90acab9db3469b673036c8cdcf5920ca3d12915a412280f09a86a14ad2e +size 5081 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2v2Header_Rgba32_pal8os2v2.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2v2Header_Rgba32_pal8os2v2.png new file mode 100644 index 0000000000..2c9fab29fe --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2v2Header_Rgba32_pal8os2v2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9e1fc90acab9db3469b673036c8cdcf5920ca3d12915a412280f09a86a14ad2e +size 5081 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2v2XShortHeader_Rgba32_pal8os2v2-16.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2v2XShortHeader_Rgba32_pal8os2v2-16.png new file mode 100644 index 0000000000..2c9fab29fe --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_Os2v2XShortHeader_Rgba32_pal8os2v2-16.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9e1fc90acab9db3469b673036c8cdcf5920ca3d12915a412280f09a86a14ad2e +size 5081 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_RunLengthEncoded_24Bit_Rgba32_rgb24rle24.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_RunLengthEncoded_24Bit_Rgba32_rgb24rle24.png new file mode 100644 index 0000000000..2c9fab29fe --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_RunLengthEncoded_24Bit_Rgba32_rgb24rle24.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9e1fc90acab9db3469b673036c8cdcf5920ca3d12915a412280f09a86a14ad2e +size 5081 diff --git a/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_RunLengthEncoded_24Bit_Rgba32_rle24rlecut.png b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_RunLengthEncoded_24Bit_Rgba32_rle24rlecut.png new file mode 100644 index 0000000000..8311bc95be --- /dev/null +++ b/tests/Images/External/ReferenceOutput/BmpDecoderTests/BmpDecoder_CanDecode_RunLengthEncoded_24Bit_Rgba32_rle24rlecut.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:13d9b630227069f3fd744ef486d64d3f997ee0a9844824e9986c55d754bf413c +size 4379 diff --git a/tests/Images/Input/Bmp/pal2.bmp b/tests/Images/Input/Bmp/pal2.bmp new file mode 100644 index 0000000000..ac351d5fb6 --- /dev/null +++ b/tests/Images/Input/Bmp/pal2.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bac6eec4100831e635fcd34a9e0e34a8a9082abdec132ac327aa1bfc7137d40f +size 2118 diff --git a/tests/Images/Input/Bmp/pal2color.bmp b/tests/Images/Input/Bmp/pal2color.bmp new file mode 100644 index 0000000000..dd7c31bf67 --- /dev/null +++ b/tests/Images/Input/Bmp/pal2color.bmp @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6ac541592afb207524091aa19d59614851c293193600eacb1170b4854d351dae +size 2118