Browse Source

Add exact flag as a encoder parameter

pull/1552/head
Brian Popow 5 years ago
parent
commit
217c3e6dc7
  1. 7
      src/ImageSharp/Formats/WebP/IWebpEncoderOptions.cs
  2. 2
      src/ImageSharp/Formats/WebP/Lossless/PredictorEncoder.cs
  3. 22
      src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs
  4. 3
      src/ImageSharp/Formats/WebP/WebpEncoder.cs
  5. 9
      src/ImageSharp/Formats/WebP/WebpEncoderCore.cs
  6. 25
      tests/ImageSharp.Tests/Formats/WebP/WebpEncoderTests.cs

7
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]).
/// </summary>
int EntropyPasses { 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 false.
/// </summary>
bool Exact { get; }
}
}

2
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);

22
src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs

@ -54,6 +54,12 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
/// </summary>
private readonly int method;
/// <summary>
/// Flag indicating whether to preserve the exact RGB values under transparent area. Otherwise, discard this invisible
/// RGB information for better compression.
/// </summary>
private readonly bool exact;
private const int ApplyPaletteGreedyMax = 4;
private const int PaletteInvSizeBits = 11;
@ -69,7 +75,8 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless
/// <param name="height">The height of the input image.</param>
/// <param name="quality">The encoding quality.</param>
/// <param name="method">Quality/speed trade-off (0=fast, 6=slower-better).</param>
public Vp8LEncoder(MemoryAllocator memoryAllocator, Configuration configuration, int width, int height, int quality, int method)
/// <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>
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<uint>(pixelCount);
this.EncodedData = memoryAllocator.Allocate<uint>(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);

3
src/ImageSharp/Formats/WebP/WebpEncoder.cs

@ -29,6 +29,9 @@ namespace SixLabors.ImageSharp.Formats.Webp
/// <inheritdoc/>
public int EntropyPasses { get; set; }
/// <inheritdoc/>
public bool Exact { get; set; }
/// <inheritdoc/>
public void Encode<TPixel>(Image<TPixel> image, Stream stream)
where TPixel : unmanaged, IPixel<TPixel>

9
src/ImageSharp/Formats/WebP/WebpEncoderCore.cs

@ -47,6 +47,12 @@ namespace SixLabors.ImageSharp.Formats.Webp
/// </summary>
private readonly int entropyPasses;
/// <summary>
/// Flag indicating whether to preserve the exact RGB values under transparent area. Otherwise, discard this invisible
/// RGB information for better compression.
/// </summary>
private readonly bool exact;
/// <summary>
/// The global configuration.
/// </summary>
@ -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;
}
/// <summary>
@ -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);
}
}

25
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<TPixel>(TestImageProvider<TPixel> provider, int method, int quality)
where TPixel : unmanaged, IPixel<TPixel>
{
@ -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<TPixel>(TestImageProvider<TPixel> provider, int method)
where TPixel : unmanaged, IPixel<TPixel>
{
var encoder = new WebpEncoder()
{
Lossy = false,
Method = method,
Exact = true
};
using Image<TPixel> 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)]

Loading…
Cancel
Save