Browse Source

Add loopcount and duration to the webp metadata

pull/1985/head
Brian Popow 5 years ago
parent
commit
4c30ccdf04
  1. 8
      src/ImageSharp/Formats/Gif/GifFormat.cs
  2. 7
      src/ImageSharp/Formats/Webp/MetadataExtensions.cs
  3. 36
      src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs
  4. 11
      src/ImageSharp/Formats/Webp/WebpFormat.cs
  5. 33
      src/ImageSharp/Formats/Webp/WebpFrameMetadata.cs
  6. 11
      src/ImageSharp/Formats/Webp/WebpMetadata.cs
  7. 4
      src/ImageSharp/Metadata/ImageFrameMetadata.cs
  8. 4
      src/ImageSharp/Metadata/ImageMetadata.cs
  9. 12
      tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs

8
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. // Licensed under the Apache License, Version 2.0.
using System.Collections.Generic; using System.Collections.Generic;
@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary> /// <summary>
/// Gets the current instance. /// Gets the current instance.
/// </summary> /// </summary>
public static GifFormat Instance { get; } = new GifFormat(); public static GifFormat Instance { get; } = new();
/// <inheritdoc/> /// <inheritdoc/>
public string Name => "GIF"; public string Name => "GIF";
@ -32,9 +32,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
public IEnumerable<string> FileExtensions => GifConstants.FileExtensions; public IEnumerable<string> FileExtensions => GifConstants.FileExtensions;
/// <inheritdoc/> /// <inheritdoc/>
public GifMetadata CreateDefaultFormatMetadata() => new GifMetadata(); public GifMetadata CreateDefaultFormatMetadata() => new();
/// <inheritdoc/> /// <inheritdoc/>
public GifFrameMetadata CreateDefaultFormatFrameMetadata() => new GifFrameMetadata(); public GifFrameMetadata CreateDefaultFormatFrameMetadata() => new();
} }
} }

7
src/ImageSharp/Formats/Webp/MetadataExtensions.cs

@ -17,5 +17,12 @@ namespace SixLabors.ImageSharp
/// <param name="metadata">The metadata this method extends.</param> /// <param name="metadata">The metadata this method extends.</param>
/// <returns>The <see cref="WebpMetadata"/>.</returns> /// <returns>The <see cref="WebpMetadata"/>.</returns>
public static WebpMetadata GetWebpMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(WebpFormat.Instance); public static WebpMetadata GetWebpMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(WebpFormat.Instance);
/// <summary>
/// Gets the webp format specific metadata for the image frame.
/// </summary>
/// <param name="metadata">The metadata this method extends.</param>
/// <returns>The <see cref="WebpFrameMetadata"/>.</returns>
public static WebpFrameMetadata GetWebpMetadata(this ImageFrameMetadata metadata) => metadata.GetFormatMetadata(WebpFormat.Instance);
} }
} }

36
src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs

@ -3,6 +3,7 @@
using System; using System;
using System.Buffers; using System.Buffers;
using System.Runtime.CompilerServices;
using SixLabors.ImageSharp.Formats.Webp.Lossless; using SixLabors.ImageSharp.Formats.Webp.Lossless;
using SixLabors.ImageSharp.Formats.Webp.Lossy; using SixLabors.ImageSharp.Formats.Webp.Lossy;
using SixLabors.ImageSharp.IO; using SixLabors.ImageSharp.IO;
@ -37,6 +38,16 @@ namespace SixLabors.ImageSharp.Formats.Webp
/// </summary> /// </summary>
private Rectangle? restoreArea; private Rectangle? restoreArea;
/// <summary>
/// The abstract metadata.
/// </summary>
private ImageMetadata metadata;
/// <summary>
/// The gif specific metadata.
/// </summary>
private WebpMetadata webpMetadata;
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="WebpAnimationDecoder"/> class. /// Initializes a new instance of the <see cref="WebpAnimationDecoder"/> class.
/// </summary> /// </summary>
@ -68,6 +79,10 @@ namespace SixLabors.ImageSharp.Formats.Webp
Image<TPixel> image = null; Image<TPixel> image = null;
ImageFrame<TPixel> previousFrame = null; ImageFrame<TPixel> previousFrame = null;
this.metadata = new ImageMetadata();
this.webpMetadata = this.metadata.GetWebpMetadata();
this.webpMetadata.AnimationLoopCount = features.AnimationLoopCount;
int remainingBytes = (int)completeDataSize; int remainingBytes = (int)completeDataSize;
while (remainingBytes > 0) while (remainingBytes > 0)
{ {
@ -140,17 +155,22 @@ namespace SixLabors.ImageSharp.Formats.Webp
break; break;
} }
var metaData = new ImageMetadata();
ImageFrame<TPixel> currentFrame = null; ImageFrame<TPixel> currentFrame = null;
ImageFrame<TPixel> imageFrame; ImageFrame<TPixel> imageFrame;
if (previousFrame is null) if (previousFrame is null)
{ {
image = new Image<TPixel>(this.configuration, (int)width, (int)height, backgroundColor.ToPixel<TPixel>(), metaData); image = new Image<TPixel>(this.configuration, (int)width, (int)height, backgroundColor.ToPixel<TPixel>(), this.metadata);
this.SetFrameMetadata(image.Frames.RootFrame.Metadata, frameData.Duration);
imageFrame = image.Frames.RootFrame; imageFrame = image.Frames.RootFrame;
} }
else else
{ {
currentFrame = image.Frames.AddFrame(previousFrame); // This clones the frame and adds it the collection. currentFrame = image.Frames.AddFrame(previousFrame); // This clones the frame and adds it the collection.
this.SetFrameMetadata(currentFrame.Metadata, frameData.Duration);
imageFrame = currentFrame; imageFrame = currentFrame;
} }
@ -179,6 +199,18 @@ namespace SixLabors.ImageSharp.Formats.Webp
return (uint)(stream.Position - streamStartPosition); return (uint)(stream.Position - streamStartPosition);
} }
/// <summary>
/// Sets the frames metadata.
/// </summary>
/// <param name="meta">The metadata.</param>
/// <param name="duration">The frame duration.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void SetFrameMetadata(ImageFrameMetadata meta, uint duration)
{
WebpFrameMetadata frameMetadata = meta.GetWebpMetadata();
frameMetadata.FrameDuration = duration;
}
/// <summary> /// <summary>
/// Reads the ALPH chunk data. /// Reads the ALPH chunk data.
/// </summary> /// </summary>

11
src/ImageSharp/Formats/Webp/WebpFormat.cs

@ -6,9 +6,9 @@ using System.Collections.Generic;
namespace SixLabors.ImageSharp.Formats.Webp namespace SixLabors.ImageSharp.Formats.Webp
{ {
/// <summary> /// <summary>
/// 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.
/// </summary> /// </summary>
public sealed class WebpFormat : IImageFormat<WebpMetadata> public sealed class WebpFormat : IImageFormat<WebpMetadata, WebpFrameMetadata>
{ {
private WebpFormat() private WebpFormat()
{ {
@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
/// <summary> /// <summary>
/// Gets the current instance. /// Gets the current instance.
/// </summary> /// </summary>
public static WebpFormat Instance { get; } = new WebpFormat(); public static WebpFormat Instance { get; } = new();
/// <inheritdoc/> /// <inheritdoc/>
public string Name => "Webp"; public string Name => "Webp";
@ -32,6 +32,9 @@ namespace SixLabors.ImageSharp.Formats.Webp
public IEnumerable<string> FileExtensions => WebpConstants.FileExtensions; public IEnumerable<string> FileExtensions => WebpConstants.FileExtensions;
/// <inheritdoc/> /// <inheritdoc/>
public WebpMetadata CreateDefaultFormatMetadata() => new WebpMetadata(); public WebpMetadata CreateDefaultFormatMetadata() => new();
/// <inheritdoc/>
public WebpFrameMetadata CreateDefaultFormatFrameMetadata() => new();
} }
} }

33
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
{
/// <summary>
/// Provides webp specific metadata information for the image frame.
/// </summary>
public class WebpFrameMetadata : IDeepCloneable
{
/// <summary>
/// Initializes a new instance of the <see cref="WebpFrameMetadata"/> class.
/// </summary>
public WebpFrameMetadata()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="WebpFrameMetadata"/> class.
/// </summary>
/// <param name="other">The metadata to create an instance from.</param>
private WebpFrameMetadata(WebpFrameMetadata other) => this.FrameDuration = other.FrameDuration;
/// <summary>
/// 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.
/// </summary>
public uint FrameDuration { get; set; }
/// <inheritdoc/>
public IDeepCloneable DeepClone() => new WebpFrameMetadata(this);
}
}

11
src/ImageSharp/Formats/Webp/WebpMetadata.cs

@ -19,13 +19,22 @@ namespace SixLabors.ImageSharp.Formats.Webp
/// Initializes a new instance of the <see cref="WebpMetadata"/> class. /// Initializes a new instance of the <see cref="WebpMetadata"/> class.
/// </summary> /// </summary>
/// <param name="other">The metadata to create an instance from.</param> /// <param name="other">The metadata to create an instance from.</param>
private WebpMetadata(WebpMetadata other) => this.FileFormat = other.FileFormat; private WebpMetadata(WebpMetadata other)
{
this.FileFormat = other.FileFormat;
this.AnimationLoopCount = other.AnimationLoopCount;
}
/// <summary> /// <summary>
/// Gets or sets the webp file format used. Either lossless or lossy. /// Gets or sets the webp file format used. Either lossless or lossy.
/// </summary> /// </summary>
public WebpFileFormatType? FileFormat { get; set; } public WebpFileFormatType? FileFormat { get; set; }
/// <summary>
/// Gets or sets the loop count. The number of times to loop the animation. 0 means infinitely.
/// </summary>
public ushort AnimationLoopCount { get; set; } = 1;
/// <inheritdoc/> /// <inheritdoc/>
public IDeepCloneable DeepClone() => new WebpMetadata(this); public IDeepCloneable DeepClone() => new WebpMetadata(this);
} }

4
src/ImageSharp/Metadata/ImageFrameMetadata.cs

@ -15,7 +15,7 @@ namespace SixLabors.ImageSharp.Metadata
/// </summary> /// </summary>
public sealed class ImageFrameMetadata : IDeepCloneable<ImageFrameMetadata> public sealed class ImageFrameMetadata : IDeepCloneable<ImageFrameMetadata>
{ {
private readonly Dictionary<IImageFormat, IDeepCloneable> formatMetadata = new Dictionary<IImageFormat, IDeepCloneable>(); private readonly Dictionary<IImageFormat, IDeepCloneable> formatMetadata = new();
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="ImageFrameMetadata"/> class. /// Initializes a new instance of the <see cref="ImageFrameMetadata"/> class.
@ -67,7 +67,7 @@ namespace SixLabors.ImageSharp.Metadata
public IptcProfile IptcProfile { get; set; } public IptcProfile IptcProfile { get; set; }
/// <inheritdoc/> /// <inheritdoc/>
public ImageFrameMetadata DeepClone() => new ImageFrameMetadata(this); public ImageFrameMetadata DeepClone() => new(this);
/// <summary> /// <summary>
/// Gets the metadata value associated with the specified key. /// Gets the metadata value associated with the specified key.

4
src/ImageSharp/Metadata/ImageMetadata.cs

@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Metadata
/// </summary> /// </summary>
public const PixelResolutionUnit DefaultPixelResolutionUnits = PixelResolutionUnit.PixelsPerInch; public const PixelResolutionUnit DefaultPixelResolutionUnits = PixelResolutionUnit.PixelsPerInch;
private readonly Dictionary<IImageFormat, IDeepCloneable> formatMetadata = new Dictionary<IImageFormat, IDeepCloneable>(); private readonly Dictionary<IImageFormat, IDeepCloneable> formatMetadata = new();
private double horizontalResolution; private double horizontalResolution;
private double verticalResolution; private double verticalResolution;
@ -175,7 +175,7 @@ namespace SixLabors.ImageSharp.Metadata
} }
/// <inheritdoc/> /// <inheritdoc/>
public ImageMetadata DeepClone() => new ImageMetadata(this); public ImageMetadata DeepClone() => new(this);
/// <summary> /// <summary>
/// Synchronizes the profiles with the current metadata. /// Synchronizes the profiles with the current metadata.

12
tests/ImageSharp.Tests/Formats/WebP/WebpDecoderTests.cs

@ -337,8 +337,14 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
{ {
using (Image<TPixel> image = provider.GetImage(WebpDecoder)) using (Image<TPixel> image = provider.GetImage(WebpDecoder))
{ {
WebpMetadata webpMetaData = image.Metadata.GetWebpMetadata();
WebpFrameMetadata frameMetaData = image.Frames.RootFrame.Metadata.GetWebpMetadata();
image.DebugSaveMultiFrame(provider); image.DebugSaveMultiFrame(provider);
image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Exact); 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<TPixel> image = provider.GetImage(WebpDecoder)) using (Image<TPixel> image = provider.GetImage(WebpDecoder))
{ {
WebpMetadata webpMetaData = image.Metadata.GetWebpMetadata();
WebpFrameMetadata frameMetaData = image.Frames.RootFrame.Metadata.GetWebpMetadata();
image.DebugSaveMultiFrame(provider); image.DebugSaveMultiFrame(provider);
image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Tolerant(0.04f)); image.CompareToReferenceOutputMultiFrame(provider, ImageComparer.Tolerant(0.04f));
Assert.Equal(0, webpMetaData.AnimationLoopCount);
Assert.Equal(150U, frameMetaData.FrameDuration);
} }
} }

Loading…
Cancel
Save