diff --git a/src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs b/src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs index 16eb7e298..e580421de 100644 --- a/src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs +++ b/src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs @@ -27,6 +27,16 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy /// private readonly Vp8BitWriter bitWriter; + /// + /// The quality, that will be used to encode the image. + /// + private readonly int quality; + + /// + /// Quality/speed trade-off (0=fast, 6=slower-better). + /// + private readonly int method; + /// /// Fixed-point precision for RGB->YUV. /// @@ -42,7 +52,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy private const int QFix = 17; - private readonly byte[] zigzag = new byte[] { 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15 }; + private readonly byte[] zigzag = { 0, 1, 4, 8, 5, 2, 3, 6, 9, 12, 13, 10, 7, 11, 14, 15 }; private readonly byte[] averageBytesPerMb = { 50, 24, 16, 9, 7, 5, 3, 2 }; @@ -52,9 +62,13 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy /// The memory allocator. /// The width of the input image. /// The height of the input image. - public Vp8Encoder(MemoryAllocator memoryAllocator, int width, int height) + /// The encoding quality. + /// Quality/speed trade-off (0=fast, 6=slower-better). + public Vp8Encoder(MemoryAllocator memoryAllocator, int width, int height, int quality, int method) { this.memoryAllocator = memoryAllocator; + this.quality = quality.Clamp(0, 100); + this.method = method.Clamp(0, 6); var pixelCount = width * height; int mbw = (width + 15) >> 4; @@ -81,10 +95,19 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy /// public int Alpha { get; set; } + /// + /// Gets the luma component. + /// private IMemoryOwner Y { get; } + /// + /// Gets the chroma U component. + /// private IMemoryOwner U { get; } + /// + /// Gets the chroma U component. + /// private IMemoryOwner V { get; } /// @@ -112,6 +135,12 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy /// private int MbHeaderLimit { get; } + /// + /// Encodes the image to the specified stream from the . + /// + /// The pixel format. + /// The to encode from. + /// The to encode the image data to. public void Encode(Image image, Stream stream) where TPixel : unmanaged, IPixel { @@ -139,10 +168,8 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy } var it = new Vp8EncIterator(this.YTop, this.UvTop, this.Preds, this.Nz, mb, mbw, mbh); - int method = 4; // TODO: hardcoded for now - int quality = 100; // TODO: hardcoded for now var alphas = new int[WebPConstants.MaxAlpha + 1]; - int alpha = this.MacroBlockAnalysis(width, height, it, y, u, v, yStride, uvStride, method, quality, alphas, out int uvAlpha); + int alpha = this.MacroBlockAnalysis(width, height, it, y, u, v, yStride, uvStride, alphas, out int uvAlpha); // Analysis is done, proceed to actual coding. @@ -156,7 +183,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy { var info = new Vp8ModeScore(); it.Import(y, u, v, yStride, uvStride, width, height); - if (!this.Decimate(it, segmentInfos, info, method)) + if (!this.Decimate(it, segmentInfos, info)) { this.CodeResiduals(it, info); } @@ -196,7 +223,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy // TODO: SetupMatrices } - private int MacroBlockAnalysis(int width, int height, Vp8EncIterator it, Span y, Span u, Span v, int yStride, int uvStride, int method, int quality, int[] alphas, out int uvAlpha) + private int MacroBlockAnalysis(int width, int height, Vp8EncIterator it, Span y, Span u, Span v, int yStride, int uvStride, int[] alphas, out int uvAlpha) { int alpha = 0; uvAlpha = 0; @@ -205,7 +232,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy do { it.Import(y, u, v, yStride, uvStride, width, height); - int bestAlpha = this.MbAnalyze(it, method, quality, alphas, out var bestUvAlpha); + int bestAlpha = this.MbAnalyze(it, alphas, out var bestUvAlpha); // Accumulate for later complexity analysis. alpha += bestAlpha; @@ -217,16 +244,16 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy return alpha; } - private int MbAnalyze(Vp8EncIterator it, int method, int quality, int[] alphas, out int bestUvAlpha) + private int MbAnalyze(Vp8EncIterator it, int[] alphas, out int bestUvAlpha) { it.SetIntra16Mode(0); // default: Intra16, DC_PRED it.SetSkip(false); // not skipped. it.SetSegment(0); // default segment, spec-wise. int bestAlpha; - if (method <= 1) + if (this.method <= 1) { - bestAlpha = it.FastMbAnalyze(quality); + bestAlpha = it.FastMbAnalyze(this.quality); } else { @@ -244,7 +271,7 @@ namespace SixLabors.ImageSharp.Formats.WebP.Lossy return bestAlpha; // Mixed susceptibility (not just luma). } - private bool Decimate(Vp8EncIterator it, Vp8SegmentInfo[] segmentInfos, Vp8ModeScore rd, int method) + private bool Decimate(Vp8EncIterator it, Vp8SegmentInfo[] segmentInfos, Vp8ModeScore rd) { rd.InitScore(); @@ -256,7 +283,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. - this.RefineUsingDistortion(it, segmentInfos, rd, method >= 2, method >= 1); + this.RefineUsingDistortion(it, segmentInfos, rd, this.method >= 2, this.method >= 1); bool isSkipped = rd.Nz == 0; it.SetSkip(isSkipped); diff --git a/src/ImageSharp/Formats/WebP/WebPEncoderCore.cs b/src/ImageSharp/Formats/WebP/WebPEncoderCore.cs index 8943ce9bd..1158421f0 100644 --- a/src/ImageSharp/Formats/WebP/WebPEncoderCore.cs +++ b/src/ImageSharp/Formats/WebP/WebPEncoderCore.cs @@ -79,7 +79,7 @@ namespace SixLabors.ImageSharp.Formats.WebP if (this.lossy) { - var enc = new Vp8Encoder(this.memoryAllocator, image.Width, image.Height); + var enc = new Vp8Encoder(this.memoryAllocator, image.Width, image.Height, this.quality, this.method); enc.Encode(image, stream); } else