diff --git a/src/ImageSharp/Formats/WebP/IWebpEncoderOptions.cs b/src/ImageSharp/Formats/WebP/IWebpEncoderOptions.cs
index 2d8a7fdeb..2dbd54478 100644
--- a/src/ImageSharp/Formats/WebP/IWebpEncoderOptions.cs
+++ b/src/ImageSharp/Formats/WebP/IWebpEncoderOptions.cs
@@ -38,5 +38,12 @@ namespace SixLabors.ImageSharp.Formats.Webp
/// Gets the number of entropy-analysis passes (in [1..10]).
///
int EntropyPasses { 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 false.
+ ///
+ bool Exact { get; }
}
}
diff --git a/src/ImageSharp/Formats/WebP/Lossless/PredictorEncoder.cs b/src/ImageSharp/Formats/WebP/Lossless/PredictorEncoder.cs
index 97d96e713..9f666ff6a 100644
--- a/src/ImageSharp/Formats/WebP/Lossless/PredictorEncoder.cs
+++ b/src/ImageSharp/Formats/WebP/Lossless/PredictorEncoder.cs
@@ -641,7 +641,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
switch (mode)
{
case 0:
- LosslessUtils.PredictorSub0(current, numPixels, output);
+ LosslessUtils.PredictorSub0(current + xStart, numPixels, output);
break;
case 1:
LosslessUtils.PredictorSub1(current + xStart, numPixels, output);
diff --git a/src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs b/src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs
index dab106524..d6a5de07a 100644
--- a/src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs
+++ b/src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs
@@ -54,6 +54,12 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
///
private readonly int method;
+ ///
+ /// Flag indicating whether to preserve the exact RGB values under transparent area. Otherwise, discard this invisible
+ /// RGB information for better compression.
+ ///
+ private readonly bool exact;
+
private const int ApplyPaletteGreedyMax = 4;
private const int PaletteInvSizeBits = 11;
@@ -69,7 +75,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
/// The height of the input image.
/// The encoding quality.
/// Quality/speed trade-off (0=fast, 6=slower-better).
- public Vp8LEncoder(MemoryAllocator memoryAllocator, Configuration configuration, int width, int height, int quality, int method)
+ /// Flag indicating whether to preserve the exact RGB values under transparent area. Otherwise, discard this invisible RGB information for better compression.
+ public Vp8LEncoder(MemoryAllocator memoryAllocator, Configuration configuration, int width, int height, int quality, int method, bool exact)
{
int pixelCount = width * height;
int initialSize = pixelCount * 2;
@@ -78,6 +85,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
this.configuration = configuration;
this.quality = Numerics.Clamp(quality, 0, 100);
this.method = Numerics.Clamp(method, 0, 6);
+ this.exact = exact;
this.bitWriter = new Vp8LBitWriter(initialSize);
this.Bgra = memoryAllocator.Allocate(pixelCount);
this.EncodedData = memoryAllocator.Allocate(pixelCount);
@@ -603,12 +611,20 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
private void ApplyPredictFilter(int width, int height, bool usedSubtractGreen)
{
int nearLosslessStrength = 100; // TODO: for now always 100
- bool exact = false; // TODO: always false for now.
int predBits = this.TransformBits;
int transformWidth = LosslessUtils.SubSampleSize(width, predBits);
int transformHeight = LosslessUtils.SubSampleSize(height, predBits);
- PredictorEncoder.ResidualImage(width, height, predBits, this.EncodedData.GetSpan(), this.BgraScratch.GetSpan(), this.TransformData.GetSpan(), nearLosslessStrength, exact, usedSubtractGreen);
+ PredictorEncoder.ResidualImage(
+ width,
+ height,
+ predBits,
+ this.EncodedData.GetSpan(),
+ this.BgraScratch.GetSpan(),
+ this.TransformData.GetSpan(),
+ nearLosslessStrength,
+ this.exact,
+ usedSubtractGreen);
this.bitWriter.PutBits(WebpConstants.TransformPresent, 1);
this.bitWriter.PutBits((uint)Vp8LTransformType.PredictorTransform, 2);
diff --git a/src/ImageSharp/Formats/WebP/WebpEncoder.cs b/src/ImageSharp/Formats/WebP/WebpEncoder.cs
index dc01840da..225938d2b 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 bool Exact { get; set; }
+
///
public void Encode(Image image, Stream stream)
where TPixel : unmanaged, IPixel
diff --git a/src/ImageSharp/Formats/WebP/WebpEncoderCore.cs b/src/ImageSharp/Formats/WebP/WebpEncoderCore.cs
index 9fe477d0c..8d7da5a17 100644
--- a/src/ImageSharp/Formats/WebP/WebpEncoderCore.cs
+++ b/src/ImageSharp/Formats/WebP/WebpEncoderCore.cs
@@ -47,6 +47,12 @@ namespace SixLabors.ImageSharp.Formats.Webp
///
private readonly int entropyPasses;
+ ///
+ /// Flag indicating whether to preserve the exact RGB values under transparent area. Otherwise, discard this invisible
+ /// RGB information for better compression.
+ ///
+ private readonly bool exact;
+
///
/// The global configuration.
///
@@ -65,6 +71,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
this.quality = options.Quality;
this.method = options.Method;
this.entropyPasses = options.EntropyPasses;
+ this.exact = options.Exact;
}
///
@@ -89,7 +96,7 @@ namespace SixLabors.ImageSharp.Formats.Webp
}
else
{
- using var enc = new Vp8LEncoder(this.memoryAllocator, this.configuration, image.Width, image.Height, this.quality, this.method);
+ using var enc = new Vp8LEncoder(this.memoryAllocator, this.configuration, image.Width, image.Height, this.quality, this.method, this.exact);
enc.Encode(image, stream);
}
}
diff --git a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs
index 95ea65d2b..44ae674f3 100644
--- a/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs
+++ b/tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs
@@ -64,7 +64,6 @@ 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)]
- [WithFile(TestImages.Png.BikeSmall, PixelTypes.Rgba32, 6, 100)]
public void Encode_Lossless_WithDifferentMethodAndQuality_Works(TestImageProvider provider, int method, int quality)
where TPixel : unmanaged, IPixel
{
@@ -128,6 +127,30 @@ namespace SixLabors.ImageSharp.Tests.Formats.Webp
image.VerifyEncoder(provider, "webp", testOutputDetails, encoder, customComparer: GetComparer(quality));
}
+ [Theory]
+ [WithFile(Lossy.NoFilter06, PixelTypes.Rgba32, 0)]
+ [WithFile(Lossy.NoFilter06, PixelTypes.Rgba32, 1)]
+ [WithFile(Lossy.NoFilter06, PixelTypes.Rgba32, 2)]
+ [WithFile(Lossy.NoFilter06, PixelTypes.Rgba32, 3)]
+ [WithFile(Lossy.NoFilter06, PixelTypes.Rgba32, 4)]
+ [WithFile(Lossy.NoFilter06, PixelTypes.Rgba32, 5)]
+ [WithFile(Lossy.NoFilter06, PixelTypes.Rgba32, 6)]
+ [WithFile(Lossy.Alpha1, PixelTypes.Rgba32, 4)]
+ public void Encode_Lossless_WithExactFlag_Works(TestImageProvider provider, int method)
+ where TPixel : unmanaged, IPixel
+ {
+ var encoder = new WebpEncoder()
+ {
+ Lossy = false,
+ Method = method,
+ Exact = true
+ };
+
+ using Image image = provider.GetImage();
+ string testOutputDetails = string.Concat("lossless", "_m", method);
+ image.VerifyEncoder(provider, "webp", testOutputDetails, encoder);
+ }
+
[Theory]
[WithFile(TestPatternOpaque, PixelTypes.Rgba32)]
[WithFile(TestPatternOpaqueSmall, PixelTypes.Rgba32)]