diff --git a/src/ImageSharp/Formats/Png/PngDecoderCore.cs b/src/ImageSharp/Formats/Png/PngDecoderCore.cs index 7df41d31ea..b54e697e9e 100644 --- a/src/ImageSharp/Formats/Png/PngDecoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngDecoderCore.cs @@ -216,6 +216,8 @@ protected override Image Decode(BufferedReadStream stream, Cance break; case PngChunkType.FrameData: { + ValidateFrameDataChunk(chunk); + if (frameCount > this.maxFrames) { goto EOF; @@ -258,10 +260,10 @@ protected override Image Decode(BufferedReadStream stream, Cance currentFrameControl ??= new FrameControl((uint)this.header.Width, (uint)this.header.Height); if (image is null) { + // PLTE and tRNS precede IDAT. Validate the required indexed palette before + // allocating pixel storage, outside recovery from compressed-data errors. + this.AssignColorPalette(this.palette, this.paletteAlpha, pngMetadata); this.InitializeImage(metadata, currentFrameControl.Value, out image); - - // Both PLTE and tRNS chunks, if present, have been read at this point as per spec. - AssignColorPalette(this.palette, this.paletteAlpha, pngMetadata); } this.ReadScanlines( @@ -413,6 +415,8 @@ protected override ImageInfo Identify(BufferedReadStream stream, CancellationTok break; case PngChunkType.FrameData: + ValidateFrameDataChunk(chunk); + if (frameCount > this.maxFrames) { // Must skip the chunk data even when we've hit maxFrames, because TryReadChunk @@ -454,7 +458,7 @@ protected override ImageInfo Identify(BufferedReadStream stream, CancellationTok InitializeFrameMetadata(framesMetadata, currentFrameControl.Value); // Both PLTE and tRNS chunks, if present, have been read at this point as per spec. - AssignColorPalette(this.palette, this.paletteAlpha, pngMetadata); + this.AssignColorPalette(this.palette, this.paletteAlpha, pngMetadata); } this.SkipChunkDataAndCrc(chunk); @@ -1331,15 +1335,22 @@ private void ProcessInterlacedDefilteredScanline( } /// - /// Decodes and assigns the color palette to the metadata + /// Assigns the palette to metadata and rejects indexed images without a complete palette entry. /// /// The palette buffer. /// The alpha palette buffer. /// The png metadata. - private static void AssignColorPalette(ReadOnlySpan palette, ReadOnlySpan alpha, PngMetadata pngMetadata) + private void AssignColorPalette(ReadOnlySpan palette, ReadOnlySpan alpha, PngMetadata pngMetadata) { - if (palette.Length == 0) + // Indexed samples require at least one complete RGB entry. Reject a missing table before scanline + // recovery can suppress the error in IgnoreImageData mode. Other color types have an optional palette. + if (palette.Length < Unsafe.SizeOf()) { + if (this.pngColorType is PngColorType.Palette) + { + PngThrowHelper.ThrowMissingPalette(); + } + return; } @@ -1380,9 +1391,11 @@ private void AssignTransparentMarkers(ReadOnlySpan alpha, PngMetadata pngM { if (this.header.BitDepth == 16) { - ushort rc = BinaryPrimitives.ReadUInt16LittleEndian(alpha[..2]); - ushort gc = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(2, 2)); - ushort bc = BinaryPrimitives.ReadUInt16LittleEndian(alpha.Slice(4, 2)); + // tRNS samples are stored big-endian like all other PNG multi-byte values, + // matching the byte order used for the 16-bit samples in the image data. + ushort rc = BinaryPrimitives.ReadUInt16BigEndian(alpha[..2]); + ushort gc = BinaryPrimitives.ReadUInt16BigEndian(alpha.Slice(2, 2)); + ushort bc = BinaryPrimitives.ReadUInt16BigEndian(alpha.Slice(4, 2)); pngMetadata.TransparentColor = Color.FromPixel(new Rgb48(rc, gc, bc)); return; @@ -1400,7 +1413,7 @@ private void AssignTransparentMarkers(ReadOnlySpan alpha, PngMetadata pngM { if (this.header.BitDepth == 16) { - pngMetadata.TransparentColor = Color.FromPixel(new L16(BinaryPrimitives.ReadUInt16LittleEndian(alpha[..2]))); + pngMetadata.TransparentColor = Color.FromPixel(new L16(BinaryPrimitives.ReadUInt16BigEndian(alpha[..2]))); } else { @@ -2183,24 +2196,32 @@ private int ReadNextDataChunk() Span buffer = stackalloc byte[20]; - int length = this.currentStream.Read(buffer, 0, 4); - if (length == 0) + // The compressed stream is the concatenation of the payloads of all consecutive data chunks, and the + // specification permits zero-length data chunks. A return value of 0 tells the inflater that the + // compressed stream has ended, so empty chunks are skipped rather than reported. + while (true) { - return 0; - } + // Skip the CRC of the previous chunk. + int length = this.currentStream.Read(buffer, 0, 4); + if (length == 0 || !this.TryReadChunk(buffer, out PngChunk chunk)) + { + return 0; + } - if (this.TryReadChunk(buffer, out PngChunk chunk)) - { - if (chunk.Type is PngChunkType.Data or PngChunkType.FrameData) + if (chunk.Type is not PngChunkType.Data) { - chunk.Data?.Dispose(); - return chunk.Length; + // Only IDAT payloads belong to this stream. Frame data must return to the outer loop + // for its own framing checks, outside recovery from compressed-data errors. + this.nextChunk = chunk; + return 0; } - this.nextChunk = chunk; + chunk.Data?.Dispose(); + if (chunk.Length > 0) + { + return chunk.Length; + } } - - return 0; } /// @@ -2216,26 +2237,34 @@ private int ReadNextFrameDataChunk() Span buffer = stackalloc byte[20]; - int length = this.currentStream.Read(buffer, 0, 4); - if (length == 0) - { - return 0; - } - - if (this.TryReadChunk(buffer, out PngChunk chunk)) + // As with data chunks, frame data chunks with no payload after the sequence number are valid and are + // skipped, so that a return value of 0 only ever means that the frame's compressed stream has ended. + while (true) { - if (chunk.Type is PngChunkType.FrameData) + // Skip the CRC of the previous chunk. + int length = this.currentStream.Read(buffer, 0, 4); + if (length == 0 || !this.TryReadChunk(buffer, out PngChunk chunk)) { - chunk.Data?.Dispose(); + return 0; + } - this.currentStream.Position += 4; // Skip sequence number - return chunk.Length - 4; + if (chunk.Type is not PngChunkType.FrameData || chunk.Length < sizeof(uint)) + { + // A short fdAT cannot supply compressed data. Let the outer chunk loop validate it outside + // image-data recovery, just as that loop handles the next frame control or end chunk. + this.nextChunk = chunk; + return 0; } - this.nextChunk = chunk; - } + chunk.Data?.Dispose(); - return 0; + this.currentStream.Position += 4; // Skip sequence number + int dataLength = chunk.Length - 4; + if (dataLength > 0) + { + return dataLength; + } + } } /// @@ -2249,20 +2278,34 @@ private void SkipRemainingFrameDataChunks(Span buffer) { while (this.TryReadChunk(buffer, out PngChunk chunk)) { - if (chunk.Type is PngChunkType.FrameData) + if (chunk.Type is PngChunkType.FrameData && chunk.Length >= sizeof(uint)) { chunk.Data?.Dispose(); this.SkipChunkDataAndCrc(chunk); } else { - // Not a FrameData chunk; store it so the next TryReadChunk call returns it. + // Preserve other chunks, including malformed fdAT, for validation by the outer chunk loop. this.nextChunk = chunk; return; } } } + /// + /// Checks the required sequence number before consuming a frame data chunk. + /// + /// The frame data chunk. + private static void ValidateFrameDataChunk(in PngChunk chunk) + { + // A missing sequence number is a structural error in every integrity mode. Call this from the outer + // chunk loop, outside the scope that permits recovery from compressed image-data errors. + if (chunk.Length < sizeof(uint)) + { + PngThrowHelper.ThrowInvalidImageContentException("The frame data chunk does not contain a sequence number."); + } + } + /// /// Reads a chunk from the stream. /// diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index caf2393d89..13171c8181 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -1247,10 +1247,11 @@ private void WriteTransparencyChunk(Stream stream, PngMetadata pngMetadata) { if (this.use16Bit) { + // tRNS samples are stored big-endian like all other PNG multi-byte values. Rgb48 rgb = pngMetadata.TransparentColor.Value.ToPixel(); - BinaryPrimitives.WriteUInt16LittleEndian(alpha, rgb.R); - BinaryPrimitives.WriteUInt16LittleEndian(alpha.Slice(2, 2), rgb.G); - BinaryPrimitives.WriteUInt16LittleEndian(alpha.Slice(4, 2), rgb.B); + BinaryPrimitives.WriteUInt16BigEndian(alpha, rgb.R); + BinaryPrimitives.WriteUInt16BigEndian(alpha.Slice(2, 2), rgb.G); + BinaryPrimitives.WriteUInt16BigEndian(alpha.Slice(4, 2), rgb.B); this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer, 0, 6); } @@ -1269,7 +1270,7 @@ private void WriteTransparencyChunk(Stream stream, PngMetadata pngMetadata) if (this.use16Bit) { L16 l16 = pngMetadata.TransparentColor.Value.ToPixel(); - BinaryPrimitives.WriteUInt16LittleEndian(alpha, l16.PackedValue); + BinaryPrimitives.WriteUInt16BigEndian(alpha, l16.PackedValue); this.WriteChunk(stream, PngChunkType.Transparency, this.chunkDataBuffer, 0, 2); } else diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs index 252ea49603..cc3aa8664e 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.Chunks.cs @@ -7,6 +7,7 @@ using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Png; using SixLabors.ImageSharp.PixelFormats; +using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; // ReSharper disable InconsistentNaming namespace SixLabors.ImageSharp.Tests.Formats.Png; @@ -291,6 +292,158 @@ public void Decode_tRnsChunk_WithAlphaLengthGreaterColorTableLength_ShouldNotThr using Image image = Image.Load(stream); } + /// + /// Checks stored IDAT layouts and applies the configured policy to the input's CRC errors. + /// + /// The stored PNG input. + /// The segment integrity policy. + /// Whether the input contains an invalid image-data CRC. + [Theory] + [InlineData(TestImages.Png.IdatLeadingEmpty, SegmentIntegrityHandling.Strict, false)] + [InlineData(TestImages.Png.IdatConsecutiveLeadingEmpty, SegmentIntegrityHandling.Strict, false)] + [InlineData(TestImages.Png.IdatFirstOneByte, SegmentIntegrityHandling.Strict, false)] + [InlineData(TestImages.Png.IdatEmptyWithinHeader, SegmentIntegrityHandling.Strict, false)] + [InlineData(TestImages.Png.IdatEveryOneByte, SegmentIntegrityHandling.Strict, false)] + [InlineData(TestImages.Png.IdatEveryOneByteWithEmpty, SegmentIntegrityHandling.Strict, false)] + [InlineData(TestImages.Png.IdatEmptyBetween, SegmentIntegrityHandling.Strict, false)] + [InlineData(TestImages.Png.IdatTrailingEmpty, SegmentIntegrityHandling.Strict, false)] + [InlineData(TestImages.Png.IdatEmptyBadCrc, SegmentIntegrityHandling.Strict, true)] + [InlineData(TestImages.Png.IdatEmptyBadCrc, SegmentIntegrityHandling.IgnoreAncillary, true)] + [InlineData(TestImages.Png.IdatEmptyBadCrc, SegmentIntegrityHandling.IgnoreImageData, true)] + public void Decode_CompressedDataSplitAcrossDataChunks_FollowsIntegrityPolicy(string path, SegmentIntegrityHandling integrity, bool hasInvalidCrc) + { + byte[] data = TestFile.Create(path).Bytes; + DecoderOptions options = new() { SegmentIntegrityHandling = integrity }; + if (hasInvalidCrc && integrity != SegmentIntegrityHandling.IgnoreImageData) + { + InvalidImageContentException exception = Assert.Throws(() => + { + using Image actual = Image.Load(options, data); + }); + + Assert.Equal("CRC Error. PNG IDAT chunk is corrupt!", exception.Message); + return; + } + + using Image expected = TestFile.Create(TestImages.Png.Noise).CreateRgba32Image(); + using Image actual = Image.Load(options, data); + ImageComparer.Exact.VerifySimilarity(expected, actual); + } + + /// + /// Checks stored empty frame payloads without changing their pixels or bypassing image-data CRC policy. + /// + /// The stored APNG input. + /// The segment integrity policy. + /// Whether the input contains invalid frame-data CRCs. + [Theory] + [InlineData(TestImages.Png.FdatLeadingEmpty, SegmentIntegrityHandling.Strict, false)] + [InlineData(TestImages.Png.FdatConsecutiveLeadingEmpty, SegmentIntegrityHandling.Strict, false)] + [InlineData(TestImages.Png.FdatEmptyWithinHeader, SegmentIntegrityHandling.Strict, false)] + [InlineData(TestImages.Png.FdatTrailingEmpty, SegmentIntegrityHandling.Strict, false)] + [InlineData(TestImages.Png.FdatEmptyBadCrc, SegmentIntegrityHandling.Strict, true)] + [InlineData(TestImages.Png.FdatEmptyBadCrc, SegmentIntegrityHandling.IgnoreAncillary, true)] + [InlineData(TestImages.Png.FdatEmptyBadCrc, SegmentIntegrityHandling.IgnoreImageData, true)] + public void Decode_EmptyFrameDataChunks_FollowsIntegrityPolicy(string path, SegmentIntegrityHandling integrity, bool hasInvalidCrc) + { + byte[] data = TestFile.Create(path).Bytes; + DecoderOptions options = new() { SegmentIntegrityHandling = integrity }; + if (hasInvalidCrc && integrity != SegmentIntegrityHandling.IgnoreImageData) + { + InvalidImageContentException exception = Assert.Throws(() => + { + using Image actual = Image.Load(options, data); + }); + + Assert.Equal("CRC Error. PNG fdAT chunk is corrupt!", exception.Message); + return; + } + + using Image expected = TestFile.Create(TestImages.Png.APng).CreateRgba32Image(); + using Image actual = Image.Load(options, data); + ImageComparer.Exact.VerifySimilarity(expected, actual); + } + + /// + /// Rejects stored frame chunks without a complete sequence number, including chunks within compressed data. + /// + /// The malformed APNG input. + /// The segment integrity policy. + [Theory] + [InlineData(TestImages.Png.FdatShort0, SegmentIntegrityHandling.Strict)] + [InlineData(TestImages.Png.FdatShort3, SegmentIntegrityHandling.Strict)] + [InlineData(TestImages.Png.FdatTruncatedSequenceBetween, SegmentIntegrityHandling.Strict)] + [InlineData(TestImages.Png.FdatShort0, SegmentIntegrityHandling.IgnoreAncillary)] + [InlineData(TestImages.Png.FdatShort3, SegmentIntegrityHandling.IgnoreAncillary)] + [InlineData(TestImages.Png.FdatTruncatedSequenceBetween, SegmentIntegrityHandling.IgnoreAncillary)] + [InlineData(TestImages.Png.FdatShort0, SegmentIntegrityHandling.IgnoreImageData)] + [InlineData(TestImages.Png.FdatShort3, SegmentIntegrityHandling.IgnoreImageData)] + [InlineData(TestImages.Png.FdatTruncatedSequenceBetween, SegmentIntegrityHandling.IgnoreImageData)] + public void DecodeAndIdentify_FrameDataWithoutSequenceNumber_ThrowsInvalidImageContentException(string path, SegmentIntegrityHandling integrity) + { + byte[] data = TestFile.Create(path).Bytes; + DecoderOptions options = new() { SegmentIntegrityHandling = integrity }; + InvalidImageContentException exception = Assert.Throws(() => + { + using Image actual = Image.Load(options, data); + }); + + Assert.Equal("The frame data chunk does not contain a sequence number.", exception.Message); + Assert.Throws(() => Image.Identify(options, data)); + } + + /// + /// Rejects stored indexed PNGs without one complete RGB palette entry in every integrity mode. + /// + /// The malformed PNG input, including its interlace mode and palette length. + /// The segment integrity policy. + [Theory] + [InlineData(TestImages.Png.PaletteShort0, SegmentIntegrityHandling.Strict)] + [InlineData(TestImages.Png.PaletteShort1, SegmentIntegrityHandling.Strict)] + [InlineData(TestImages.Png.PaletteShort2, SegmentIntegrityHandling.Strict)] + [InlineData(TestImages.Png.PaletteAdam7Short0, SegmentIntegrityHandling.Strict)] + [InlineData(TestImages.Png.PaletteAdam7Short1, SegmentIntegrityHandling.Strict)] + [InlineData(TestImages.Png.PaletteAdam7Short2, SegmentIntegrityHandling.Strict)] + [InlineData(TestImages.Png.PaletteShort2, SegmentIntegrityHandling.IgnoreAncillary)] + [InlineData(TestImages.Png.PaletteShort2, SegmentIntegrityHandling.IgnoreImageData)] + public void Decode_PaletteChunkShorterThanOneEntry_ThrowsInvalidImageContentException(string path, SegmentIntegrityHandling integrity) + { + DecoderOptions options = new() { SegmentIntegrityHandling = integrity }; + Assert.Throws(() => + { + using Image actual = Image.Load(options, TestFile.Create(path).Bytes); + }); + } + + /// + /// Checks that the stored 16-bit marker 0x0102 selects that gray sample, not the byte-swapped sample 0x0201. + /// + [Fact] + public void Decode_16BitGrayscaleTransparency_IsBigEndian() + { + byte[] data = TestFile.Create(TestImages.Png.TrnsGray16BigEndian).Bytes; + Assert.Equal(new L16(0x0102), Image.Identify(data).Metadata.GetPngMetadata().TransparentColor?.ToPixel()); + + using Image image = Image.Load(data); + Assert.Equal(ushort.MinValue, image[0, 0].A); + Assert.Equal(ushort.MaxValue, image[1, 0].A); + } + + /// + /// Checks that the stored marker (0x0102, 0x0304, 0x0506) selects that RGB sample, not its byte-swapped sample. + /// + [Fact] + public void Decode_16BitRgbTransparency_IsBigEndian() + { + byte[] data = TestFile.Create(TestImages.Png.TrnsRgb16BigEndian).Bytes; + Rgb48 expected = new(0x0102, 0x0304, 0x0506); + Assert.Equal(expected, Image.Identify(data).Metadata.GetPngMetadata().TransparentColor?.ToPixel()); + + using Image image = Image.Load(data); + Assert.Equal(ushort.MinValue, image[0, 0].A); + Assert.Equal(ushort.MaxValue, image[1, 0].A); + } + private static string GetChunkTypeName(uint value) { byte[] data = new byte[4]; diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.Chunks.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.Chunks.cs index fdaa70e031..0a47eda2d5 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.Chunks.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.Chunks.cs @@ -354,4 +354,53 @@ public void ExcludeFilter_WithNone_DoesNotExcludeChunks() bytesSpan = bytesSpan[(4 + 4 + length + 4)..]; } } + + /// + /// Checks the stored byte order independently of the decoder, then checks the identified transparency marker. + /// + /// The stored 16-bit input image. + /// The PNG color type. + [Theory] + [InlineData(TestImages.Png.TrnsGray16BigEndian, PngColorType.Grayscale)] + [InlineData(TestImages.Png.TrnsRgb16BigEndian, PngColorType.Rgb)] + public void TransparencyChunk_16Bit_IsWrittenBigEndian(string path, PngColorType colorType) + { + // arrange + using Image input = Image.Load(TestFile.Create(path).Bytes); + PngMetadata metadata = input.Metadata.GetPngMetadata(); + metadata.ColorType = colorType; + metadata.BitDepth = PngBitDepth.Bit16; + metadata.TransparentColor = colorType == PngColorType.Grayscale + ? Color.FromPixel(new L16(0x0102)) + : Color.FromPixel(new Rgb48(0x0102, 0x0304, 0x0506)); + + byte[] expected = colorType == PngColorType.Grayscale + ? [0x01, 0x02] + : [0x01, 0x02, 0x03, 0x04, 0x05, 0x06]; + + using MemoryStream memStream = new(); + + // act + input.Save(memStream, new PngEncoder { ColorType = colorType, BitDepth = PngBitDepth.Bit16 }); + + // assert + byte[] data = memStream.ToArray(); + Span bytesSpan = data.AsSpan(8); // Skip header. + bool transparencyFound = false; + while (bytesSpan.Length > 0) + { + int length = BinaryPrimitives.ReadInt32BigEndian(bytesSpan[..4]); + PngChunkType type = (PngChunkType)BinaryPrimitives.ReadInt32BigEndian(bytesSpan.Slice(4, 4)); + if (type == PngChunkType.Transparency) + { + transparencyFound = true; + Assert.Equal(expected, bytesSpan.Slice(8, length).ToArray()); + } + + bytesSpan = bytesSpan[(4 + 4 + length + 4)..]; + } + + Assert.True(transparencyFound); + Assert.Equal(metadata.TransparentColor, Image.Identify(data).Metadata.GetPngMetadata().TransparentColor); + } } diff --git a/tests/ImageSharp.Tests/TestImages.cs b/tests/ImageSharp.Tests/TestImages.cs index 24e1bf83fc..31dca8d5a6 100644 --- a/tests/ImageSharp.Tests/TestImages.cs +++ b/tests/ImageSharp.Tests/TestImages.cs @@ -55,6 +55,7 @@ public static class Png public const string Gray4BitTrans = "Png/gray-4-tRNS.png"; public const string L8BitTrans = "Png/gray-8-tRNS.png"; public const string LowColorVariance = "Png/low-variance.png"; + public const string Noise = "Png/noise-rgba32.png"; public const string PngWithMetadata = "Png/PngWithMetaData.png"; public const string InvalidTextData = "Png/InvalidTextData.png"; public const string DuplicateHeaderChunkResync = "Png/duplicate-header-chunk-resync.png"; @@ -65,6 +66,33 @@ public static class Png public const string AdamHeadsHlg = "Png/adamHeadsHLG.png"; public const string IptcMetadata = "Png/iptc-profile.png"; + // Chunk framing, integrity and transparency inputs. + public const string IdatLeadingEmpty = "Png/chunks/idat-leading-empty.png"; + public const string IdatConsecutiveLeadingEmpty = "Png/chunks/idat-consecutive-leading-empty.png"; + public const string IdatFirstOneByte = "Png/chunks/idat-first-one-byte.png"; + public const string IdatEmptyWithinHeader = "Png/chunks/idat-empty-within-header.png"; + public const string IdatEveryOneByte = "Png/chunks/idat-every-one-byte.png"; + public const string IdatEveryOneByteWithEmpty = "Png/chunks/idat-every-one-byte-with-empty.png"; + public const string IdatEmptyBetween = "Png/chunks/idat-empty-between.png"; + public const string IdatTrailingEmpty = "Png/chunks/idat-trailing-empty.png"; + public const string IdatEmptyBadCrc = "Png/chunks/idat-empty-bad-crc.png"; + public const string FdatLeadingEmpty = "Png/chunks/fdat-leading-empty.png"; + public const string FdatConsecutiveLeadingEmpty = "Png/chunks/fdat-consecutive-leading-empty.png"; + public const string FdatEmptyWithinHeader = "Png/chunks/fdat-empty-within-header.png"; + public const string FdatTrailingEmpty = "Png/chunks/fdat-trailing-empty.png"; + public const string FdatEmptyBadCrc = "Png/chunks/fdat-empty-bad-crc.png"; + public const string FdatTruncatedSequenceBetween = "Png/chunks/fdat-truncated-sequence-between.png"; + public const string FdatShort0 = "Png/chunks/fdat-short-0.png"; + public const string FdatShort3 = "Png/chunks/fdat-short-3.png"; + public const string PaletteShort0 = "Png/chunks/palette-short-0.png"; + public const string PaletteShort1 = "Png/chunks/palette-short-1.png"; + public const string PaletteShort2 = "Png/chunks/palette-short-2.png"; + public const string PaletteAdam7Short0 = "Png/chunks/palette-adam7-short-0.png"; + public const string PaletteAdam7Short1 = "Png/chunks/palette-adam7-short-1.png"; + public const string PaletteAdam7Short2 = "Png/chunks/palette-adam7-short-2.png"; + public const string TrnsGray16BigEndian = "Png/chunks/trns-gray16-big-endian.png"; + public const string TrnsRgb16BigEndian = "Png/chunks/trns-rgb16-big-endian.png"; + // Animated // https://philip.html5.org/tests/apng/tests.html public const string APng = "Png/animated/apng.png"; diff --git a/tests/Images/Input/Png/chunks/fdat-consecutive-leading-empty.png b/tests/Images/Input/Png/chunks/fdat-consecutive-leading-empty.png new file mode 100644 index 0000000000..8f394e2a1c --- /dev/null +++ b/tests/Images/Input/Png/chunks/fdat-consecutive-leading-empty.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:79f77899925f8f9b77309d6d392090346889a2bd7f7b3b38595c2af10fe26060 +size 2636 diff --git a/tests/Images/Input/Png/chunks/fdat-empty-bad-crc.png b/tests/Images/Input/Png/chunks/fdat-empty-bad-crc.png new file mode 100644 index 0000000000..9861b0232a --- /dev/null +++ b/tests/Images/Input/Png/chunks/fdat-empty-bad-crc.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:25419ca02b75f7be52e31601e9b5525fd1554bad430747c1a5a7eb2fdf92c5d0 +size 2636 diff --git a/tests/Images/Input/Png/chunks/fdat-empty-within-header.png b/tests/Images/Input/Png/chunks/fdat-empty-within-header.png new file mode 100644 index 0000000000..6e679b3f3c --- /dev/null +++ b/tests/Images/Input/Png/chunks/fdat-empty-within-header.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4a9f21fdcad7d91e82cbad349e25d3e40f6f9ab78e085e3960a5bdc410f6f0ca +size 2636 diff --git a/tests/Images/Input/Png/chunks/fdat-leading-empty.png b/tests/Images/Input/Png/chunks/fdat-leading-empty.png new file mode 100644 index 0000000000..7e2c1f19d2 --- /dev/null +++ b/tests/Images/Input/Png/chunks/fdat-leading-empty.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:98eeca49aa75598628c2edd9f24179f3e76a6f139495de0a389b704c002a047a +size 2572 diff --git a/tests/Images/Input/Png/chunks/fdat-short-0.png b/tests/Images/Input/Png/chunks/fdat-short-0.png new file mode 100644 index 0000000000..db433236aa --- /dev/null +++ b/tests/Images/Input/Png/chunks/fdat-short-0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:719690c12c5770ea6aa959787ba21a5a8ccd551f4e75f1a543f593abac3c6aea +size 1417 diff --git a/tests/Images/Input/Png/chunks/fdat-short-3.png b/tests/Images/Input/Png/chunks/fdat-short-3.png new file mode 100644 index 0000000000..3ab27d75f9 --- /dev/null +++ b/tests/Images/Input/Png/chunks/fdat-short-3.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5fcaa5d6925d3c35e9e9102d797b96ab2621bab0bc48747bd261a99bc46341a5 +size 1429 diff --git a/tests/Images/Input/Png/chunks/fdat-trailing-empty.png b/tests/Images/Input/Png/chunks/fdat-trailing-empty.png new file mode 100644 index 0000000000..8d82d3655d --- /dev/null +++ b/tests/Images/Input/Png/chunks/fdat-trailing-empty.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:68999c390cd9a26119fb7fe0e694d95e862ba3b5fb4d380b40229ef75b9c9128 +size 2572 diff --git a/tests/Images/Input/Png/chunks/fdat-truncated-sequence-between.png b/tests/Images/Input/Png/chunks/fdat-truncated-sequence-between.png new file mode 100644 index 0000000000..07462ed096 --- /dev/null +++ b/tests/Images/Input/Png/chunks/fdat-truncated-sequence-between.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3de847e39d88d93482979c7a799d24d1ff71ee9b14828ccbdb629397ae683c90 +size 2632 diff --git a/tests/Images/Input/Png/chunks/idat-consecutive-leading-empty.png b/tests/Images/Input/Png/chunks/idat-consecutive-leading-empty.png new file mode 100644 index 0000000000..c152e6a1d4 --- /dev/null +++ b/tests/Images/Input/Png/chunks/idat-consecutive-leading-empty.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0cc1e6e5435e61588c395787343dd3794121b560fa88aa0ca9fc4c4a70517781 +size 4922 diff --git a/tests/Images/Input/Png/chunks/idat-empty-bad-crc.png b/tests/Images/Input/Png/chunks/idat-empty-bad-crc.png new file mode 100644 index 0000000000..076fdb91c6 --- /dev/null +++ b/tests/Images/Input/Png/chunks/idat-empty-bad-crc.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:aed0d412a8607f7d3407411c065ef16b6a8295b30e9ced3653160692b20df010 +size 4922 diff --git a/tests/Images/Input/Png/chunks/idat-empty-between.png b/tests/Images/Input/Png/chunks/idat-empty-between.png new file mode 100644 index 0000000000..a2a7001da7 --- /dev/null +++ b/tests/Images/Input/Png/chunks/idat-empty-between.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b6f95d596f54a3ffb33d59e6f95371b895b722dc0ae3e9d54c4d472e5652e3b2 +size 4922 diff --git a/tests/Images/Input/Png/chunks/idat-empty-within-header.png b/tests/Images/Input/Png/chunks/idat-empty-within-header.png new file mode 100644 index 0000000000..d2d16ac585 --- /dev/null +++ b/tests/Images/Input/Png/chunks/idat-empty-within-header.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:bd86ee484684f1d7a7d705914bfb6feb91e5c89cfa221b861315e440ebd27a2e +size 4922 diff --git a/tests/Images/Input/Png/chunks/idat-every-one-byte-with-empty.png b/tests/Images/Input/Png/chunks/idat-every-one-byte-with-empty.png new file mode 100644 index 0000000000..0188d1df9a --- /dev/null +++ b/tests/Images/Input/Png/chunks/idat-every-one-byte-with-empty.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:56fbdd00aebeadc5b85509b89c9ee3a160b5f5f4c17d283c7298e606b55581e8 +size 121070 diff --git a/tests/Images/Input/Png/chunks/idat-every-one-byte.png b/tests/Images/Input/Png/chunks/idat-every-one-byte.png new file mode 100644 index 0000000000..dbcd1f7061 --- /dev/null +++ b/tests/Images/Input/Png/chunks/idat-every-one-byte.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:937fcd40242fe8faefb8106d9d6ec3134e812778161b3946596a085cd0a1cf15 +size 62978 diff --git a/tests/Images/Input/Png/chunks/idat-first-one-byte.png b/tests/Images/Input/Png/chunks/idat-first-one-byte.png new file mode 100644 index 0000000000..355bb94d67 --- /dev/null +++ b/tests/Images/Input/Png/chunks/idat-first-one-byte.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:691283f42518be72a1e60a4555f62827a044e63423db7a8f1445238b00a5a24c +size 4910 diff --git a/tests/Images/Input/Png/chunks/idat-leading-empty.png b/tests/Images/Input/Png/chunks/idat-leading-empty.png new file mode 100644 index 0000000000..45fafa00d8 --- /dev/null +++ b/tests/Images/Input/Png/chunks/idat-leading-empty.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d695fd60e2405d44623484a5cbc78c10bbdd84a15dd705234dfd6b90a1b30cb4 +size 4910 diff --git a/tests/Images/Input/Png/chunks/idat-trailing-empty.png b/tests/Images/Input/Png/chunks/idat-trailing-empty.png new file mode 100644 index 0000000000..afa97ef140 --- /dev/null +++ b/tests/Images/Input/Png/chunks/idat-trailing-empty.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:abd1a897f8ea4d3253d137f14705643e6ad5572b2aeca85e41536831ac97b997 +size 4910 diff --git a/tests/Images/Input/Png/chunks/palette-adam7-short-0.png b/tests/Images/Input/Png/chunks/palette-adam7-short-0.png new file mode 100644 index 0000000000..74c5734294 --- /dev/null +++ b/tests/Images/Input/Png/chunks/palette-adam7-short-0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:acd3c2dc78112c2801008944cd5504db2d94b958cf3dd95065d982f7842957b5 +size 81 diff --git a/tests/Images/Input/Png/chunks/palette-adam7-short-1.png b/tests/Images/Input/Png/chunks/palette-adam7-short-1.png new file mode 100644 index 0000000000..6056a742dd --- /dev/null +++ b/tests/Images/Input/Png/chunks/palette-adam7-short-1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:408e6d68c4385b07ed184f066180e0491f08bf06999476f81fe9b5bcd3833281 +size 82 diff --git a/tests/Images/Input/Png/chunks/palette-adam7-short-2.png b/tests/Images/Input/Png/chunks/palette-adam7-short-2.png new file mode 100644 index 0000000000..428bfaa23c --- /dev/null +++ b/tests/Images/Input/Png/chunks/palette-adam7-short-2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1d91e38273e814dbf5df7633a8c2db30264746b2179aff04c8946c3eeeb7ab31 +size 83 diff --git a/tests/Images/Input/Png/chunks/palette-short-0.png b/tests/Images/Input/Png/chunks/palette-short-0.png new file mode 100644 index 0000000000..9aa8726fc5 --- /dev/null +++ b/tests/Images/Input/Png/chunks/palette-short-0.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ca328183eca2bebd5fcff5ca0a17c0781e6465780b022ec2baa41bcc38465108 +size 81 diff --git a/tests/Images/Input/Png/chunks/palette-short-1.png b/tests/Images/Input/Png/chunks/palette-short-1.png new file mode 100644 index 0000000000..7d29e3864c --- /dev/null +++ b/tests/Images/Input/Png/chunks/palette-short-1.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4d6ff9e835c14793099cc665b121e52f9e246633380a4a5f506f43e42bff1371 +size 82 diff --git a/tests/Images/Input/Png/chunks/palette-short-2.png b/tests/Images/Input/Png/chunks/palette-short-2.png new file mode 100644 index 0000000000..321b347ec1 --- /dev/null +++ b/tests/Images/Input/Png/chunks/palette-short-2.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1d9a6bb7f0774bde7633b53042fd3b6c43a5d9ec61d9d2c6a2f6ba49afad4fd3 +size 83 diff --git a/tests/Images/Input/Png/chunks/trns-gray16-big-endian.png b/tests/Images/Input/Png/chunks/trns-gray16-big-endian.png new file mode 100644 index 0000000000..4bd5bd6fb5 --- /dev/null +++ b/tests/Images/Input/Png/chunks/trns-gray16-big-endian.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:30035517659d0c0a1a27af315927c073fef6173a20f4fddd3e7cce105a7a30fa +size 84 diff --git a/tests/Images/Input/Png/chunks/trns-rgb16-big-endian.png b/tests/Images/Input/Png/chunks/trns-rgb16-big-endian.png new file mode 100644 index 0000000000..c95cb8c244 --- /dev/null +++ b/tests/Images/Input/Png/chunks/trns-rgb16-big-endian.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cc1e7a3bc53545da4c41967daaf694b55a6c32dffa080a09b57b9ea67f108ce7 +size 96 diff --git a/tests/Images/Input/Png/noise-rgba32.png b/tests/Images/Input/Png/noise-rgba32.png new file mode 100644 index 0000000000..ef275bd4dd --- /dev/null +++ b/tests/Images/Input/Png/noise-rgba32.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:269ce0e5665d956a332b494c1c4fc5cbeceef4c59033368e7c614f543fc39d18 +size 4898