Browse Source

Use quality parameter

pull/1552/head
Brian Popow 6 years ago
parent
commit
733f83a2a0
  1. 2
      src/ImageSharp/Formats/WebP/IWebPEncoderOptions.cs
  2. 25
      src/ImageSharp/Formats/WebP/Lossless/Vp8LEncoder.cs
  3. 18
      src/ImageSharp/Formats/WebP/MetadataExtensions.cs
  4. 2
      src/ImageSharp/Formats/WebP/WebPEncoder.cs
  5. 4
      src/ImageSharp/Formats/WebP/WebPEncoderCore.cs
  6. 4
      src/ImageSharp/Formats/WebP/WebPFormat.cs
  7. 9
      tests/ImageSharp.Tests/Formats/WebP/WebPEncoderTests.cs

2
src/ImageSharp/Formats/WebP/IWebPEncoderOptions.cs

@ -20,7 +20,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// 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.
/// </summary>
float Quality { get; }
int Quality { get; }
/// <summary>
/// Gets the encoding method to use. Its a quality/speed trade-off (0=fast, 6=slower-better).

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

@ -40,6 +40,11 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
/// </summary>
private Vp8LBitWriter bitWriter;
/// <summary>
/// The quality, that will be used to encode the image.
/// </summary>
private readonly int quality;
private const int ApplyPaletteGreedyMax = 4;
private const int PaletteInvSizeBits = 11;
@ -52,11 +57,13 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
/// <param name="memoryAllocator">The memory allocator.</param>
/// <param name="width">The width of the input image.</param>
/// <param name="height">The height of the input image.</param>
public Vp8LEncoder(MemoryAllocator memoryAllocator, int width, int height)
/// <param name="quality">The encoding quality.</param>
public Vp8LEncoder(MemoryAllocator memoryAllocator, int width, int height, int quality)
{
var pixelCount = width * height;
int initialSize = pixelCount * 2;
this.quality = quality.Clamp(1, 100);
this.bitWriter = new Vp8LBitWriter(initialSize);
this.Bgra = memoryAllocator.Allocate<uint>(pixelCount);
this.Palette = memoryAllocator.Allocate<uint>(WebPConstants.MaxPaletteSize);
@ -255,8 +262,6 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
// Analyze image (entropy, numPalettes etc).
CrunchConfig[] crunchConfigs = this.EncoderAnalyze(image, out bool redAndBlueAlwaysZero);
int quality = 75; // TODO: quality is hardcoded for now.
// TODO : Do we want to do this multi-threaded, this will probably require a second class:
// one which co-ordinates the threading and comparison and another which does the actual encoding
foreach (CrunchConfig crunchConfig in crunchConfigs)
@ -297,12 +302,12 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
if (this.UsePredictorTransform)
{
this.ApplyPredictFilter(this.CurrentWidth, height, quality, this.UseSubtractGreenTransform);
this.ApplyPredictFilter(this.CurrentWidth, height, this.quality, this.UseSubtractGreenTransform);
}
if (this.UseCrossColorTransform)
{
this.ApplyCrossColorFilter(this.CurrentWidth, height, quality);
this.ApplyCrossColorFilter(this.CurrentWidth, height, this.quality);
}
this.bitWriter.PutBits(0, 1); // No more transforms.
@ -314,7 +319,6 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
this.Refs,
this.CurrentWidth,
height,
quality,
useCache,
crunchConfig,
this.CacheBits,
@ -329,7 +333,6 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
private CrunchConfig[] EncoderAnalyze<TPixel>(Image<TPixel> image, out bool redAndBlueAlwaysZero)
where TPixel : unmanaged, IPixel<TPixel>
{
var configQuality = 75; // TODO: hardcoded quality for now
int method = 4; // TODO: method hardcoded to 4 for now.
int width = image.Width;
int height = image.Height;
@ -348,7 +351,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
bool doNotCache = false;
var crunchConfigs = new List<CrunchConfig>();
if (method == 6 && configQuality == 100)
if (method == 6 && this.quality == 100)
{
doNotCache = true;
@ -367,7 +370,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
{
// Only choose the guessed best transform.
crunchConfigs.Add(new CrunchConfig { EntropyIdx = entropyIdx });
if (configQuality >= 75 && method == 5)
if (this.quality >= 75 && method == 5)
{
// Test with and without color cache.
doNotCache = true;
@ -396,14 +399,14 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossless
return crunchConfigs.ToArray();
}
private void EncodeImage(Span<uint> bgra, Vp8LHashChain hashChain, Vp8LBackwardRefs[] refsArray, int width, int height, int quality, bool useCache, CrunchConfig config, int cacheBits, int histogramBits, int initBytePosition)
private void EncodeImage(Span<uint> bgra, Vp8LHashChain hashChain, Vp8LBackwardRefs[] refsArray, int width, int height, bool useCache, CrunchConfig config, int cacheBits, int histogramBits, int initBytePosition)
{
int histogramImageXySize = LosslessUtils.SubSampleSize(width, histogramBits) * LosslessUtils.SubSampleSize(height, histogramBits);
var histogramSymbols = new ushort[histogramImageXySize];
var huffTree = new HuffmanTree[3 * WebPConstants.CodeLengthCodes];
for (int i = 0; i < huffTree.Length; i++)
{
huffTree[i] = new HuffmanTree();
huffTree[i] = default;
}
if (useCache)

18
src/ImageSharp/Formats/WebP/MetadataExtensions.cs

@ -0,0 +1,18 @@
using SixLabors.ImageSharp.Formats.WebP;
using SixLabors.ImageSharp.Metadata;
namespace SixLabors.ImageSharp
{
/// <summary>
/// Extension methods for the <see cref="ImageMetadata"/> type.
/// </summary>
public static partial class MetadataExtensions
{
/// <summary>
/// Gets the webp format specific metadata for the image.
/// </summary>
/// <param name="metadata">The metadata this method extends.</param>
/// <returns>The <see cref="WebPMetadata"/>.</returns>
public static WebPMetadata GetWebpMetadata(this ImageMetadata metadata) => metadata.GetFormatMetadata(WebPFormat.Instance);
}
}

2
src/ImageSharp/Formats/WebP/WebPEncoder.cs

@ -18,7 +18,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
public bool Lossy { get; set; }
/// <inheritdoc/>
public float Quality { get; set; }
public int Quality { get; set; }
/// <inheritdoc/>
public int Method { get; set; }

4
src/ImageSharp/Formats/WebP/WebPEncoderCore.cs

@ -41,7 +41,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// <summary>
/// Compression quality. Between 0 and 100.
/// </summary>
private float quality;
private readonly int quality;
/// <summary>
/// Initializes a new instance of the <see cref="WebPEncoderCore"/> class.
@ -78,7 +78,7 @@ namespace SixLabors.ImageSharp.Formats.WebP
}
else
{
var enc = new Vp8LEncoder(this.memoryAllocator, image.Width, image.Height);
var enc = new Vp8LEncoder(this.memoryAllocator, image.Width, image.Height, this.quality);
enc.Encode(image, stream);
}
}

4
src/ImageSharp/Formats/WebP/WebPFormat.cs

@ -10,6 +10,10 @@ namespace SixLabors.ImageSharp.Formats.WebP
/// </summary>
public sealed class WebPFormat : IImageFormat<WebPMetadata>
{
private WebPFormat()
{
}
/// <summary>
/// Gets the current instance.
/// </summary>

9
tests/ImageSharp.Tests/Formats/WebP/WebPEncoderTests.cs

@ -9,8 +9,10 @@ namespace SixLabors.ImageSharp.Tests.Formats.WebP
public class WebPEncoderTests
{
[Theory]
[WithFile(TestImages.Bmp.Car, PixelTypes.Rgba32)]
public void Encode_Lossless_Works<TPixel>(TestImageProvider<TPixel> provider)
[WithFile(TestImages.Bmp.Car, PixelTypes.Rgba32, 100)]
[WithFile(TestImages.Bmp.Car, PixelTypes.Rgba32, 80)]
[WithFile(TestImages.Bmp.Car, PixelTypes.Rgba32, 20)]
public void Encode_Lossless_Works<TPixel>(TestImageProvider<TPixel> provider, int quality)
where TPixel : unmanaged, IPixel<TPixel>
{
var encoder = new WebPEncoder()
@ -20,7 +22,8 @@ namespace SixLabors.ImageSharp.Tests.Formats.WebP
using (Image<TPixel> image = provider.GetImage())
{
image.VerifyEncoder(provider, "webp", "lossless", encoder);
var testOutputDetails = string.Concat("lossless", "_", quality);
image.VerifyEncoder(provider, "webp", testOutputDetails, encoder);
}
}
}

Loading…
Cancel
Save