// Copyright (c) Six Labors. // Licensed under the Six Labors Split License. using System.Text; using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Pbm; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; using SixLabors.ImageSharp.Tests.TestUtilities.ReferenceCodecs; using static SixLabors.ImageSharp.Tests.TestImages.Pbm; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.Formats.Pbm; [Trait("Format", "Pbm")] [ValidateDisposedMemoryAllocations] public class PbmDecoderTests { [Theory] [InlineData(BlackAndWhitePlain, PbmColorType.BlackAndWhite, PbmComponentType.Bit)] [InlineData(BlackAndWhiteBinary, PbmColorType.BlackAndWhite, PbmComponentType.Bit)] [InlineData(GrayscalePlain, PbmColorType.Grayscale, PbmComponentType.Byte)] [InlineData(GrayscalePlainMagick, PbmColorType.Grayscale, PbmComponentType.Byte)] [InlineData(GrayscaleBinary, PbmColorType.Grayscale, PbmComponentType.Byte)] [InlineData(GrayscaleBinaryWide, PbmColorType.Grayscale, PbmComponentType.Short)] [InlineData(RgbPlain, PbmColorType.Rgb, PbmComponentType.Byte)] [InlineData(RgbPlainMagick, PbmColorType.Rgb, PbmComponentType.Byte)] [InlineData(RgbBinary, PbmColorType.Rgb, PbmComponentType.Byte)] [InlineData(RgbBinaryWide, PbmColorType.Rgb, PbmComponentType.Short)] public void ImageLoadCanDecode(string imagePath, PbmColorType expectedColorType, PbmComponentType expectedComponentType) { // Arrange TestFile testFile = TestFile.Create(imagePath); using MemoryStream stream = new(testFile.Bytes, false); // Act using Image image = Image.Load(stream); // Assert Assert.NotNull(image); PbmMetadata metadata = image.Metadata.GetPbmMetadata(); Assert.NotNull(metadata); Assert.Equal(expectedColorType, metadata.ColorType); Assert.Equal(expectedComponentType, metadata.ComponentType); } [Theory] [InlineData(BlackAndWhitePlain)] [InlineData(BlackAndWhiteBinary)] [InlineData(GrayscalePlain)] [InlineData(GrayscalePlainMagick)] [InlineData(GrayscaleBinary)] [InlineData(GrayscaleBinaryWide)] public void ImageLoadL8CanDecode(string imagePath) { // Arrange TestFile testFile = TestFile.Create(imagePath); using MemoryStream stream = new(testFile.Bytes, false); // Act using Image image = Image.Load(stream); // Assert Assert.NotNull(image); } [Theory] [InlineData(RgbPlain)] [InlineData(RgbPlainMagick)] [InlineData(RgbBinary)] public void ImageLoadRgb24CanDecode(string imagePath) { // Arrange TestFile testFile = TestFile.Create(imagePath); using MemoryStream stream = new(testFile.Bytes, false); // Act using Image image = Image.Load(stream); // Assert Assert.NotNull(image); } [Theory] [WithFile(BlackAndWhitePlain, PixelTypes.L8, "pbm")] [WithFile(BlackAndWhiteBinary, PixelTypes.L8, "pbm")] [WithFile(Issue2477, PixelTypes.L8, "pbm")] [WithFile(GrayscalePlain, PixelTypes.L8, "pgm")] [WithFile(GrayscalePlainNormalized, PixelTypes.L8, "pgm")] [WithFile(GrayscaleBinary, PixelTypes.L8, "pgm")] [WithFile(GrayscaleBinaryWide, PixelTypes.L16, "pgm")] [WithFile(RgbPlain, PixelTypes.Rgb24, "ppm")] [WithFile(RgbPlainNormalized, PixelTypes.Rgb24, "ppm")] [WithFile(RgbBinary, PixelTypes.Rgb24, "ppm")] [WithFile(RgbBinaryWide, PixelTypes.Rgb48, "ppm")] public void DecodeReferenceImage(TestImageProvider provider, string extension) where TPixel : unmanaged, IPixel { using Image image = provider.GetImage(); image.DebugSave(provider, extension: extension); bool isGrayscale = extension is "pgm" or "pbm"; image.CompareToReferenceOutput(provider, grayscale: isGrayscale); } [Theory] [WithFile(GrayscaleBinaryWide, PixelTypes.Rgb48)] [WithFile(RgbBinaryWide, PixelTypes.Rgb48)] public void Decode_WideBinary_MatchesReferenceDecoder(TestImageProvider provider) where TPixel : unmanaged, IPixel { using Image image = provider.GetImage(PbmDecoder.Instance); image.CompareToOriginal(provider, ImageComparer.Exact, new MagickReferenceDecoder(PbmFormat.Instance)); } [Fact] public void Decode_WideBinaryGrayscale_SamplesAreBigEndian() { // Per the Netpbm specification, 16-bit samples store the most significant byte first. byte[] header = Encoding.ASCII.GetBytes("P5\n2 1\n65535\n"); byte[] samples = [0x80, 0x00, 0x00, 0x80]; byte[] data = [.. header, .. samples]; using Image image = Image.Load(data); Assert.Equal(0x8000, image[0, 0].PackedValue); Assert.Equal(0x0080, image[1, 0].PackedValue); } [Fact] public void Decode_WideBinaryRgb_SamplesAreBigEndian() { // Per the Netpbm specification, 16-bit samples store the most significant byte first. byte[] header = Encoding.ASCII.GetBytes("P6\n1 1\n65535\n"); byte[] samples = [0x81, 0xB5, 0x84, 0x91, 0x86, 0x71]; byte[] data = [.. header, .. samples]; using Image image = Image.Load(data); Assert.Equal(new Rgb48(0x81B5, 0x8491, 0x8671), image[0, 0]); } [Fact] public void Decode_NonStandardByteMaxPixelValue_UpscalesToFullRange() { byte[] data = Encoding.ASCII.GetBytes("P2\n1 1\n100\n100"); using Image image = Image.Load(data); Assert.Equal(255, image[0, 0].PackedValue); } [Fact] public void Decode_NonStandardShortMaxPixelValue_UpscalesToFullRange() { byte[] header = Encoding.ASCII.GetBytes("P5\n1 1\n1000\n"); byte[] samples = [0x03, 0xE8]; byte[] data = [.. header, .. samples]; using Image image = Image.Load(data); Assert.Equal(65535, image[0, 0].PackedValue); } [Theory] [WithFile(RgbPlain, PixelTypes.Rgb24)] public void PbmDecoder_Decode_Resize(TestImageProvider provider) where TPixel : unmanaged, IPixel { DecoderOptions options = new() { TargetSize = new Size { Width = 150, Height = 150 } }; using Image image = provider.GetImage(PbmDecoder.Instance, options); FormattableString details = $"{options.TargetSize.Value.Width}_{options.TargetSize.Value.Height}"; image.DebugSave(provider, testOutputDetails: details, appendPixelTypeToFileName: false); image.CompareToReferenceOutput( ImageComparer.Exact, provider, testOutputDetails: details, appendPixelTypeToFileName: false); } [Theory] [InlineData("P2")] [InlineData("P3")] [InlineData("P5")] [InlineData("P6")] public void Decode_MaxPixelValueZero_ThrowsInvalidImageContentException(string magic) { byte[] bytes = Encoding.ASCII.GetBytes($"{magic} 1 1 0\n0"); using MemoryStream stream = new(bytes); Assert.Throws(() => Image.Load(stream)); } [Fact] public void PlainText_PrematureEof() { byte[] bytes = Encoding.ASCII.GetBytes($"P1\n100 100\n1 0 1 0 1 0"); using EofHitCounter eofHitCounter = EofHitCounter.RunDecoder(bytes); Assert.True(eofHitCounter.EofHitCount <= 2); Assert.Equal(new Size(100, 100), eofHitCounter.Image.Size); } [Fact] public void Binary_PrematureEof() { using EofHitCounter eofHitCounter = EofHitCounter.RunDecoder(RgbBinaryPrematureEof); Assert.True(eofHitCounter.EofHitCount <= 2); Assert.Equal(new Size(29, 30), eofHitCounter.Image.Size); } }