diff --git a/src/ImageSharp/Formats/Png/PngChunkType.cs b/src/ImageSharp/Formats/Png/PngChunkType.cs index 866bf28af..a008bf8ea 100644 --- a/src/ImageSharp/Formats/Png/PngChunkType.cs +++ b/src/ImageSharp/Formats/Png/PngChunkType.cs @@ -9,22 +9,7 @@ namespace SixLabors.ImageSharp.Formats.Png; internal enum PngChunkType : uint { /// - /// - /// acTL (Single) - AnimationControl = 0x6163544cU, - - /// - /// - /// fcTL (Multiple) - FrameControl = 0x6663544cU, - - /// - /// - /// fdAT (Multiple) - FrameData = 0x66644154U, - - /// - /// The IDAT chunk contains the actual image data. The image can contains more + /// This chunk contains the actual image data. The image can contains more /// than one chunk of this type. All chunks together are the whole image. /// /// IDAT (Multiple) @@ -155,6 +140,27 @@ internal enum PngChunkType : uint /// cHRM (Single) Chroma = 0x6348524d, + /// + /// This chunk is an ancillary chunk as defined in the PNG Specification. + /// It must appear before the first IDAT chunk within a valid PNG stream. + /// + /// acTL (Single, APNG) + AnimationControl = 0x6163544cU, + + /// + /// This chunk is an ancillary chunk as defined in the PNG Specification. + /// It must appear before the IDAT or fdAT chunks of the frame to which it applies. + /// + /// fcTL (Multiple, APNG) + FrameControl = 0x6663544cU, + + /// + /// This chunk has the same purpose as an IDAT chunk. + /// It has the same structure as an IDAT chunk, except preceded by a sequence number. + /// + /// fdAT (Multiple, APNG) + FrameData = 0x66644154U, + /// /// Malformed chunk named CgBI produced by apple, which is not conform to the specification. /// Related issue is here https://github.com/SixLabors/ImageSharp/issues/410 diff --git a/src/ImageSharp/Formats/Png/PngEncoderCore.cs b/src/ImageSharp/Formats/Png/PngEncoderCore.cs index 2cbc86f42..155041789 100644 --- a/src/ImageSharp/Formats/Png/PngEncoderCore.cs +++ b/src/ImageSharp/Formats/Png/PngEncoderCore.cs @@ -168,7 +168,8 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable this.WriteXmpChunk(stream, metadata); this.WriteTextChunks(stream, pngMetadata); - if (this.encoder.IsSimplePng is not true && targetImage.Frames.Count > 1) + if ((this.encoder.IsSimplePng is null && targetImage.Frames.Count > 1) + || this.encoder.IsSimplePng is false) { this.WriteAnimationControlChunk(stream, targetImage.Frames.Count, pngMetadata.NumberPlays); @@ -642,7 +643,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable ref Rgba32 rgbaPaletteRef = ref MemoryMarshal.GetReference(rgbaPaletteSpan); // Loop, assign, and extract alpha values from the palette. - for (int i = 0; i < paletteLength; i++) + for (int i = 0; i < paletteLength; ++i) { Rgba32 rgba = Unsafe.Add(ref rgbaPaletteRef, (uint)i); byte alpha = rgba.A; @@ -674,7 +675,7 @@ internal sealed class PngEncoderCore : IImageEncoderInternals, IDisposable /// The image metadata. private void WritePhysicalChunk(Stream stream, ImageMetadata meta) { - if ((this.chunkFilter & PngChunkFilter.ExcludePhysicalChunk) == PngChunkFilter.ExcludePhysicalChunk) + if (this.chunkFilter.HasFlag(PngChunkFilter.ExcludePhysicalChunk)) { return; } diff --git a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs index 2fc0dc0c0..19283ebf8 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngDecoderTests.cs @@ -111,20 +111,7 @@ public partial class PngDecoderTests public void Decode_APng(TestImageProvider provider) where TPixel : unmanaged, IPixel { - using Image image = provider.GetImage(PngDecoder.Instance); - image.SaveAsPng("C:\\WorkSpace\\1.png"); - image.DebugSave(provider); - image.CompareToOriginal(provider, ImageComparer.Exact); - // TODO test - } - - [Theory] - [WithFile("C:\\WorkSpace\\Fuck.png", PixelTypes.Rgba32)] - public void Decode_APng2(TestImageProvider provider) - where TPixel : unmanaged, IPixel - { - using Image image = provider.GetImage(PngDecoder.Instance); - image.SaveAsPng("C:\\WorkSpace\\1.png"); + using Image image = provider.GetImage(PngDecoder.Instance); // MagickReferenceDecoder cannot decode APNGs } [Theory] diff --git a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs index b4fda5d32..74885283d 100644 --- a/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Png/PngEncoderTests.cs @@ -439,6 +439,19 @@ public partial class PngEncoderTests }); } + [Theory] + [WithFile(TestImages.Png.APng, PixelTypes.Rgba32)] + public void Encode_APng(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using Image image = provider.GetImage(PngDecoder.Instance); + using MemoryStream memStream = new(); + image.Save(memStream, PngEncoder); + memStream.Position = 0; + using Image output = Image.Load(memStream); + ImageComparer.Exact.VerifySimilarity(output, image); + } + [Theory] [MemberData(nameof(PngTrnsFiles))] public void Encode_PreserveTrns(string imagePath, PngBitDepth pngBitDepth, PngColorType pngColorType) diff --git a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs index 460ecac85..42be46645 100644 --- a/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs +++ b/tests/ImageSharp.Tests/TestUtilities/ImagingTestCaseUtility.cs @@ -208,7 +208,7 @@ public class ImagingTestCaseUtility bool appendPixelTypeToFileName = true) where TPixel : unmanaged, IPixel { - encoder = encoder ?? TestEnvironment.GetReferenceEncoder($"foo.{extension}"); + encoder ??= TestEnvironment.GetReferenceEncoder($"foo.{extension}"); string[] files = this.GetTestOutputFileNamesMultiFrame( image.Frames.Count, @@ -218,14 +218,10 @@ public class ImagingTestCaseUtility for (int i = 0; i < image.Frames.Count; i++) { - using (Image frameImage = image.Frames.CloneFrame(i)) - { - string filePath = files[i]; - using (FileStream stream = File.OpenWrite(filePath)) - { - frameImage.Save(stream, encoder); - } - } + using Image frameImage = image.Frames.CloneFrame(i); + string filePath = files[i]; + using FileStream stream = File.OpenWrite(filePath); + frameImage.Save(stream, encoder); } return files; diff --git a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs index 31c9f541e..7cdf66e3a 100644 --- a/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs +++ b/tests/ImageSharp.Tests/TestUtilities/TestImageExtensions.cs @@ -534,10 +534,8 @@ public static class TestImageExtensions referenceDecoder ??= TestEnvironment.GetReferenceDecoder(path); using MemoryStream stream = new(testFile.Bytes); - using (Image original = referenceDecoder.Decode(referenceDecoderOptions ?? DecoderOptions.Default, stream)) - { - comparer.VerifySimilarity(original, image); - } + using Image original = referenceDecoder.Decode(referenceDecoderOptions ?? DecoderOptions.Default, stream); + comparer.VerifySimilarity(original, image); return image; } @@ -560,10 +558,8 @@ public static class TestImageExtensions referenceDecoder ??= TestEnvironment.GetReferenceDecoder(path); using MemoryStream stream = new(testFile.Bytes); - using (Image original = referenceDecoder.Decode(DecoderOptions.Default, stream)) - { - comparer.VerifySimilarity(original, image); - } + using Image original = referenceDecoder.Decode(DecoderOptions.Default, stream); + comparer.VerifySimilarity(original, image); return image; } diff --git a/tests/Images/Input/Png/apng.png b/tests/Images/Input/Png/apng.png index c5b2adf8e..7def301ae 100644 --- a/tests/Images/Input/Png/apng.png +++ b/tests/Images/Input/Png/apng.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f6b0e5a904e269a9108b32c0f5cc98cda4240a60db421f560f45d2e36ead32a9 -size 212 +oid sha256:7c15e4670da1826d1cc25555bd6cbe287ecc70327cd029a7613334a39a283021 +size 2508