From 2fc9719a6234404ae11c6ab15eb3f660acaf5b75 Mon Sep 17 00:00:00 2001 From: James Jackson-South Date: Sun, 16 Oct 2022 21:21:02 +1000 Subject: [PATCH] Convert WebpEncoder --- src/ImageSharp/Formats/Webp/AlphaEncoder.cs | 12 ++- .../Formats/Webp/IWebpEncoderOptions.cs | 79 ---------------- .../Formats/Webp/Lossless/Vp8LEncoder.cs | 15 ++- .../Formats/Webp/Lossy/Vp8Encoder.cs | 20 +++- src/ImageSharp/Formats/Webp/WebpEncoder.cs | 92 +++++++++++++------ .../Formats/Webp/WebpEncoderCore.cs | 36 +++++--- 6 files changed, 124 insertions(+), 130 deletions(-) delete mode 100644 src/ImageSharp/Formats/Webp/IWebpEncoderOptions.cs diff --git a/src/ImageSharp/Formats/Webp/AlphaEncoder.cs b/src/ImageSharp/Formats/Webp/AlphaEncoder.cs index 1e60235e26..fc5580a4a7 100644 --- a/src/ImageSharp/Formats/Webp/AlphaEncoder.cs +++ b/src/ImageSharp/Formats/Webp/AlphaEncoder.cs @@ -24,10 +24,11 @@ internal class AlphaEncoder : IDisposable /// The to encode from. /// The global configuration. /// The memory manager. + /// Whether to skip metadata encoding. /// Indicates, if the data should be compressed with the lossless webp compression. /// The size in bytes of the alpha data. /// The encoded alpha data. - public IMemoryOwner EncodeAlpha(Image image, Configuration configuration, MemoryAllocator memoryAllocator, bool compress, out int size) + public IMemoryOwner EncodeAlpha(Image image, Configuration configuration, MemoryAllocator memoryAllocator, bool skipMetadata, bool compress, out int size) where TPixel : unmanaged, IPixel { int width = image.Width; @@ -36,14 +37,15 @@ internal class AlphaEncoder : IDisposable if (compress) { - WebpEncodingMethod effort = WebpEncodingMethod.Default; - int quality = 8 * (int)effort; - using var lossLessEncoder = new Vp8LEncoder( + const WebpEncodingMethod effort = WebpEncodingMethod.Default; + const int quality = 8 * (int)effort; + using Vp8LEncoder lossLessEncoder = new( memoryAllocator, configuration, width, height, quality, + skipMetadata, effort, WebpTransparentColorMode.Preserve, false, @@ -75,7 +77,7 @@ internal class AlphaEncoder : IDisposable { int width = image.Width; int height = image.Height; - var alphaAsImage = new Image(width, height); + Image alphaAsImage = new(width, height); for (int y = 0; y < height; y++) { diff --git a/src/ImageSharp/Formats/Webp/IWebpEncoderOptions.cs b/src/ImageSharp/Formats/Webp/IWebpEncoderOptions.cs deleted file mode 100644 index bc316d08c7..0000000000 --- a/src/ImageSharp/Formats/Webp/IWebpEncoderOptions.cs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright (c) Six Labors. -// Licensed under the Six Labors Split License. - -namespace SixLabors.ImageSharp.Formats.Webp; - -/// -/// Configuration options for use during webp encoding. -/// -internal interface IWebpEncoderOptions -{ - /// - /// Gets the webp file format used. Either lossless or lossy. - /// Defaults to lossy. - /// - WebpFileFormatType? FileFormat { get; } - - /// - /// Gets the compression quality. Between 0 and 100. - /// For lossy, 0 gives the smallest size and 100 the largest. For lossless, - /// this parameter is the amount of effort put into the compression: 0 is the fastest but gives larger - /// files compared to the slowest, but best, 100. - /// Defaults to 75. - /// - int Quality { get; } - - /// - /// Gets the encoding method to use. Its a quality/speed trade-off (0=fast, 6=slower-better). - /// Defaults to 4. - /// - WebpEncodingMethod Method { get; } - - /// - /// Gets a value indicating whether the alpha plane should be compressed with Webp lossless format. - /// Defaults to true. - /// - bool UseAlphaCompression { get; } - - /// - /// Gets the number of entropy-analysis passes (in [1..10]). - /// Defaults to 1. - /// - int EntropyPasses { get; } - - /// - /// Gets the amplitude of the spatial noise shaping. Spatial noise shaping (or sns for short) refers to a general collection of built-in algorithms - /// used to decide which area of the picture should use relatively less bits, and where else to better transfer these bits. - /// The possible range goes from 0 (algorithm is off) to 100 (the maximal effect). - /// Defaults to 50. - /// - int SpatialNoiseShaping { get; } - - /// - /// Gets the strength of the deblocking filter, between 0 (no filtering) and 100 (maximum filtering). - /// A value of 0 will turn off any filtering. Higher value will increase the strength of the filtering process applied after decoding the picture. - /// The higher the value the smoother the picture will appear. - /// Typical values are usually in the range of 20 to 50. - /// Defaults to 60. - /// - int FilterStrength { get; } - - /// - /// Gets a value indicating whether to preserve the exact RGB values under transparent area. Otherwise, discard this invisible - /// RGB information for better compression. - /// The default value is Clear. - /// - WebpTransparentColorMode TransparentColorMode { get; } - - /// - /// Gets a value indicating whether near lossless mode should be used. - /// This option adjusts pixel values to help compressibility, but has minimal impact on the visual quality. - /// - bool NearLossless { get; } - - /// - /// Gets the quality of near-lossless image preprocessing. The range is 0 (maximum preprocessing) to 100 (no preprocessing, the default). - /// The typical value is around 60. Note that lossy with -q 100 can at times yield better results. - /// - int NearLosslessQuality { get; } -} diff --git a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs index 3917c863b9..7f1f4f4e2f 100644 --- a/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs @@ -8,6 +8,8 @@ using System.Runtime.InteropServices; using SixLabors.ImageSharp.Formats.Webp.BitWriter; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; +using SixLabors.ImageSharp.Metadata.Profiles.Xmp; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Webp.Lossless; @@ -67,6 +69,11 @@ internal class Vp8LEncoder : IDisposable /// private readonly WebpTransparentColorMode transparentColorMode; + /// + /// Whether to skip metadata during encoding. + /// + private readonly bool skipMetadata; + /// /// Indicating whether near lossless mode should be used. /// @@ -91,6 +98,7 @@ internal class Vp8LEncoder : IDisposable /// The width of the input image. /// The height of the input image. /// The encoding quality. + /// Whether to skip metadata encoding. /// Quality/speed trade-off (0=fast, 6=slower-better). /// Flag indicating whether to preserve the exact RGB values under transparent area. /// Otherwise, discard this invisible RGB information for better compression. @@ -102,6 +110,7 @@ internal class Vp8LEncoder : IDisposable int width, int height, int quality, + bool skipMetadata, WebpEncodingMethod method, WebpTransparentColorMode transparentColorMode, bool nearLossless, @@ -113,6 +122,7 @@ internal class Vp8LEncoder : IDisposable this.memoryAllocator = memoryAllocator; this.configuration = configuration; this.quality = Numerics.Clamp(quality, 0, 100); + this.skipMetadata = skipMetadata; this.method = method; this.transparentColorMode = transparentColorMode; this.nearLossless = nearLossless; @@ -239,6 +249,9 @@ internal class Vp8LEncoder : IDisposable ImageMetadata metadata = image.Metadata; metadata.SyncProfiles(); + ExifProfile exifProfile = this.skipMetadata ? null : metadata.ExifProfile; + XmpProfile xmpProfile = this.skipMetadata ? null : metadata.XmpProfile; + // Convert image pixels to bgra array. bool hasAlpha = this.ConvertPixelsToBgra(image, width, height); @@ -252,7 +265,7 @@ internal class Vp8LEncoder : IDisposable this.EncodeStream(image); // Write bytes from the bitwriter buffer to the stream. - this.bitWriter.WriteEncodedImageToStream(stream, metadata.ExifProfile, metadata.XmpProfile, metadata.IccProfile, (uint)width, (uint)height, hasAlpha); + this.bitWriter.WriteEncodedImageToStream(stream, exifProfile, xmpProfile, metadata.IccProfile, (uint)width, (uint)height, hasAlpha); } /// diff --git a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs index b15ccc052b..309e4175a0 100644 --- a/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs +++ b/src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs @@ -6,6 +6,8 @@ using System.Runtime.CompilerServices; using SixLabors.ImageSharp.Formats.Webp.BitWriter; using SixLabors.ImageSharp.Memory; using SixLabors.ImageSharp.Metadata; +using SixLabors.ImageSharp.Metadata.Profiles.Exif; +using SixLabors.ImageSharp.Metadata.Profiles.Xmp; using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Webp.Lossy; @@ -55,6 +57,11 @@ internal class Vp8Encoder : IDisposable /// private Vp8BitWriter bitWriter; + /// + /// Whether to skip metadata during encoding. + /// + private readonly bool skipMetadata; + private readonly Vp8RdLevel rdOptLevel; private int maxI4HeaderBits; @@ -94,6 +101,7 @@ internal class Vp8Encoder : IDisposable /// The width of the input image. /// The height of the input image. /// The encoding quality. + /// Whether to skip metadata encoding. /// Quality/speed trade-off (0=fast, 6=slower-better). /// Number of entropy-analysis passes (in [1..10]). /// The filter the strength of the deblocking filter, between 0 (no filtering) and 100 (maximum filtering). @@ -105,6 +113,7 @@ internal class Vp8Encoder : IDisposable int width, int height, int quality, + bool skipMetadata, WebpEncodingMethod method, int entropyPasses, int filterStrength, @@ -116,6 +125,7 @@ internal class Vp8Encoder : IDisposable this.Width = width; this.Height = height; this.quality = Numerics.Clamp(quality, 0, 100); + this.skipMetadata = skipMetadata; this.method = method; this.entropyPasses = Numerics.Clamp(entropyPasses, 1, 10); this.filterStrength = Numerics.Clamp(filterStrength, 0, 100); @@ -342,7 +352,7 @@ internal class Vp8Encoder : IDisposable if (hasAlpha) { // TODO: This can potentially run in an separate task. - IMemoryOwner encodedAlphaData = alphaEncoder.EncodeAlpha(image, this.configuration, this.memoryAllocator, this.alphaCompression, out alphaDataSize); + IMemoryOwner encodedAlphaData = alphaEncoder.EncodeAlpha(image, this.configuration, this.memoryAllocator, this.skipMetadata, this.alphaCompression, out alphaDataSize); alphaData = encodedAlphaData.GetSpan(); if (alphaDataSize < pixelCount) { @@ -384,10 +394,14 @@ internal class Vp8Encoder : IDisposable // Write bytes from the bitwriter buffer to the stream. ImageMetadata metadata = image.Metadata; metadata.SyncProfiles(); + + ExifProfile exifProfile = this.skipMetadata ? null : metadata.ExifProfile; + XmpProfile xmpProfile = this.skipMetadata ? null : metadata.XmpProfile; + this.bitWriter.WriteEncodedImageToStream( stream, - metadata.ExifProfile, - metadata.XmpProfile, + exifProfile, + xmpProfile, metadata.IccProfile, (uint)width, (uint)height, diff --git a/src/ImageSharp/Formats/Webp/WebpEncoder.cs b/src/ImageSharp/Formats/Webp/WebpEncoder.cs index b6a45555d8..359128254f 100644 --- a/src/ImageSharp/Formats/Webp/WebpEncoder.cs +++ b/src/ImageSharp/Formats/Webp/WebpEncoder.cs @@ -2,58 +2,94 @@ // Licensed under the Six Labors Split License. using SixLabors.ImageSharp.Advanced; -using SixLabors.ImageSharp.PixelFormats; namespace SixLabors.ImageSharp.Formats.Webp; /// /// Image encoder for writing an image to a stream in the Webp format. /// -public sealed class WebpEncoder : IImageEncoder, IWebpEncoderOptions +public sealed class WebpEncoder : ImageEncoder { - /// - public WebpFileFormatType? FileFormat { get; set; } + /// + /// Gets the webp file format used. Either lossless or lossy. + /// Defaults to lossy. + /// + public WebpFileFormatType? FileFormat { get; init; } - /// - public int Quality { get; set; } = 75; + /// + /// Gets the compression quality. Between 0 and 100. + /// For lossy, 0 gives the smallest size and 100 the largest. For lossless, + /// this parameter is the amount of effort put into the compression: 0 is the fastest but gives larger + /// files compared to the slowest, but best, 100. + /// Defaults to 75. + /// + public int Quality { get; init; } = 75; - /// - public WebpEncodingMethod Method { get; set; } = WebpEncodingMethod.Default; + /// + /// Gets the encoding method to use. Its a quality/speed trade-off (0=fast, 6=slower-better). + /// Defaults to 4. + /// + public WebpEncodingMethod Method { get; init; } = WebpEncodingMethod.Default; - /// - public bool UseAlphaCompression { get; set; } = true; + /// + /// Gets a value indicating whether the alpha plane should be compressed with Webp lossless format. + /// Defaults to true. + /// + public bool UseAlphaCompression { get; init; } = true; - /// - public int EntropyPasses { get; set; } = 1; + /// + /// Gets the number of entropy-analysis passes (in [1..10]). + /// Defaults to 1. + /// + public int EntropyPasses { get; init; } = 1; - /// - public int SpatialNoiseShaping { get; set; } = 50; + /// + /// Gets the amplitude of the spatial noise shaping. Spatial noise shaping (or sns for short) refers to a general collection of built-in algorithms + /// used to decide which area of the picture should use relatively less bits, and where else to better transfer these bits. + /// The possible range goes from 0 (algorithm is off) to 100 (the maximal effect). + /// Defaults to 50. + /// + public int SpatialNoiseShaping { get; init; } = 50; - /// - public int FilterStrength { get; set; } = 60; + /// + /// Gets the strength of the deblocking filter, between 0 (no filtering) and 100 (maximum filtering). + /// A value of 0 will turn off any filtering. Higher value will increase the strength of the filtering process applied after decoding the picture. + /// The higher the value the smoother the picture will appear. + /// Typical values are usually in the range of 20 to 50. + /// Defaults to 60. + /// + public int FilterStrength { get; init; } = 60; - /// - public WebpTransparentColorMode TransparentColorMode { get; set; } = WebpTransparentColorMode.Clear; + /// + /// Gets a value indicating whether to preserve the exact RGB values under transparent area. Otherwise, discard this invisible + /// RGB information for better compression. + /// The default value is Clear. + /// + public WebpTransparentColorMode TransparentColorMode { get; init; } = WebpTransparentColorMode.Clear; - /// - public bool NearLossless { get; set; } + /// + /// Gets a value indicating whether near lossless mode should be used. + /// This option adjusts pixel values to help compressibility, but has minimal impact on the visual quality. + /// + public bool NearLossless { get; init; } - /// - public int NearLosslessQuality { get; set; } = 100; + /// + /// Gets the quality of near-lossless image preprocessing. The range is 0 (maximum preprocessing) to 100 (no preprocessing, the default). + /// The typical value is around 60. Note that lossy with -q 100 can at times yield better results. + /// + public int NearLosslessQuality { get; init; } = 100; /// - public void Encode(Image image, Stream stream) - where TPixel : unmanaged, IPixel + public override void Encode(Image image, Stream stream) { - var encoder = new WebpEncoderCore(this, image.GetMemoryAllocator()); + WebpEncoderCore encoder = new(this, image.GetMemoryAllocator()); encoder.Encode(image, stream); } /// - public Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken) - where TPixel : unmanaged, IPixel + public override Task EncodeAsync(Image image, Stream stream, CancellationToken cancellationToken) { - var encoder = new WebpEncoderCore(this, image.GetMemoryAllocator()); + WebpEncoderCore encoder = new(this, image.GetMemoryAllocator()); return encoder.EncodeAsync(image, stream, cancellationToken); } } diff --git a/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs b/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs index f9ceaf3098..e8ee316d88 100644 --- a/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs +++ b/src/ImageSharp/Formats/Webp/WebpEncoderCore.cs @@ -56,6 +56,11 @@ internal sealed class WebpEncoderCore : IImageEncoderInternals /// private readonly WebpTransparentColorMode transparentColorMode; + /// + /// Whether to skip metadata during encoding. + /// + private readonly bool skipMetadata; + /// /// Indicating whether near lossless mode should be used. /// @@ -80,21 +85,22 @@ internal sealed class WebpEncoderCore : IImageEncoderInternals /// /// Initializes a new instance of the class. /// - /// The encoder options. + /// The encoder with options. /// The memory manager. - public WebpEncoderCore(IWebpEncoderOptions options, MemoryAllocator memoryAllocator) + public WebpEncoderCore(WebpEncoder encoder, MemoryAllocator memoryAllocator) { this.memoryAllocator = memoryAllocator; - this.alphaCompression = options.UseAlphaCompression; - this.fileFormat = options.FileFormat; - this.quality = options.Quality; - this.method = options.Method; - this.entropyPasses = options.EntropyPasses; - this.spatialNoiseShaping = options.SpatialNoiseShaping; - this.filterStrength = options.FilterStrength; - this.transparentColorMode = options.TransparentColorMode; - this.nearLossless = options.NearLossless; - this.nearLosslessQuality = options.NearLosslessQuality; + this.alphaCompression = encoder.UseAlphaCompression; + this.fileFormat = encoder.FileFormat; + this.quality = encoder.Quality; + this.method = encoder.Method; + this.entropyPasses = encoder.EntropyPasses; + this.spatialNoiseShaping = encoder.SpatialNoiseShaping; + this.filterStrength = encoder.FilterStrength; + this.transparentColorMode = encoder.TransparentColorMode; + this.skipMetadata = encoder.SkipMetadata; + this.nearLossless = encoder.NearLossless; + this.nearLosslessQuality = encoder.NearLosslessQuality; } /// @@ -124,12 +130,13 @@ internal sealed class WebpEncoderCore : IImageEncoderInternals if (lossless) { - using var enc = new Vp8LEncoder( + using Vp8LEncoder enc = new( this.memoryAllocator, this.configuration, image.Width, image.Height, this.quality, + this.skipMetadata, this.method, this.transparentColorMode, this.nearLossless, @@ -138,12 +145,13 @@ internal sealed class WebpEncoderCore : IImageEncoderInternals } else { - using var enc = new Vp8Encoder( + using Vp8Encoder enc = new( this.memoryAllocator, this.configuration, image.Width, image.Height, this.quality, + this.skipMetadata, this.method, this.entropyPasses, this.filterStrength,