Browse Source

Add loopcount and duration to the webp metadata

pull/1985/head
Brian Popow 4 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.
using System.Collections.Generic;
@ -17,7 +17,7 @@ namespace SixLabors.ImageSharp.Formats.Gif
/// <summary>
/// Gets the current instance.
/// </summary>
public static GifFormat Instance { get; } = new GifFormat();
public static GifFormat Instance { get; } = new();
/// <inheritdoc/>
public string Name => "GIF";
@ -32,9 +32,9 @@ namespace SixLabors.ImageSharp.Formats.Gif
public IEnumerable<string> FileExtensions => GifConstants.FileExtensions;
/// <inheritdoc/>
public GifMetadata CreateDefaultFormatMetadata() => new GifMetadata();
public GifMetadata CreateDefaultFormatMetadata() => new();
/// <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>
/// <returns>The <see cref="WebpMetadata"/>.</returns>
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.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
/// </summary>
private Rectangle? restoreArea;
/// <summary>
/// The abstract metadata.
/// </summary>
private ImageMetadata metadata;
/// <summary>
/// The gif specific metadata.
/// </summary>
private WebpMetadata webpMetadata;
/// <summary>
/// Initializes a new instance of the <see cref="WebpAnimationDecoder"/> class.
/// </summary>
@ -68,6 +79,10 @@ namespace SixLabors.ImageSharp.Formats.Webp
Image<TPixel> image = null;
ImageFrame<TPixel> 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<TPixel> currentFrame = null;
ImageFrame<TPixel> imageFrame;
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;
}
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);
}
/// <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>
/// Reads the ALPH chunk data.
/// </summary>

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

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

4
src/ImageSharp/Metadata/ImageFrameMetadata.cs

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

4
src/ImageSharp/Metadata/ImageMetadata.cs

@ -33,7 +33,7 @@ namespace SixLabors.ImageSharp.Metadata
/// </summary>
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 verticalResolution;
@ -175,7 +175,7 @@ namespace SixLabors.ImageSharp.Metadata
}
/// <inheritdoc/>
public ImageMetadata DeepClone() => new ImageMetadata(this);
public ImageMetadata DeepClone() => new(this);
/// <summary>
/// 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))
{
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<TPixel> 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);
}
}

Loading…
Cancel
Save