diff --git a/src/ImageSharp/Formats/Webp/IWebpDecoderOptions.cs b/src/ImageSharp/Formats/Webp/IWebpDecoderOptions.cs index 7bd78da3d..cf607ef69 100644 --- a/src/ImageSharp/Formats/Webp/IWebpDecoderOptions.cs +++ b/src/ImageSharp/Formats/Webp/IWebpDecoderOptions.cs @@ -1,6 +1,8 @@ // Copyright (c) Six Labors. // Licensed under the Apache License, Version 2.0. +using SixLabors.ImageSharp.Metadata; + namespace SixLabors.ImageSharp.Formats.Webp { /// @@ -12,5 +14,10 @@ namespace SixLabors.ImageSharp.Formats.Webp /// Gets a value indicating whether the metadata should be ignored when the image is being decoded. /// bool IgnoreMetadata { get; } + + /// + /// Gets the decoding mode for multi-frame images. + /// + FrameDecodingMode DecodingMode { get; } } } diff --git a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs index 60d41c984..16589e50f 100644 --- a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs +++ b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs @@ -53,10 +53,12 @@ namespace SixLabors.ImageSharp.Formats.Webp /// /// The memory allocator. /// The global configuration. - public WebpAnimationDecoder(MemoryAllocator memoryAllocator, Configuration configuration) + /// The frame decoding mode. + public WebpAnimationDecoder(MemoryAllocator memoryAllocator, Configuration configuration, FrameDecodingMode decodingMode) { this.memoryAllocator = memoryAllocator; this.configuration = configuration; + this.DecodingMode = decodingMode; } /// @@ -64,6 +66,11 @@ namespace SixLabors.ImageSharp.Formats.Webp /// public IMemoryOwner AlphaData { get; set; } + /// + /// Gets the decoding mode for multi-frame images. + /// + public FrameDecodingMode DecodingMode { get; } + /// /// Decodes the animated webp image from the specified stream. /// @@ -103,7 +110,7 @@ namespace SixLabors.ImageSharp.Formats.Webp break; } - if (stream.Position == stream.Length) + if (stream.Position == stream.Length || this.DecodingMode is FrameDecodingMode.First) { break; } diff --git a/src/ImageSharp/Formats/Webp/WebpDecoder.cs b/src/ImageSharp/Formats/Webp/WebpDecoder.cs index 1736e97ce..71b4e4f23 100644 --- a/src/ImageSharp/Formats/Webp/WebpDecoder.cs +++ b/src/ImageSharp/Formats/Webp/WebpDecoder.cs @@ -6,6 +6,7 @@ using System.Threading; using System.Threading.Tasks; using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.Memory; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Webp @@ -20,6 +21,12 @@ namespace SixLabors.ImageSharp.Formats.Webp /// public bool IgnoreMetadata { get; set; } + /// + /// Gets or sets the decoding mode for multi-frame images. + /// Defaults to All. + /// + public FrameDecodingMode DecodingMode { get; set; } = FrameDecodingMode.All; + /// public Image Decode(Configuration configuration, Stream stream) where TPixel : unmanaged, IPixel diff --git a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs index 01e604a4b..252af7867 100644 --- a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs +++ b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs @@ -56,10 +56,19 @@ namespace SixLabors.ImageSharp.Formats.Webp public WebpDecoderCore(Configuration configuration, IWebpDecoderOptions options) { this.Configuration = configuration; + this.DecodingMode = options.DecodingMode; this.memoryAllocator = configuration.MemoryAllocator; this.IgnoreMetadata = options.IgnoreMetadata; } + /// + public Configuration Configuration { get; } + + /// + /// Gets the decoding mode for multi-frame images. + /// + public FrameDecodingMode DecodingMode { get; } + /// /// Gets a value indicating whether the metadata should be ignored when the image is being decoded. /// @@ -70,9 +79,6 @@ namespace SixLabors.ImageSharp.Formats.Webp /// public ImageMetadata Metadata { get; private set; } - /// - public Configuration Configuration { get; } - /// /// Gets the dimensions of the image. /// @@ -96,7 +102,7 @@ namespace SixLabors.ImageSharp.Formats.Webp { if (this.webImageInfo.Features is { Animation: true }) { - using var animationDecoder = new WebpAnimationDecoder(this.memoryAllocator, this.Configuration); + using var animationDecoder = new WebpAnimationDecoder(this.memoryAllocator, this.Configuration, this.DecodingMode); return animationDecoder.Decode(stream, this.webImageInfo.Features, this.webImageInfo.Width, this.webImageInfo.Height, fileSize); } diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs index ae1dddba0..5e878f780 100644 --- a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs +++ b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs @@ -3,6 +3,7 @@ using System.IO; using SixLabors.ImageSharp.Formats.Webp; +using SixLabors.ImageSharp.Metadata; using SixLabors.ImageSharp.PixelFormats; using SixLabors.ImageSharp.Tests.TestUtilities; using SixLabors.ImageSharp.Tests.TestUtilities.ImageComparison; @@ -234,7 +235,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp // TODO: Reference decoder throws here MagickCorruptImageErrorException, webpinfo also indicates an error here, but decoding the image seems to work. // [WithFile(Lossless.GreenTransform5, PixelTypes.Rgba32)] - public void WebpDecoder_CanDecode_Lossless_WithSubstractGreenTransform( + public void WebpDecoder_CanDecode_Lossless_WithSubtractGreenTransform( TestImageProvider provider) where TPixel : unmanaged, IPixel { @@ -345,6 +346,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp Assert.Equal(0, webpMetaData.AnimationLoopCount); Assert.Equal(150U, frameMetaData.FrameDuration); + Assert.Equal(12, image.Frames.Count); } } @@ -363,6 +365,18 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp Assert.Equal(0, webpMetaData.AnimationLoopCount); Assert.Equal(150U, frameMetaData.FrameDuration); + Assert.Equal(12, image.Frames.Count); + } + } + + [Theory] + [WithFile(Lossless.Animated, PixelTypes.Rgba32)] + public void Decode_AnimatedLossless_WithFrameDecodingModeFirst_OnlyDecodesOneFrame(TestImageProvider provider) + where TPixel : unmanaged, IPixel + { + using (Image image = provider.GetImage(new WebpDecoder() { DecodingMode = FrameDecodingMode.First })) + { + Assert.Equal(1, image.Frames.Count); } }