diff --git a/src/ImageSharp/Formats/WebP/IWebpEncoderOptions.cs b/src/ImageSharp/Formats/WebP/IWebpEncoderOptions.cs
index 2dbd54478b..dad67f8041 100644
--- a/src/ImageSharp/Formats/WebP/IWebpEncoderOptions.cs
+++ b/src/ImageSharp/Formats/WebP/IWebpEncoderOptions.cs
@@ -39,6 +39,15 @@ namespace SixLabors.ImageSharp.Formats.Webp
///
int EntropyPasses { 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.
diff --git a/src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs b/src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs
index 83f44e1ee1..ee3f1a8a5b 100644
--- a/src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs
+++ b/src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs
@@ -42,6 +42,11 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
///
private readonly int entropyPasses;
+ ///
+ /// Specify the strength of the deblocking filter, between 0 (no filtering) and 100 (maximum filtering). A value of 0 will turn off any filtering.
+ ///
+ private readonly int filterStrength;
+
///
/// A bit writer for writing lossy webp streams.
///
@@ -78,11 +83,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
private const int QMax = 100;
- // TODO: filterStrength is hardcoded, should be configurable.
- private const int FilterStrength = 60;
-
///
- /// Initializes a new instance of the class.
+ /// Initializes a new instance of the class.
///
/// The memory allocator.
/// The global configuration.
@@ -91,7 +93,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
/// The encoding quality.
/// Quality/speed trade-off (0=fast, 6=slower-better).
/// Number of entropy-analysis passes (in [1..10]).
- public Vp8Encoder(MemoryAllocator memoryAllocator, Configuration configuration, int width, int height, int quality, int method, int entropyPasses)
+ /// The filter the strength of the deblocking filter, between 0 (no filtering) and 100 (maximum filtering).
+ public Vp8Encoder(MemoryAllocator memoryAllocator, Configuration configuration, int width, int height, int quality, int method, int entropyPasses, int filterStrength)
{
this.memoryAllocator = memoryAllocator;
this.configuration = configuration;
@@ -100,6 +103,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
this.quality = Numerics.Clamp(quality, 0, 100);
this.method = Numerics.Clamp(method, 0, 6);
this.entropyPasses = Numerics.Clamp(entropyPasses, 1, 10);
+ this.filterStrength = Numerics.Clamp(filterStrength, 0, 100);
this.rdOptLevel = (method >= 6) ? Vp8RdLevel.RdOptTrellisAll
: (method >= 5) ? Vp8RdLevel.RdOptTrellis
: (method >= 3) ? Vp8RdLevel.RdOptBasic
@@ -476,7 +480,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
private void AdjustFilterStrength()
{
- if (FilterStrength > 0)
+ if (this.filterStrength > 0)
{
int maxLevel = 0;
for (int s = 0; s < WebpConstants.NumMbSegments; s++)
@@ -708,7 +712,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy
int filterType = 1; // TODO: filterType is hardcoded
// level0 is in [0..500]. Using '-f 50' as filter_strength is mid-filtering.
- int level0 = 5 * FilterStrength;
+ int level0 = 5 * this.filterStrength;
for (int i = 0; i < WebpConstants.NumMbSegments; ++i)
{
Vp8SegmentInfo m = this.SegmentInfos[i];
diff --git a/src/ImageSharp/Formats/WebP/WebpEncoder.cs b/src/ImageSharp/Formats/WebP/WebpEncoder.cs
index 225938d2b6..ae0fb51228 100644
--- a/src/ImageSharp/Formats/WebP/WebpEncoder.cs
+++ b/src/ImageSharp/Formats/WebP/WebpEncoder.cs
@@ -29,6 +29,9 @@ namespace SixLabors.ImageSharp.Formats.Webp
///
public int EntropyPasses { get; set; }
+ ///
+ public int FilterStrength { get; set; } = 60;
+
///
public bool Exact { get; set; }
diff --git a/src/ImageSharp/Formats/WebP/WebpEncoderCore.cs b/src/ImageSharp/Formats/WebP/WebpEncoderCore.cs
index 8d7da5a175..09a319de84 100644
--- a/src/ImageSharp/Formats/WebP/WebpEncoderCore.cs
+++ b/src/ImageSharp/Formats/WebP/WebpEncoderCore.cs
@@ -47,6 +47,11 @@ namespace SixLabors.ImageSharp.Formats.Webp
///
private readonly int entropyPasses;
+ ///
+ /// The filter the strength of the deblocking filter, between 0 (no filtering) and 100 (maximum filtering).
+ ///
+ private readonly int filterStrength;
+
///
/// Flag indicating whether to preserve the exact RGB values under transparent area. Otherwise, discard this invisible
/// RGB information for better compression.
@@ -71,6 +76,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
this.quality = options.Quality;
this.method = options.Method;
this.entropyPasses = options.EntropyPasses;
+ this.filterStrength = options.FilterStrength;
this.exact = options.Exact;
}
@@ -91,7 +97,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
if (this.lossy)
{
- using var enc = new Vp8Encoder(this.memoryAllocator, this.configuration, image.Width, image.Height, this.quality, this.method, this.entropyPasses);
+ using var enc = new Vp8Encoder(this.memoryAllocator, this.configuration, image.Width, image.Height, this.quality, this.method, this.entropyPasses, this.filterStrength);
enc.Encode(image, stream);
}
else
diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs
index 8de790b982..4d9ff2cfbf 100644
--- a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs
@@ -98,6 +98,26 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
image.VerifyEncoder(provider, "webp", testOutputDetails, encoder, customComparer: GetComparer(quality));
}
+ [Theory]
+ [WithFile(Lossy.NoFilter06, PixelTypes.Rgba32, 100)]
+ [WithFile(Lossy.NoFilter06, PixelTypes.Rgba32, 80)]
+ [WithFile(Lossy.NoFilter06, PixelTypes.Rgba32, 50)]
+ [WithFile(Lossy.NoFilter06, PixelTypes.Rgba32, 30)]
+ [WithFile(Lossy.NoFilter06, PixelTypes.Rgba32, 10)]
+ public void Encode_Lossy_WithDifferentFilterStrength_Works(TestImageProvider provider, int filterStrength)
+ where TPixel : unmanaged, IPixel
+ {
+ var encoder = new WebpEncoder()
+ {
+ Lossy = true,
+ FilterStrength = filterStrength
+ };
+
+ using Image image = provider.GetImage();
+ string testOutputDetails = string.Concat("lossy", "_f", filterStrength);
+ image.VerifyEncoder(provider, "webp", testOutputDetails, encoder, customComparer: GetComparer(75));
+ }
+
[Theory]
[WithFile(Lossy.NoFilter06, PixelTypes.Rgba32, 0, 75)]
[WithFile(Lossy.NoFilter06, PixelTypes.Rgba32, 1, 75)]