diff --git a/src/ImageSharp/Formats/Gif/FrameDecodingMode.cs b/src/ImageSharp/Formats/Gif/FrameDecodingMode.cs new file mode 100644 index 000000000..05791c92e --- /dev/null +++ b/src/ImageSharp/Formats/Gif/FrameDecodingMode.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors and contributors. +// Licensed under the Apache License, Version 2.0. + +namespace SixLabors.ImageSharp.Formats.Gif +{ + /// + /// Enumerated frame process modes to apply to multi-frame images. + /// + public enum FrameDecodingMode + { + /// + /// Decodes all the frames of a multi-frame image. + /// + All, + + /// + /// Decodes only the first frame of a multi-frame image. + /// + First + } +} \ No newline at end of file diff --git a/src/ImageSharp/Formats/Gif/GifDecoder.cs b/src/ImageSharp/Formats/Gif/GifDecoder.cs index 5ded251e2..11b5b57fa 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoder.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoder.cs @@ -24,6 +24,11 @@ namespace SixLabors.ImageSharp.Formats.Gif /// public Encoding TextEncoding { get; set; } = GifConstants.DefaultEncoding; + /// + /// Gets or sets the decoding mode for multi-frame images + /// + public FrameDecodingMode DecodingMode { get; set; } = FrameDecodingMode.All; + /// public Image Decode(Configuration configuration, Stream stream) where TPixel : struct, IPixel diff --git a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs index 7fa3aa04a..453197b0c 100644 --- a/src/ImageSharp/Formats/Gif/GifDecoderCore.cs +++ b/src/ImageSharp/Formats/Gif/GifDecoderCore.cs @@ -84,6 +84,7 @@ namespace SixLabors.ImageSharp.Formats.Gif { this.TextEncoding = options.TextEncoding ?? GifConstants.DefaultEncoding; this.IgnoreMetadata = options.IgnoreMetadata; + this.DecodingMode = options.DecodingMode; this.configuration = configuration ?? Configuration.Default; } @@ -97,6 +98,11 @@ namespace SixLabors.ImageSharp.Formats.Gif /// public Encoding TextEncoding { get; } + /// + /// Gets the decoding mode for multi-frame images + /// + public FrameDecodingMode DecodingMode { get; } + /// /// Decodes the stream to the image. /// @@ -129,6 +135,11 @@ namespace SixLabors.ImageSharp.Formats.Gif { if (nextFlag == GifConstants.ImageLabel) { + if (this.previousFrame != null && this.DecodingMode == FrameDecodingMode.First) + { + break; + } + this.ReadFrame(); } else if (nextFlag == GifConstants.ExtensionIntroducer) @@ -238,14 +249,6 @@ namespace SixLabors.ImageSharp.Formats.Gif { throw new ImageFormatException($"Invalid gif colormap size '{this.logicalScreenDescriptor.GlobalColorTableSize}'"); } - - /* // No point doing this as the max width/height is always int.Max and that always bigger than the max size of a gif which is stored in a short. - if (this.logicalScreenDescriptor.Width > Image.MaxWidth || this.logicalScreenDescriptor.Height > Image.MaxHeight) - { - throw new ArgumentOutOfRangeException( - $"The input gif '{this.logicalScreenDescriptor.Width}x{this.logicalScreenDescriptor.Height}' is bigger then the max allowed size '{Image.MaxWidth}x{Image.MaxHeight}'"); - } - */ } /// diff --git a/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs b/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs index fd4cc82c9..a2288f30a 100644 --- a/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs +++ b/src/ImageSharp/Formats/Gif/IGifDecoderOptions.cs @@ -19,5 +19,10 @@ namespace SixLabors.ImageSharp.Formats.Gif /// Gets the encoding that should be used when reading comments. /// Encoding TextEncoding { get; } + + /// + /// Gets the decoding mode for multi-frame images + /// + FrameDecodingMode DecodingMode { get; } } } diff --git a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs index d04c49a98..cd78add75 100644 --- a/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/Gif/GifDecoderTests.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. using System.Text; -using SixLabors.ImageSharp.Formats; using SixLabors.ImageSharp.Formats.Gif; using SixLabors.ImageSharp.PixelFormats; using SixLabors.Primitives; @@ -45,12 +44,12 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void Decode_IgnoreMetadataIsFalse_CommentsAreRead() { - GifDecoder options = new GifDecoder() + var options = new GifDecoder { IgnoreMetadata = false }; - TestFile testFile = TestFile.Create(TestImages.Gif.Rings); + var testFile = TestFile.Create(TestImages.Gif.Rings); using (Image image = testFile.CreateImage(options)) { @@ -63,12 +62,12 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void Decode_IgnoreMetadataIsTrue_CommentsAreIgnored() { - GifDecoder options = new GifDecoder() + var options = new GifDecoder { IgnoreMetadata = true }; - TestFile testFile = TestFile.Create(TestImages.Gif.Rings); + var testFile = TestFile.Create(TestImages.Gif.Rings); using (Image image = testFile.CreateImage(options)) { @@ -79,12 +78,12 @@ namespace SixLabors.ImageSharp.Tests [Fact] public void Decode_TextEncodingSetToUnicode_TextIsReadWithCorrectEncoding() { - GifDecoder options = new GifDecoder() + var options = new GifDecoder { TextEncoding = Encoding.Unicode }; - TestFile testFile = TestFile.Create(TestImages.Gif.Rings); + var testFile = TestFile.Create(TestImages.Gif.Rings); using (Image image = testFile.CreateImage(options)) { @@ -92,5 +91,27 @@ namespace SixLabors.ImageSharp.Tests Assert.Equal("浉条卥慨灲", image.MetaData.Properties[0].Value); } } + + [Theory] + [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32)] + public void CanDecodeJustOneFrame(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new GifDecoder { DecodingMode = FrameDecodingMode.First })) + { + Assert.Equal(1, image.Frames.Count); + } + } + + [Theory] + [WithFile(TestImages.Gif.Giphy, PixelTypes.Rgba32)] + public void CanDecodeAllFrames(TestImageProvider provider) + where TPixel : struct, IPixel + { + using (Image image = provider.GetImage(new GifDecoder { DecodingMode = FrameDecodingMode.All })) + { + Assert.True(image.Frames.Count > 1); + } + } } -} +} \ No newline at end of file