From a80ae33e4cac6acd60c0303f6740ce85c85cccad Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Mon, 9 Oct 2023 18:31:25 +0200 Subject: [PATCH] Add webp decoder option BackgroundColorHandling to decide howto handle the background color in the ANIM chunk --- .../Decompressors/WebpTiffCompression.cs | 2 +- .../Compression/TiffDecompressorsFactory.cs | 1 + .../Formats/Webp/BackgroundColorHandling.cs | 21 +++++++++++++++++++ .../Formats/Webp/WebpAnimationDecoder.cs | 14 +++++++++++-- src/ImageSharp/Formats/Webp/WebpDecoder.cs | 18 +++++++++++----- .../Formats/Webp/WebpDecoderCore.cs | 18 ++++++++++------ .../Formats/Webp/WebpDecoderOptions.cs | 21 +++++++++++++++++++ 7 files changed, 81 insertions(+), 14 deletions(-) create mode 100644 src/ImageSharp/Formats/Webp/BackgroundColorHandling.cs create mode 100644 src/ImageSharp/Formats/Webp/WebpDecoderOptions.cs diff --git a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs index a5ce4f8426..416472e830 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/Decompressors/WebpTiffCompression.cs @@ -32,7 +32,7 @@ public WebpTiffCompression(DecoderOptions options, MemoryAllocator memoryAllocat /// protected override void Decompress(BufferedReadStream stream, int byteCount, int stripHeight, Span buffer, CancellationToken cancellationToken) { - using WebpDecoderCore decoder = new(this.options); + using WebpDecoderCore decoder = new(new WebpDecoderOptions()); using Image image = decoder.Decode(stream, cancellationToken); CopyImageBytesToBuffer(buffer, image.Frames.RootFrame.PixelBuffer); } diff --git a/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs b/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs index b9a1f31553..720e376b9d 100644 --- a/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs +++ b/src/ImageSharp/Formats/Tiff/Compression/TiffDecompressorsFactory.cs @@ -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; diff --git a/src/ImageSharp/Formats/Webp/BackgroundColorHandling.cs b/src/ImageSharp/Formats/Webp/BackgroundColorHandling.cs new file mode 100644 index 0000000000..c843311c7f --- /dev/null +++ b/src/ImageSharp/Formats/Webp/BackgroundColorHandling.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Webp; + +/// +/// Enum to decide how to handle the background color of the Animation chunk during decoding. +/// +public enum BackgroundColorHandling +{ + /// + /// 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. + /// + Standard = 0, + + /// + /// The background color of the ANIM chunk is ignored and instead the canvas is initialized with black, BGRA(0, 0, 0, 0). + /// + Ignore = 1 +} diff --git a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs index 21337ce6c8..90c9c70b26 100644 --- a/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs +++ b/src/ImageSharp/Formats/Webp/WebpAnimationDecoder.cs @@ -52,17 +52,24 @@ internal class WebpAnimationDecoder : IDisposable /// private IMemoryOwner? alphaData; + /// + /// The flag to decide how to handle the background color in the Animation Chunk. + /// + private readonly BackgroundColorHandling backgroundColorHandling; + /// /// Initializes a new instance of the class. /// /// The memory allocator. /// The global configuration. /// The maximum number of frames to decode. Inclusive. - public WebpAnimationDecoder(MemoryAllocator memoryAllocator, Configuration configuration, uint maxFrames) + /// The flag to decide how to handle the background color in the Animation Chunk. + public WebpAnimationDecoder(MemoryAllocator memoryAllocator, Configuration configuration, uint maxFrames, BackgroundColorHandling backgroundColorHandling) { this.memoryAllocator = memoryAllocator; this.configuration = configuration; this.maxFrames = maxFrames; + this.backgroundColorHandling = backgroundColorHandling; } /// @@ -94,7 +101,10 @@ public Image Decode(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: diff --git a/src/ImageSharp/Formats/Webp/WebpDecoder.cs b/src/ImageSharp/Formats/Webp/WebpDecoder.cs index daa5eaf4fe..e23b817ccd 100644 --- a/src/ImageSharp/Formats/Webp/WebpDecoder.cs +++ b/src/ImageSharp/Formats/Webp/WebpDecoder.cs @@ -8,7 +8,7 @@ namespace SixLabors.ImageSharp.Formats.Webp; /// /// Image decoder for generating an image out of a webp stream. /// -public sealed class WebpDecoder : ImageDecoder +public sealed class WebpDecoder : SpecializedImageDecoder { private WebpDecoder() { @@ -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); } /// - protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) + protected override Image Decode(WebpDecoderOptions options, Stream stream, CancellationToken cancellationToken) { Guard.NotNull(options, nameof(options)); Guard.NotNull(stream, nameof(stream)); using WebpDecoderCore decoder = new(options); - Image image = decoder.Decode(options.Configuration, stream, cancellationToken); + Image image = decoder.Decode(options.GeneralOptions.Configuration, stream, cancellationToken); - ScaleToTargetSize(options, image); + ScaleToTargetSize(options.GeneralOptions, image); return image; } + /// + protected override Image Decode(WebpDecoderOptions options, Stream stream, CancellationToken cancellationToken) + => this.Decode(options, stream, cancellationToken); + /// protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) => this.Decode(options, stream, cancellationToken); + + /// + protected override WebpDecoderOptions CreateDefaultSpecializedOptions(DecoderOptions options) + => new() { GeneralOptions = options }; } diff --git a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs index 223e15a0e7..8832ac1068 100644 --- a/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs +++ b/src/ImageSharp/Formats/Webp/WebpDecoderCore.cs @@ -48,16 +48,22 @@ internal sealed class WebpDecoderCore : IImageDecoderInternals, IDisposable /// private WebpImageInfo? webImageInfo; + /// + /// The flag to decide how to handle the background color in the Animation Chunk. + /// + private BackgroundColorHandling backgroundColorHandling; + /// /// Initializes a new instance of the class. /// /// The decoder options. - 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; } @@ -83,7 +89,7 @@ public Image Decode(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(stream, this.webImageInfo.Features, this.webImageInfo.Width, this.webImageInfo.Height, fileSize); } diff --git a/src/ImageSharp/Formats/Webp/WebpDecoderOptions.cs b/src/ImageSharp/Formats/Webp/WebpDecoderOptions.cs new file mode 100644 index 0000000000..6fb15acbb4 --- /dev/null +++ b/src/ImageSharp/Formats/Webp/WebpDecoderOptions.cs @@ -0,0 +1,21 @@ +// Copyright (c) Six Labors. +// Licensed under the Six Labors Split License. + +namespace SixLabors.ImageSharp.Formats.Webp; + +/// +/// Configuration options for decoding webp images. +/// +public sealed class WebpDecoderOptions : ISpecializedDecoderOptions +{ + /// + public DecoderOptions GeneralOptions { get; init; } = new(); + + /// + /// 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. + /// + /// + public BackgroundColorHandling BackgroundColorHandling { get; init; } = BackgroundColorHandling.Standard; +}