Skip to content

Commit

Permalink
Add webp decoder option BackgroundColorHandling to decide howto handl…
Browse files Browse the repository at this point in the history
…e the background color in the ANIM chunk
  • Loading branch information
brianpopow committed Oct 9, 2023
1 parent 63d1d2c commit a80ae33
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public WebpTiffCompression(DecoderOptions options, MemoryAllocator memoryAllocat
/// <inheritdoc/>
protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span<byte> buffer, CancellationToken cancellationToken)
{
using WebpDecoderCore decoder = new(this.options);
using WebpDecoderCore decoder = new(new WebpDecoderOptions());
using Image<Rgb24> image = decoder.Decode<Rgb24>(stream, cancellationToken);
CopyImageBytesToBuffer(buffer, image.Frames.RootFrame.PixelBuffer);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using SixLabors.ImageSharp.Formats.Tiff.Compression.Decompressors;
using SixLabors.ImageSharp.Formats.Tiff.Constants;
using SixLabors.ImageSharp.Formats.Tiff.PhotometricInterpretation;
using SixLabors.ImageSharp.Formats.Webp;
using SixLabors.ImageSharp.Memory;

namespace SixLabors.ImageSharp.Formats.Tiff.Compression;
Expand Down
21 changes: 21 additions & 0 deletions src/ImageSharp/Formats/Webp/BackgroundColorHandling.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

namespace SixLabors.ImageSharp.Formats.Webp;

/// <summary>
/// Enum to decide how to handle the background color of the Animation chunk during decoding.
/// </summary>
public enum BackgroundColorHandling
{
/// <summary>
/// The background color of the ANIM chunk will be used to initialize the canvas to fill the unused space on the canvas around the frame.
/// Also, if AnimationDisposalMethod.Dispose is used, this color will be used to restore the canvas background.
/// </summary>
Standard = 0,

/// <summary>
/// The background color of the ANIM chunk is ignored and instead the canvas is initialized with black, BGRA(0, 0, 0, 0).
/// </summary>
Ignore = 1
}
14 changes: 12 additions & 2 deletions src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,17 +52,24 @@ internal class WebpAnimationDecoder : IDisposable
/// </summary>
private IMemoryOwner<byte>? alphaData;

/// <summary>
/// The flag to decide how to handle the background color in the Animation Chunk.
/// </summary>
private readonly BackgroundColorHandling backgroundColorHandling;

/// <summary>
/// Initializes a new instance of the <see cref="WebpAnimationDecoder"/> class.
/// </summary>
/// <param name="memoryAllocator">The memory allocator.</param>
/// <param name="configuration">The global configuration.</param>
/// <param name="maxFrames">The maximum number of frames to decode. Inclusive.</param>
public WebpAnimationDecoder(MemoryAllocator memoryAllocator, Configuration configuration, uint maxFrames)
/// <param name="backgroundColorHandling">The flag to decide how to handle the background color in the Animation Chunk.</param>
public WebpAnimationDecoder(MemoryAllocator memoryAllocator, Configuration configuration, uint maxFrames, BackgroundColorHandling backgroundColorHandling)
{
this.memoryAllocator = memoryAllocator;
this.configuration = configuration;
this.maxFrames = maxFrames;
this.backgroundColorHandling = backgroundColorHandling;
}

/// <summary>
Expand Down Expand Up @@ -94,7 +101,10 @@ public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, WebpFeatures feat
switch (chunkType)
{
case WebpChunkType.Animation:
uint dataSize = this.ReadFrame(stream, ref image, ref previousFrame, width, height, features.AnimationBackgroundColor!.Value);
Color backgroundColor = this.backgroundColorHandling == BackgroundColorHandling.Ignore
? new Color(new Bgra32(0, 0, 0, 0))
: features.AnimationBackgroundColor!.Value;
uint dataSize = this.ReadFrame(stream, ref image, ref previousFrame, width, height, backgroundColor);
remainingBytes -= (int)dataSize;
break;
case WebpChunkType.Xmp:
Expand Down
18 changes: 13 additions & 5 deletions src/ImageSharp/Formats/Webp/WebpDecoder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Webp;
/// <summary>
/// Image decoder for generating an image out of a webp stream.
/// </summary>
public sealed class WebpDecoder : ImageDecoder
public sealed class WebpDecoder : SpecializedImageDecoder<WebpDecoderOptions>
{
private WebpDecoder()
{
Expand All @@ -25,25 +25,33 @@ protected override ImageInfo Identify(DecoderOptions options, Stream stream, Can
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));

using WebpDecoderCore decoder = new(options);
using WebpDecoderCore decoder = new(new WebpDecoderOptions() { GeneralOptions = options });
return decoder.Identify(options.Configuration, stream, cancellationToken);
}

/// <inheritdoc/>
protected override Image<TPixel> Decode<TPixel>(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
protected override Image<TPixel> Decode<TPixel>(WebpDecoderOptions options, Stream stream, CancellationToken cancellationToken)
{
Guard.NotNull(options, nameof(options));
Guard.NotNull(stream, nameof(stream));

using WebpDecoderCore decoder = new(options);
Image<TPixel> image = decoder.Decode<TPixel>(options.Configuration, stream, cancellationToken);
Image<TPixel> image = decoder.Decode<TPixel>(options.GeneralOptions.Configuration, stream, cancellationToken);

ScaleToTargetSize(options, image);
ScaleToTargetSize(options.GeneralOptions, image);

return image;
}

/// <inheritdoc/>
protected override Image Decode(WebpDecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode<Rgba32>(options, stream, cancellationToken);

/// <inheritdoc/>
protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
=> this.Decode<Rgba32>(options, stream, cancellationToken);

/// <inheritdoc/>
protected override WebpDecoderOptions CreateDefaultSpecializedOptions(DecoderOptions options)
=> new() { GeneralOptions = options };
}
18 changes: 12 additions & 6 deletions src/ImageSharp/Formats/Webp/WebpDecoderCore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,22 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable
/// </summary>
private WebpImageInfo? webImageInfo;

/// <summary>
/// The flag to decide how to handle the background color in the Animation Chunk.
/// </summary>
private BackgroundColorHandling backgroundColorHandling;

/// <summary>
/// Initializes a new instance of the <see cref="WebpDecoderCore"/> class.
/// </summary>
/// <param name="options">The decoder options.</param>
public WebpDecoderCore(DecoderOptions options)
public WebpDecoderCore(WebpDecoderOptions options)
{
this.Options = options;
this.configuration = options.Configuration;
this.skipMetadata = options.SkipMetadata;
this.maxFrames = options.MaxFrames;
this.Options = options.GeneralOptions;
this.backgroundColorHandling = options.BackgroundColorHandling;
this.configuration = options.GeneralOptions.Configuration;
this.skipMetadata = options.GeneralOptions.SkipMetadata;
this.maxFrames = options.GeneralOptions.MaxFrames;
this.memoryAllocator = this.configuration.MemoryAllocator;
}

Expand All @@ -83,7 +89,7 @@ public Image<TPixel> Decode<TPixel>(BufferedReadStream stream, CancellationToken
{
if (this.webImageInfo.Features is { Animation: true })
{
using WebpAnimationDecoder animationDecoder = new(this.memoryAllocator, this.configuration, this.maxFrames);
using WebpAnimationDecoder animationDecoder = new(this.memoryAllocator, this.configuration, this.maxFrames, this.backgroundColorHandling);
return animationDecoder.Decode<TPixel>(stream, this.webImageInfo.Features, this.webImageInfo.Width, this.webImageInfo.Height, fileSize);
}

Expand Down
21 changes: 21 additions & 0 deletions src/ImageSharp/Formats/Webp/WebpDecoderOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.

namespace SixLabors.ImageSharp.Formats.Webp;

/// <summary>
/// Configuration options for decoding webp images.
/// </summary>
public sealed class WebpDecoderOptions : ISpecializedDecoderOptions
{
/// <inheritdoc/>
public DecoderOptions GeneralOptions { get; init; } = new();

/// <summary>
/// Gets the flag to decide how to handle the background color Animation Chunk.
/// The specification is vague on how to handle the background color of the animation chunk.
/// This option let's the user choose how to deal with it.
/// </summary>
/// <see href="https://developers.google.com/speed/webp/docs/riff_container#animation"/>
public BackgroundColorHandling BackgroundColorHandling { get; init; } = BackgroundColorHandling.Standard;
}

0 comments on commit a80ae33

Please sign in to comment.