Browse Source

Convert WebpEncoder

pull/2269/head
James Jackson-South 4 years ago
parent
commit
2fc9719a62
  1. 12
      src/ImageSharp/Formats/Webp/AlphaEncoder.cs
  2. 79
      src/ImageSharp/Formats/Webp/IWebpEncoderOptions.cs
  3. 15
      src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs
  4. 20
      src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs
  5. 92
      src/ImageSharp/Formats/Webp/WebpEncoder.cs
  6. 36
      src/ImageSharp/Formats/Webp/WebpEncoderCore.cs

12
src/ImageSharp/Formats/Webp/AlphaEncoder.cs

@ -24,10 +24,11 @@ internal class AlphaEncoder : IDisposable
/// <param name="image">The <see cref="ImageFrame{TPixel}"/> to encode from.</param>
/// <param name="configuration">The global configuration.</param>
/// <param name="memoryAllocator">The memory manager.</param>
/// <param name="skipMetadata">Whether to skip metadata encoding.</param>
/// <param name="compress">Indicates, if the data should be compressed with the lossless webp compression.</param>
/// <param name="size">The size in bytes of the alpha data.</param>
/// <returns>The encoded alpha data.</returns>
public IMemoryOwner<byte> EncodeAlpha<TPixel>(Image<TPixel> image, Configuration configuration, MemoryAllocator memoryAllocator, bool compress, out int size)
public IMemoryOwner<byte> EncodeAlpha<TPixel>(Image<TPixel> image, Configuration configuration, MemoryAllocator memoryAllocator, bool skipMetadata, bool compress, out int size)
where TPixel : unmanaged, IPixel<TPixel>
{
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<Rgba32>(width, height);
Image<Rgba32> alphaAsImage = new(width, height);
for (int y = 0; y < height; y++)
{

79
src/ImageSharp/Formats/Webp/IWebpEncoderOptions.cs

@ -1,79 +0,0 @@
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
namespace SixLabors.ImageSharp.Formats.Webp;
/// <summary>
/// Configuration options for use during webp encoding.
/// </summary>
internal interface IWebpEncoderOptions
{
/// <summary>
/// Gets the webp file format used. Either lossless or lossy.
/// Defaults to lossy.
/// </summary>
WebpFileFormatType? FileFormat { get; }
/// <summary>
/// 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.
/// </summary>
int Quality { get; }
/// <summary>
/// Gets the encoding method to use. Its a quality/speed trade-off (0=fast, 6=slower-better).
/// Defaults to 4.
/// </summary>
WebpEncodingMethod Method { get; }
/// <summary>
/// Gets a value indicating whether the alpha plane should be compressed with Webp lossless format.
/// Defaults to true.
/// </summary>
bool UseAlphaCompression { get; }
/// <summary>
/// Gets the number of entropy-analysis passes (in [1..10]).
/// Defaults to 1.
/// </summary>
int EntropyPasses { get; }
/// <summary>
/// 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.
/// </summary>
int SpatialNoiseShaping { get; }
/// <summary>
/// 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.
/// </summary>
int FilterStrength { get; }
/// <summary>
/// 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.
/// </summary>
WebpTransparentColorMode TransparentColorMode { get; }
/// <summary>
/// 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.
/// </summary>
bool NearLossless { get; }
/// <summary>
/// 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.
/// </summary>
int NearLosslessQuality { get; }
}

15
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
/// </summary>
private readonly WebpTransparentColorMode transparentColorMode;
/// <summary>
/// Whether to skip metadata during encoding.
/// </summary>
private readonly bool skipMetadata;
/// <summary>
/// Indicating whether near lossless mode should be used.
/// </summary>
@ -91,6 +98,7 @@ internal class Vp8LEncoder : IDisposable
/// <param name="width">The width of the input image.</param>
/// <param name="height">The height of the input image.</param>
/// <param name="quality">The encoding quality.</param>
/// <param name="skipMetadata">Whether to skip metadata encoding.</param>
/// <param name="method">Quality/speed trade-off (0=fast, 6=slower-better).</param>
/// <param name="transparentColorMode">Flag indicating whether to preserve the exact RGB values under transparent area.
/// Otherwise, discard this invisible RGB information for better compression.</param>
@ -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);
}
/// <summary>

20
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
/// </summary>
private Vp8BitWriter bitWriter;
/// <summary>
/// Whether to skip metadata during encoding.
/// </summary>
private readonly bool skipMetadata;
private readonly Vp8RdLevel rdOptLevel;
private int maxI4HeaderBits;
@ -94,6 +101,7 @@ internal class Vp8Encoder : IDisposable
/// <param name="width">The width of the input image.</param>
/// <param name="height">The height of the input image.</param>
/// <param name="quality">The encoding quality.</param>
/// <param name="skipMetadata">Whether to skip metadata encoding.</param>
/// <param name="method">Quality/speed trade-off (0=fast, 6=slower-better).</param>
/// <param name="entropyPasses">Number of entropy-analysis passes (in [1..10]).</param>
/// <param name="filterStrength">The filter the strength of the deblocking filter, between 0 (no filtering) and 100 (maximum filtering).</param>
@ -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<byte> encodedAlphaData = alphaEncoder.EncodeAlpha(image, this.configuration, this.memoryAllocator, this.alphaCompression, out alphaDataSize);
IMemoryOwner<byte> 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,

92
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;
/// <summary>
/// Image encoder for writing an image to a stream in the Webp format.
/// </summary>
public sealed class WebpEncoder : IImageEncoder, IWebpEncoderOptions
public sealed class WebpEncoder : ImageEncoder
{
/// <inheritdoc/>
public WebpFileFormatType? FileFormat { get; set; }
/// <summary>
/// Gets the webp file format used. Either lossless or lossy.
/// Defaults to lossy.
/// </summary>
public WebpFileFormatType? FileFormat { get; init; }
/// <inheritdoc/>
public int Quality { get; set; } = 75;
/// <summary>
/// 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.
/// </summary>
public int Quality { get; init; } = 75;
/// <inheritdoc/>
public WebpEncodingMethod Method { get; set; } = WebpEncodingMethod.Default;
/// <summary>
/// Gets the encoding method to use. Its a quality/speed trade-off (0=fast, 6=slower-better).
/// Defaults to 4.
/// </summary>
public WebpEncodingMethod Method { get; init; } = WebpEncodingMethod.Default;
/// <inheritdoc/>
public bool UseAlphaCompression { get; set; } = true;
/// <summary>
/// Gets a value indicating whether the alpha plane should be compressed with Webp lossless format.
/// Defaults to true.
/// </summary>
public bool UseAlphaCompression { get; init; } = true;
/// <inheritdoc/>
public int EntropyPasses { get; set; } = 1;
/// <summary>
/// Gets the number of entropy-analysis passes (in [1..10]).
/// Defaults to 1.
/// </summary>
public int EntropyPasses { get; init; } = 1;
/// <inheritdoc/>
public int SpatialNoiseShaping { get; set; } = 50;
/// <summary>
/// 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.
/// </summary>
public int SpatialNoiseShaping { get; init; } = 50;
/// <inheritdoc/>
public int FilterStrength { get; set; } = 60;
/// <summary>
/// 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.
/// </summary>
public int FilterStrength { get; init; } = 60;
/// <inheritdoc/>
public WebpTransparentColorMode TransparentColorMode { get; set; } = WebpTransparentColorMode.Clear;
/// <summary>
/// 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.
/// </summary>
public WebpTransparentColorMode TransparentColorMode { get; init; } = WebpTransparentColorMode.Clear;
/// <inheritdoc/>
public bool NearLossless { get; set; }
/// <summary>
/// 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.
/// </summary>
public bool NearLossless { get; init; }
/// <inheritdoc/>
public int NearLosslessQuality { get; set; } = 100;
/// <summary>
/// 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.
/// </summary>
public int NearLosslessQuality { get; init; } = 100;
/// <inheritdoc/>
public void Encode<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>
public override void Encode<TPixel>(Image<TPixel> image, Stream stream)
{
var encoder = new WebpEncoderCore(this, image.GetMemoryAllocator());
WebpEncoderCore encoder = new(this, image.GetMemoryAllocator());
encoder.Encode(image, stream);
}
/// <inheritdoc/>
public Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
where TPixel : unmanaged, IPixel<TPixel>
public override Task EncodeAsync<TPixel>(Image<TPixel> image, Stream stream, CancellationToken cancellationToken)
{
var encoder = new WebpEncoderCore(this, image.GetMemoryAllocator());
WebpEncoderCore encoder = new(this, image.GetMemoryAllocator());
return encoder.EncodeAsync(image, stream, cancellationToken);
}
}

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

@ -56,6 +56,11 @@ internal sealed class WebpEncoderCore : IImageEncoderInternals
/// </summary>
private readonly WebpTransparentColorMode transparentColorMode;
/// <summary>
/// Whether to skip metadata during encoding.
/// </summary>
private readonly bool skipMetadata;
/// <summary>
/// Indicating whether near lossless mode should be used.
/// </summary>
@ -80,21 +85,22 @@ internal sealed class WebpEncoderCore : IImageEncoderInternals
/// <summary>
/// Initializes a new instance of the <see cref="WebpEncoderCore"/> class.
/// </summary>
/// <param name="options">The encoder options.</param>
/// <param name="encoder">The encoder with options.</param>
/// <param name="memoryAllocator">The memory manager.</param>
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;
}
/// <summary>
@ -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,

Loading…
Cancel
Save