From 8d7d68f62656e01a0e36b3277650425a511a832c Mon Sep 17 00:00:00 2001 From: Brian Popow Date: Tue, 29 Jun 2021 12:24:05 +0200 Subject: [PATCH] AnalyzeBestIntra4Mode for method >= 5 --- .../Formats/WebP/Lossless/Vp8LHistogram.cs | 17 +++++- .../Formats/WebP/Lossy/Vp8EncIterator.cs | 58 ++++++++++++++++++- .../Formats/WebP/Lossy/Vp8Encoder.cs | 16 ++++- 3 files changed, 85 insertions(+), 6 deletions(-) diff --git a/src/ImageSharp/Formats/WebP/Lossless/Vp8LHistogram.cs b/src/ImageSharp/Formats/WebP/Lossless/Vp8LHistogram.cs index c5db7a568e..d49653a054 100644 --- a/src/ImageSharp/Formats/WebP/Lossless/Vp8LHistogram.cs +++ b/src/ImageSharp/Formats/WebP/Lossless/Vp8LHistogram.cs @@ -68,7 +68,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless this.Alpha = new uint[WebpConstants.NumLiteralCodes + 1]; this.Distance = new uint[WebpConstants.NumDistanceCodes]; - var literalSize = WebpConstants.NumLiteralCodes + WebpConstants.NumLengthCodes + (1 << WebpConstants.MaxColorCacheBits); + int literalSize = WebpConstants.NumLiteralCodes + WebpConstants.NumLengthCodes + (1 << WebpConstants.MaxColorCacheBits); this.Literal = new uint[literalSize + 1]; // 5 for literal, red, blue, alpha, distance. @@ -232,7 +232,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless public double AddThresh(Vp8LHistogram b, double costThreshold) { double costInitial = -this.BitCost; - this.GetCombinedHistogramEntropy(b, costThreshold, costInitial, out var cost); + this.GetCombinedHistogramEntropy(b, costThreshold, costInitial, out double cost); return cost; } @@ -350,6 +350,19 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossless return alpha; } + public void Merge(Vp8LHistogram other) + { + if (this.maxValue > other.maxValue) + { + other.maxValue = this.maxValue; + } + + if (this.lastNonZero > other.lastNonZero) + { + other.lastNonZero = this.lastNonZero; + } + } + private void SetHistogramData(int[] distribution) { int maxValue = 0; diff --git a/src/ImageSharp/Formats/WebP/Lossy/Vp8EncIterator.cs b/src/ImageSharp/Formats/WebP/Lossy/Vp8EncIterator.cs index 4bdae02658..340263f7ef 100644 --- a/src/ImageSharp/Formats/WebP/Lossy/Vp8EncIterator.cs +++ b/src/ImageSharp/Formats/WebP/Lossy/Vp8EncIterator.cs @@ -18,9 +18,13 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy public const int VOffEnc = 16 + 8; + private const int MaxIntra16Mode = 2; + + private const int MaxIntra4Mode = 2; + private const int MaxUvMode = 2; - private const int MaxIntra16Mode = 2; + private const int DefaultAlpha = -1; private readonly int mbw; @@ -373,7 +377,7 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy { int maxMode = MaxIntra16Mode; int mode; - int bestAlpha = -1; + int bestAlpha = DefaultAlpha; int bestMode = 0; this.MakeLuma16Preds(); @@ -393,9 +397,57 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy return bestAlpha; } + public int MbAnalyzeBestIntra4Mode(int bestAlpha) + { + byte[] modes = new byte[16]; + int maxMode = MaxIntra4Mode; + int i4Alpha; + var totalHisto = new Vp8LHistogram(); + int curHisto = 0; + this.StartI4(); + do + { + int mode; + int bestModeAlpha = DefaultAlpha; + var histos = new Vp8LHistogram[2]; + Span src = this.YuvIn.AsSpan(YOffEnc + WebpLookupTables.Vp8Scan[this.I4]); + + this.MakeIntra4Preds(); + for (mode = 0; mode < maxMode; ++mode) + { + int alpha; + histos[curHisto] = new Vp8LHistogram(); + histos[curHisto].CollectHistogram(src, this.YuvP.AsSpan(Vp8Encoding.Vp8I4ModeOffsets[mode]), 0, 1); + + alpha = histos[curHisto].GetAlpha(); + if (alpha > bestModeAlpha) + { + bestModeAlpha = alpha; + modes[this.I4] = (byte)mode; + + // Keep track of best histo so far. + curHisto ^= 1; + } + } + + // Accumulate best histogram. + histos[curHisto ^ 1].Merge(totalHisto); + } + while (this.RotateI4(this.YuvIn.AsSpan(YOffEnc))); // Note: we reuse the original samples for predictors. + + i4Alpha = totalHisto.GetAlpha(); + if (i4Alpha > bestAlpha) + { + this.SetIntra4Mode(modes); + bestAlpha = i4Alpha; + } + + return bestAlpha; + } + public int MbAnalyzeBestUvMode() { - int bestAlpha = -1; + int bestAlpha = DefaultAlpha; int smallestAlpha = 0; int bestMode = 0; int maxMode = MaxUvMode; diff --git a/src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs b/src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs index 4544292f6d..9d1baafb94 100644 --- a/src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs +++ b/src/ImageSharp/Formats/WebP/Lossy/Vp8Encoder.cs @@ -850,7 +850,21 @@ namespace SixLabors.ImageSharp.Formats.Webp.Lossy it.SetSkip(false); // not skipped. it.SetSegment(0); // default segment, spec-wise. - int bestAlpha = this.method <= 1 ? it.FastMbAnalyze(this.quality) : it.MbAnalyzeBestIntra16Mode(); + int bestAlpha; + if (this.method <= 1) + { + bestAlpha = it.FastMbAnalyze(this.quality); + } + else + { + bestAlpha = it.MbAnalyzeBestIntra16Mode(); + if (this.method >= 5) + { + // We go and make a fast decision for intra4/intra16. + // It's usually not a good and definitive pick, but helps seeding the stats about level bit-cost. + bestAlpha = it.MbAnalyzeBestIntra4Mode(bestAlpha); + } + } bestUvAlpha = it.MbAnalyzeBestUvMode();