diff --git a/src/ImageSharp/Formats/Gif/GifFormat.cs b/src/ImageSharp/Formats/Gif/GifFormat.cs
index 459f0068b..fcb0fe5b3 100644
--- a/src/ImageSharp/Formats/Gif/GifFormat.cs
+++ b/src/ImageSharp/Formats/Gif/GifFormat.cs
@@ -1,4 +1,4 @@
-// Copyright (c) Six Labors.
+// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
using System.Collections.Generic;
@@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
///
/// Gets the current instance.
///
- public static GifFormat Instance { get; } = new GifFormat();
+ public static GifFormat Instance { get; } = new();
///
public string Name => "GIF";
@@ -32,9 +32,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
public IEnumerable FileExtensions => GifConstants.FileExtensions;
///
- public GifMetadata CreateDefaultFormatMetadata() => new GifMetadata();
+ public GifMetadata CreateDefaultFormatMetadata() => new();
///
- public GifFrameMetadata CreateDefaultFormatFrameMetadata() => new GifFrameMetadata();
+ public GifFrameMetadata CreateDefaultFormatFrameMetadata() => new();
}
}
diff --git a/src/ImageSharp/Formats/Webp/MetadataExtensions.cs b/src/ImageSharp/Formats/Webp/MetadataExtensions.cs
index 63f8e3427..3a85b5441 100644
--- a/src/ImageSharp/Formats/Webp/MetadataExtensions.cs
+++ b/src/ImageSharp/Formats/Webp/MetadataExtensions.cs
@@ -17,5 +17,12 @@ namespace SixLabors.ImageSharp
/// The metadata this method extends.
/// The .
public static WebpMetadata GetWebpMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(WebpFormat.Instance);
+
+ ///
+ /// Gets the webp format specific metadata for the image frame.
+ ///
+ /// The metadata this method extends.
+ /// The .
+ public static WebpFrameMetadata GetWebpMetadata(this ImageFrameMetadata metadata) => metadata.GetFormatMetadata(WebpFormat.Instance);
}
}
diff --git a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs
index b31cf7a26..60d41c984 100644
--- a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs
+++ b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs
@@ -3,6 +3,7 @@
using System;
using System.Buffers;
+using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Formats.Webp.Lossless;
using SixLabors.ImageSharp.Formats.Webp.Lossy;
using SixLabors.ImageSharp.IO;
@@ -37,6 +38,16 @@ namespace SixLabors.ImageSharp.Formats.Webp
///
private Rectangle? restoreArea;
+ ///
+ /// The abstract metadata.
+ ///
+ private ImageMetadata metadata;
+
+ ///
+ /// The gif specific metadata.
+ ///
+ private WebpMetadata webpMetadata;
+
///
/// Initializes a new instance of the class.
///
@@ -68,6 +79,10 @@ namespace SixLabors.ImageSharp.Formats.Webp
Image image = null;
ImageFrame previousFrame = null;
+ this.metadata = new ImageMetadata();
+ this.webpMetadata = this.metadata.GetWebpMetadata();
+ this.webpMetadata.AnimationLoopCount = features.AnimationLoopCount;
+
int remainingBytes = (int)completeDataSize;
while (remainingBytes > 0)
{
@@ -140,17 +155,22 @@ namespace SixLabors.ImageSharp.Formats.Webp
break;
}
- var metaData = new ImageMetadata();
ImageFrame currentFrame = null;
ImageFrame imageFrame;
if (previousFrame is null)
{
- image = new Image(this.configuration, (int)width, (int)height, backgroundColor.ToPixel(), metaData);
+ image = new Image(this.configuration, (int)width, (int)height, backgroundColor.ToPixel(), this.metadata);
+
+ this.SetFrameMetadata(image.Frames.RootFrame.Metadata, frameData.Duration);
+
imageFrame = image.Frames.RootFrame;
}
else
{
currentFrame = image.Frames.AddFrame(previousFrame); // This clones the frame and adds it the collection.
+
+ this.SetFrameMetadata(currentFrame.Metadata, frameData.Duration);
+
imageFrame = currentFrame;
}
@@ -179,6 +199,18 @@ namespace SixLabors.ImageSharp.Formats.Webp
return (uint)(stream.Position - streamStartPosition);
}
+ ///
+ /// Sets the frames metadata.
+ ///
+ /// The metadata.
+ /// The frame duration.
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private void SetFrameMetadata(ImageFrameMetadata meta, uint duration)
+ {
+ WebpFrameMetadata frameMetadata = meta.GetWebpMetadata();
+ frameMetadata.FrameDuration = duration;
+ }
+
///
/// Reads the ALPH chunk data.
///
diff --git a/src/ImageSharp/Formats/Webp/WebpFormat.cs b/src/ImageSharp/Formats/Webp/WebpFormat.cs
index 1f27c4d84..bc3fb09c3 100644
--- a/src/ImageSharp/Formats/Webp/WebpFormat.cs
+++ b/src/ImageSharp/Formats/Webp/WebpFormat.cs
@@ -6,9 +6,9 @@ using System.Collections.Generic;
namespace SixLabors.ImageSharp.Formats.Webp
{
///
- /// Registers the image encoders, decoders and mime type detectors for the Webp format
+ /// Registers the image encoders, decoders and mime type detectors for the Webp format.
///
- public sealed class WebpFormat : IImageFormat
+ public sealed class WebpFormat : IImageFormat
{
private WebpFormat()
{
@@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
///
/// Gets the current instance.
///
- public static WebpFormat Instance { get; } = new WebpFormat();
+ public static WebpFormat Instance { get; } = new();
///
public string Name => "Webp";
@@ -32,6 +32,9 @@ namespace SixLabors.ImageSharp.Formats.Webp
public IEnumerable FileExtensions => WebpConstants.FileExtensions;
///
- public WebpMetadata CreateDefaultFormatMetadata() => new WebpMetadata();
+ public WebpMetadata CreateDefaultFormatMetadata() => new();
+
+ ///
+ public WebpFrameMetadata CreateDefaultFormatFrameMetadata() => new();
}
}
diff --git a/src/ImageSharp/Formats/Webp/WebpFrameMetadata.cs b/src/ImageSharp/Formats/Webp/WebpFrameMetadata.cs
new file mode 100644
index 000000000..bebfb9d79
--- /dev/null
+++ b/src/ImageSharp/Formats/Webp/WebpFrameMetadata.cs
@@ -0,0 +1,33 @@
+// Copyright (c) Six Labors.
+// Licensed under the Apache License, Version 2.0.
+
+namespace SixLabors.ImageSharp.Formats.Webp
+{
+ ///
+ /// Provides webp specific metadata information for the image frame.
+ ///
+ public class WebpFrameMetadata : IDeepCloneable
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public WebpFrameMetadata()
+ {
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The metadata to create an instance from.
+ private WebpFrameMetadata(WebpFrameMetadata other) => this.FrameDuration = other.FrameDuration;
+
+ ///
+ /// Gets or sets the frame duration. The time to wait before displaying the next frame,
+ /// in 1 millisecond units. Note the interpretation of frame duration of 0 (and often smaller and equal to 10) is implementation defined.
+ ///
+ public uint FrameDuration { get; set; }
+
+ ///
+ public IDeepCloneable DeepClone() => new WebpFrameMetadata(this);
+ }
+}
diff --git a/src/ImageSharp/Formats/Webp/WebpMetadata.cs b/src/ImageSharp/Formats/Webp/WebpMetadata.cs
index f398d3d87..5dd010502 100644
--- a/src/ImageSharp/Formats/Webp/WebpMetadata.cs
+++ b/src/ImageSharp/Formats/Webp/WebpMetadata.cs
@@ -19,13 +19,22 @@ namespace SixLabors.ImageSharp.Formats.Webp
/// Initializes a new instance of the class.
///
/// The metadata to create an instance from.
- private WebpMetadata(WebpMetadata other) => this.FileFormat = other.FileFormat;
+ private WebpMetadata(WebpMetadata other)
+ {
+ this.FileFormat = other.FileFormat;
+ this.AnimationLoopCount = other.AnimationLoopCount;
+ }
///
/// Gets or sets the webp file format used. Either lossless or lossy.
///
public WebpFileFormatType? FileFormat { get; set; }
+ ///
+ /// Gets or sets the loop count. The number of times to loop the animation. 0 means infinitely.
+ ///
+ public ushort AnimationLoopCount { get; set; } = 1;
+
///
public IDeepCloneable DeepClone() => new WebpMetadata(this);
}
diff --git a/src/ImageSharp/Metadata/ImageFrameMetadata.cs b/src/ImageSharp/Metadata/ImageFrameMetadata.cs
index 1cad4ebe8..f8ed18e28 100644
--- a/src/ImageSharp/Metadata/ImageFrameMetadata.cs
+++ b/src/ImageSharp/Metadata/ImageFrameMetadata.cs
@@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Metadata
///
public sealed class ImageFrameMetadata : IDeepCloneable
{
- private readonly Dictionary formatMetadata = new Dictionary();
+ private readonly Dictionary formatMetadata = new();
///
/// Initializes a new instance of the class.
@@ -67,7 +67,7 @@ namespace SixLabors.ImageSharp.Metadata
public IptcProfile IptcProfile { get; set; }
///
- public ImageFrameMetadata DeepClone() => new ImageFrameMetadata(this);
+ public ImageFrameMetadata DeepClone() => new(this);
///
/// Gets the metadata value associated with the specified key.
diff --git a/src/ImageSharp/Metadata/ImageMetadata.cs b/src/ImageSharp/Metadata/ImageMetadata.cs
index 89592f776..cddb76d10 100644
--- a/src/ImageSharp/Metadata/ImageMetadata.cs
+++ b/src/ImageSharp/Metadata/ImageMetadata.cs
@@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Metadata
///
public const PixelResolutionUnit DefaultPixelResolutionUnits = PixelResolutionUnit.PixelsPerInch;
- private readonly Dictionary formatMetadata = new Dictionary();
+ private readonly Dictionary formatMetadata = new();
private double horizontalResolution;
private double verticalResolution;
@@ -175,7 +175,7 @@ namespace SixLabors.ImageSharp.Metadata
}
///
- public ImageMetadata DeepClone() => new ImageMetadata(this);
+ public ImageMetadata DeepClone() => new(this);
///
/// Synchronizes the profiles with the current metadata.
diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs
index 63eab77bf..ae1dddba0 100644
--- a/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs
@@ -337,8 +337,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
{
using (Image image = provider.GetImage(WebpDecoder))
{
+ WebpMetadata webpMetaData = image.Metadata.GetWebpMetadata();
+ WebpFrameMetadata frameMetaData = image.Frames.RootFrame.Metadata.GetWebpMetadata();
+
image.DebugSaveMultiFrame(provider);
image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Exact);
+
+ Assert.Equal(0, webpMetaData.AnimationLoopCount);
+ Assert.Equal(150U, frameMetaData.FrameDuration);
}
}
@@ -349,8 +355,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
{
using (Image image = provider.GetImage(WebpDecoder))
{
+ WebpMetadata webpMetaData = image.Metadata.GetWebpMetadata();
+ WebpFrameMetadata frameMetaData = image.Frames.RootFrame.Metadata.GetWebpMetadata();
+
image.DebugSaveMultiFrame(provider);
image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Tolerant(0.04f));
+
+ Assert.Equal(0, webpMetaData.AnimationLoopCount);
+ Assert.Equal(150U, frameMetaData.FrameDuration);
}
}