Browse Source

Add enum for webp encoding method

pull/1552/head
Brian Popow 4 years ago
parent
commit
a41631efa3
  1. 2
      src/ImageSharp/Formats/Webp/IWebpEncoderOptions.cs
  2. 27
      src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs
  3. 2
      src/ImageSharp/Formats/Webp/Lossy/Vp8EncIterator.cs
  4. 37
      src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs
  5. 2
      src/ImageSharp/Formats/Webp/WebpEncoder.cs
  6. 2
      src/ImageSharp/Formats/Webp/WebpEncoderCore.cs
  7. 61
      src/ImageSharp/Formats/Webp/WebpEncodingMethod.cs
  8. 8
      tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs

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

@ -27,7 +27,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
/// Gets the encoding method to use. Its a quality/speed trade-off (0=fast, 6=slower-better).
/// Defaults to 4.
/// </summary>
int Method { get; }
WebpEncodingMethod Method { get; }
/// <summary>
/// Gets a value indicating whether the alpha plane should be compressed with Webp lossless format.

27
src/ImageSharp/Formats/Webp/Lossless/Vp8LEncoder.cs

@ -52,7 +52,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
/// <summary>
/// Quality/speed trade-off (0=fast, 6=slower-better).
/// </summary>
private readonly int method;
private readonly WebpEncodingMethod method;
/// <summary>
/// Flag indicating whether to preserve the exact RGB values under transparent area. Otherwise, discard this invisible
@ -88,7 +88,16 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
/// <param name="exact">Flag indicating whether to preserve the exact RGB values under transparent area. Otherwise, discard this invisible RGB information for better compression.</param>
/// <param name="nearLossless">Indicating whether near lossless mode should be used.</param>
/// <param name="nearLosslessQuality">The near lossless quality. The range is 0 (maximum preprocessing) to 100 (no preprocessing, the default).</param>
public Vp8LEncoder(MemoryAllocator memoryAllocator, Configuration configuration, int width, int height, int quality, int method, bool exact, bool nearLossless, int nearLosslessQuality)
public Vp8LEncoder(
MemoryAllocator memoryAllocator,
Configuration configuration,
int width,
int height,
int quality,
WebpEncodingMethod method,
bool exact,
bool nearLossless,
int nearLosslessQuality)
{
int pixelCount = width * height;
int initialSize = pixelCount * 2;
@ -96,7 +105,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
this.memoryAllocator = memoryAllocator;
this.configuration = configuration;
this.quality = Numerics.Clamp(quality, 0, 100);
this.method = Numerics.Clamp(method, 0, 6);
this.method = (WebpEncodingMethod)Numerics.Clamp((int)method, 0, 6);
this.exact = exact;
this.nearLossless = nearLossless;
this.nearLosslessQuality = Numerics.Clamp(nearLosslessQuality, 0, 100);
@ -424,7 +433,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
bool doNotCache = false;
var crunchConfigs = new List<CrunchConfig>();
if (this.method == 6 && this.quality == 100)
if (this.method == WebpEncodingMethod.BestQuality && this.quality == 100)
{
doNotCache = true;
@ -442,7 +451,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
{
// Only choose the guessed best transform.
crunchConfigs.Add(new CrunchConfig { EntropyIdx = entropyIdx });
if (this.quality >= 75 && this.method == 5)
if (this.quality >= 75 && this.method == WebpEncodingMethod.Level5)
{
// Test with and without color cache.
doNotCache = true;
@ -1615,10 +1624,10 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
/// <summary>
/// Calculates the huffman image bits.
/// </summary>
private static int GetHistoBits(int method, bool usePalette, int width, int height)
private static int GetHistoBits(WebpEncodingMethod method, bool usePalette, int width, int height)
{
// Make tile size a function of encoding method (Range: 0 to 6).
int histoBits = (usePalette ? 9 : 7) - method;
int histoBits = (usePalette ? 9 : 7) - (int)method;
while (true)
{
int huffImageSize = LosslessUtils.SubSampleSize(width, histoBits) * LosslessUtils.SubSampleSize(height, histoBits);
@ -1678,9 +1687,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
/// Calculates the bits used for the transformation.
/// </summary>
[MethodImpl(InliningOptions.ShortMethod)]
private static int GetTransformBits(int method, int histoBits)
private static int GetTransformBits(WebpEncodingMethod method, int histoBits)
{
int maxTransformBits = method < 4 ? 6 : method > 4 ? 4 : 5;
int maxTransformBits = (int)method < 4 ? 6 : (int)method > 4 ? 4 : 5;
int res = histoBits > maxTransformBits ? maxTransformBits : histoBits;
return res;
}

2
src/ImageSharp/Formats/Webp/Lossy/Vp8EncIterator.cs

@ -432,7 +432,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
}
while (this.RotateI4(this.YuvIn.AsSpan(YOffEnc))); // Note: we reuse the original samples for predictors.
var i4Alpha = totalHisto.GetAlpha();
int i4Alpha = totalHisto.GetAlpha();
if (i4Alpha > bestAlpha)
{
this.SetIntra4Mode(modes);

37
src/ImageSharp/Formats/Webp/Lossy/Vp8Encoder.cs

@ -34,7 +34,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
/// <summary>
/// Quality/speed trade-off (0=fast, 6=slower-better).
/// </summary>
private readonly int method;
private readonly WebpEncodingMethod method;
/// <summary>
/// Number of entropy-analysis passes (in [1..10]).
@ -99,20 +99,29 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
/// <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>
/// <param name="spatialNoiseShaping">The spatial noise shaping. 0=off, 100=maximum.</param>
public Vp8Encoder(MemoryAllocator memoryAllocator, Configuration configuration, int width, int height, int quality, int method, int entropyPasses, int filterStrength, int spatialNoiseShaping)
public Vp8Encoder(
MemoryAllocator memoryAllocator,
Configuration configuration,
int width,
int height,
int quality,
WebpEncodingMethod method,
int entropyPasses,
int filterStrength,
int spatialNoiseShaping)
{
this.memoryAllocator = memoryAllocator;
this.configuration = configuration;
this.Width = width;
this.Height = height;
this.quality = Numerics.Clamp(quality, 0, 100);
this.method = Numerics.Clamp(method, 0, 6);
this.method = (WebpEncodingMethod)Numerics.Clamp((int)method, 0, 6);
this.entropyPasses = Numerics.Clamp(entropyPasses, 1, 10);
this.filterStrength = Numerics.Clamp(filterStrength, 0, 100);
this.spatialNoiseShaping = Numerics.Clamp(spatialNoiseShaping, 0, 100);
this.rdOptLevel = method >= 6 ? Vp8RdLevel.RdOptTrellisAll
: method >= 5 ? Vp8RdLevel.RdOptTrellis
: method >= 3 ? Vp8RdLevel.RdOptBasic
this.rdOptLevel = method is WebpEncodingMethod.BestQuality ? Vp8RdLevel.RdOptTrellisAll
: (int)method >= 5 ? Vp8RdLevel.RdOptTrellis
: (int)method >= 3 ? Vp8RdLevel.RdOptBasic
: Vp8RdLevel.RdOptNone;
int pixelCount = width * height;
@ -360,9 +369,9 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
int targetSize = 0; // TODO: target size is hardcoded.
float targetPsnr = 0.0f; // TODO: targetPsnr is hardcoded.
bool doSearch = targetSize > 0 || targetPsnr > 0;
bool fastProbe = (this.method == 0 || this.method == 3) && !doSearch;
bool fastProbe = (this.method == 0 || this.method == WebpEncodingMethod.Level3) && !doSearch;
int numPassLeft = this.entropyPasses;
Vp8RdLevel rdOpt = this.method >= 3 || doSearch ? Vp8RdLevel.RdOptBasic : Vp8RdLevel.RdOptNone;
Vp8RdLevel rdOpt = (int)this.method >= 3 || doSearch ? Vp8RdLevel.RdOptBasic : Vp8RdLevel.RdOptNone;
int nbMbs = this.Mbw * this.Mbh;
var stats = new PassStats(targetSize, targetPsnr, QMin, QMax, this.quality);
@ -371,7 +380,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// Fast mode: quick analysis pass over few mbs. Better than nothing.
if (fastProbe)
{
if (this.method == 3)
if (this.method == WebpEncodingMethod.Level3)
{
// We need more stats for method 3 to be reliable.
nbMbs = nbMbs > 200 ? nbMbs >> 1 : 100;
@ -790,7 +799,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
private void SetupMatrices(Vp8SegmentInfo[] dqm)
{
int tlambdaScale = (this.method >= 4) ? this.spatialNoiseShaping : 0;
int tlambdaScale = (int)this.method >= 4 ? this.spatialNoiseShaping : 0;
for (int i = 0; i < dqm.Length; i++)
{
Vp8SegmentInfo m = dqm[i];
@ -861,14 +870,14 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
it.SetSegment(0); // default segment, spec-wise.
int bestAlpha;
if (this.method <= 1)
if ((int)this.method <= 1)
{
bestAlpha = it.FastMbAnalyze(this.quality);
}
else
{
bestAlpha = it.MbAnalyzeBestIntra16Mode();
if (this.method >= 5)
if ((int)this.method >= 5)
{
// We go and make a fast decision for intra4/intra16.
// It's usually not a good and definitive pick, but helps seeding the stats about level bit-cost.
@ -899,7 +908,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
if (rdOpt > Vp8RdLevel.RdOptNone)
{
QuantEnc.PickBestIntra16(it, ref rd, this.SegmentInfos, this.Proba);
if (this.method >= 2)
if ((int)this.method >= 2)
{
QuantEnc.PickBestIntra4(it, ref rd, this.SegmentInfos, this.Proba, this.maxI4HeaderBits);
}
@ -912,7 +921,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
// For method >= 2, pick the best intra4/intra16 based on SSE (~tad slower).
// For method <= 1, we don't re-examine the decision but just go ahead with
// quantization/reconstruction.
QuantEnc.RefineUsingDistortion(it, this.SegmentInfos, rd, this.method >= 2, this.method >= 1, this.MbHeaderLimit);
QuantEnc.RefineUsingDistortion(it, this.SegmentInfos, rd, (int)this.method >= 2, (int)this.method >= 1, this.MbHeaderLimit);
}
bool isSkipped = rd.Nz == 0;

2
src/ImageSharp/Formats/Webp/WebpEncoder.cs

@ -21,7 +21,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
public int Quality { get; set; } = 75;
/// <inheritdoc/>
public int Method { get; set; } = 4;
public WebpEncodingMethod Method { get; set; } = WebpEncodingMethod.Default;
/// <inheritdoc/>
public bool UseAlphaCompression { get; set; }

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

@ -40,7 +40,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
/// <summary>
/// Quality/speed trade-off (0=fast, 6=slower-better).
/// </summary>
private readonly int method;
private readonly WebpEncodingMethod method;
/// <summary>
/// The number of entropy-analysis passes (in [1..10]).

61
src/ImageSharp/Formats/Webp/WebpEncodingMethod.cs

@ -0,0 +1,61 @@
// Copyright (c) Six Labors.
// Licensed under the Apache License, Version 2.0.
namespace SixLabors.ImageSharp.Formats.Webp
{
/// <summary>
/// Quality/speed trade-off for the encoding process (0=fast, 6=slower-better).
/// </summary>
public enum WebpEncodingMethod
{
/// <summary>
/// Fastest, but quality compromise. Equivalent to <see cref="Fastest"/>.
/// </summary>
Level0 = 0,
/// <summary>
/// Fastest, but quality compromise.
/// </summary>
Fastest = Level0,
/// <summary>
/// Level1.
/// </summary>
Level1 = 1,
/// <summary>
/// Level 2.
/// </summary>
Level2 = 2,
/// <summary>
/// Level 3.
/// </summary>
Level3 = 3,
/// <summary>
/// Level 4. Equivalent to <see cref="Default"/>.
/// </summary>
Level4 = 4,
/// <summary>
/// BestQuality trade off between speed and quality.
/// </summary>
Default = Level4,
/// <summary>
/// Level 5.
/// </summary>
Level5 = 5,
/// <summary>
/// Slowest option, but best quality. Equivalent to <see cref="BestQuality"/>.
/// </summary>
Level6 = 6,
/// <summary>
/// Slowest option, but best quality.
/// </summary>
BestQuality = Level6
}
}

8
tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs

@ -25,7 +25,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
{
Lossy = false,
Quality = 100,
Method = 6
Method = WebpEncodingMethod.BestQuality
};
using Image<TPixel> image = provider.GetImage();
@ -65,7 +65,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
[WithFile(Lossy.NoFilter06, PixelTypes.Rgba32, 4, 100)]
[WithFile(Lossy.NoFilter06, PixelTypes.Rgba32, 5, 100)]
[WithFile(Lossy.NoFilter06, PixelTypes.Rgba32, 6, 100)]
public void Encode_Lossless_WithDifferentMethodAndQuality_Works<TPixel>(TestImageProvider<TPixel> provider, int method, int quality)
public void Encode_Lossless_WithDifferentMethodAndQuality_Works<TPixel>(TestImageProvider<TPixel> provider, WebpEncodingMethod method, int quality)
where TPixel : unmanaged, IPixel<TPixel>
{
var encoder = new WebpEncoder()
@ -111,7 +111,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
[WithFile(Lossy.NoFilter06, PixelTypes.Rgba32, 5)]
[WithFile(Lossy.NoFilter06, PixelTypes.Rgba32, 6)]
[WithFile(Lossy.Alpha1, PixelTypes.Rgba32, 4)]
public void Encode_Lossless_WithExactFlag_Works<TPixel>(TestImageProvider<TPixel> provider, int method)
public void Encode_Lossless_WithExactFlag_Works<TPixel>(TestImageProvider<TPixel> provider, WebpEncodingMethod method)
where TPixel : unmanaged, IPixel<TPixel>
{
var encoder = new WebpEncoder()
@ -223,7 +223,7 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
[WithFile(Lossy.NoFilter06, PixelTypes.Rgba32, 4, 100)]
[WithFile(Lossy.NoFilter06, PixelTypes.Rgba32, 5, 100)]
[WithFile(Lossy.NoFilter06, PixelTypes.Rgba32, 6, 100)]
public void Encode_Lossy_WithDifferentMethodsAndQuality_Works<TPixel>(TestImageProvider<TPixel> provider, int method, int quality)
public void Encode_Lossy_WithDifferentMethodsAndQuality_Works<TPixel>(TestImageProvider<TPixel> provider, WebpEncodingMethod method, int quality)
where TPixel : unmanaged, IPixel<TPixel>
{
var encoder = new WebpEncoder()

Loading…
Cancel
Save