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 (this.method <= WebpEncodingMethod.Level1) { bestAlpha = it.FastMbAnalyze(this.quality); } else { bestAlpha = it.MbAnalyzeBestIntra16Mode(); if (this.method >= WebpEncodingMethod.Level5) { // 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(); // Final susceptibility mix. bestAlpha = ((3 * bestAlpha) + bestUvAlpha + 2) >> 2; bestAlpha = FinalAlphaValue(bestAlpha); alphas[bestAlpha]++; it.CurrentMacroBlockInfo.Alpha = bestAlpha; // For later remapping. return(bestAlpha); // Mixed susceptibility (not just luma). }
private const int DSCALE = 1; // storage descaling, needed to make the error fit byte public static void PickBestIntra16(Vp8EncIterator it, ref Vp8ModeScore rd, Vp8SegmentInfo[] segmentInfos, Vp8EncProba proba) { const int numBlocks = 16; Vp8SegmentInfo dqm = segmentInfos[it.CurrentMacroBlockInfo.Segment]; int lambda = dqm.LambdaI16; int tlambda = dqm.TLambda; Span <byte> src = it.YuvIn.AsSpan(Vp8EncIterator.YOffEnc); Span <int> scratch = it.Scratch3; var rdTmp = new Vp8ModeScore(); var res = new Vp8Residual(); Vp8ModeScore rdCur = rdTmp; Vp8ModeScore rdBest = rd; int mode; bool isFlat = IsFlatSource16(src); rd.ModeI16 = -1; for (mode = 0; mode < WebpConstants.NumPredModes; ++mode) { // Scratch buffer. Span <byte> tmpDst = it.YuvOut2.AsSpan(Vp8EncIterator.YOffEnc); rdCur.ModeI16 = mode; // Reconstruct. rdCur.Nz = (uint)ReconstructIntra16(it, dqm, rdCur, tmpDst, mode); // Measure RD-score. rdCur.D = LossyUtils.Vp8_Sse16X16(src, tmpDst); rdCur.SD = tlambda != 0 ? Mult8B(tlambda, LossyUtils.Vp8Disto16X16(src, tmpDst, WeightY, scratch)) : 0; rdCur.H = WebpConstants.Vp8FixedCostsI16[mode]; rdCur.R = it.GetCostLuma16(rdCur, proba, res); if (isFlat) { // Refine the first impression (which was in pixel space). isFlat = IsFlat(rdCur.YAcLevels, numBlocks, WebpConstants.FlatnessLimitI16); if (isFlat) { // Block is very flat. We put emphasis on the distortion being very low! rdCur.D *= 2; rdCur.SD *= 2; } } // Since we always examine Intra16 first, we can overwrite *rd directly. rdCur.SetRdScore(lambda); if (mode == 0 || rdCur.Score < rdBest.Score) { Vp8ModeScore tmp = rdCur; rdCur = rdBest; rdBest = tmp; it.SwapOut(); } } if (rdBest != rd) { rd = rdBest; } // Finalize score for mode decision. rd.SetRdScore(dqm.LambdaMode); it.SetIntra16Mode(rd.ModeI16); // We have a blocky macroblock (only DCs are non-zero) with fairly high // distortion, record max delta so we can later adjust the minimal filtering // strength needed to smooth these blocks out. if ((rd.Nz & 0x100ffff) == 0x1000000 && rd.D > dqm.MinDisto) { dqm.StoreMaxDelta(rd.YDcLevels); } }
// Refine intra16/intra4 sub-modes based on distortion only (not rate). public static void RefineUsingDistortion(Vp8EncIterator it, Vp8SegmentInfo[] segmentInfos, Vp8ModeScore rd, bool tryBothModes, bool refineUvMode, int mbHeaderLimit) { long bestScore = Vp8ModeScore.MaxCost; int nz = 0; int mode; bool isI16 = tryBothModes || it.CurrentMacroBlockInfo.MacroBlockType == Vp8MacroBlockType.I16X16; Vp8SegmentInfo dqm = segmentInfos[it.CurrentMacroBlockInfo.Segment]; // Some empiric constants, of approximate order of magnitude. const int lambdaDi16 = 106; const int lambdaDi4 = 11; const int lambdaDuv = 120; long scoreI4 = dqm.I4Penalty; long i4BitSum = 0; long bitLimit = tryBothModes ? mbHeaderLimit : Vp8ModeScore.MaxCost; // no early-out allowed. if (isI16) { int bestMode = -1; Span <byte> src = it.YuvIn.AsSpan(Vp8EncIterator.YOffEnc); for (mode = 0; mode < WebpConstants.NumPredModes; ++mode) { Span <byte> reference = it.YuvP.AsSpan(Vp8Encoding.Vp8I16ModeOffsets[mode]); long score = (LossyUtils.Vp8_Sse16X16(src, reference) * WebpConstants.RdDistoMult) + (WebpConstants.Vp8FixedCostsI16[mode] * lambdaDi16); if (mode > 0 && WebpConstants.Vp8FixedCostsI16[mode] > bitLimit) { continue; } if (score < bestScore) { bestMode = mode; bestScore = score; } } if (it.X == 0 || it.Y == 0) { // Avoid starting a checkerboard resonance from the border. See bug #432 of libwebp. if (IsFlatSource16(src)) { bestMode = it.X == 0 ? 0 : 2; tryBothModes = false; // Stick to i16. } } it.SetIntra16Mode(bestMode); // We'll reconstruct later, if i16 mode actually gets selected. } // Next, evaluate Intra4. if (tryBothModes || !isI16) { // We don't evaluate the rate here, but just account for it through a // constant penalty (i4 mode usually needs more bits compared to i16). isI16 = false; it.StartI4(); do { int bestI4Mode = -1; long bestI4Score = Vp8ModeScore.MaxCost; Span <byte> src = it.YuvIn.AsSpan(Vp8EncIterator.YOffEnc + WebpLookupTables.Vp8Scan[it.I4]); short[] modeCosts = it.GetCostModeI4(rd.ModesI4); it.MakeIntra4Preds(); for (mode = 0; mode < WebpConstants.NumBModes; ++mode) { Span <byte> reference = it.YuvP.AsSpan(Vp8Encoding.Vp8I4ModeOffsets[mode]); long score = (LossyUtils.Vp8_Sse4X4(src, reference) * WebpConstants.RdDistoMult) + (modeCosts[mode] * lambdaDi4); if (score < bestI4Score) { bestI4Mode = mode; bestI4Score = score; } } i4BitSum += modeCosts[bestI4Mode]; rd.ModesI4[it.I4] = (byte)bestI4Mode; scoreI4 += bestI4Score; if (scoreI4 >= bestScore || i4BitSum > bitLimit) { // Intra4 won't be better than Intra16. Bail out and pick Intra16. isI16 = true; break; } else { // Reconstruct partial block inside YuvOut2 buffer Span <byte> tmpDst = it.YuvOut2.AsSpan(Vp8EncIterator.YOffEnc + WebpLookupTables.Vp8Scan[it.I4]); nz |= ReconstructIntra4(it, dqm, rd.YAcLevels.AsSpan(it.I4 * 16, 16), src, tmpDst, bestI4Mode) << it.I4; } }while (it.RotateI4(it.YuvOut2.AsSpan(Vp8EncIterator.YOffEnc))); } // Final reconstruction, depending on which mode is selected. if (!isI16) { it.SetIntra4Mode(rd.ModesI4); it.SwapOut(); bestScore = scoreI4; } else { int intra16Mode = it.Preds[it.PredIdx]; nz = ReconstructIntra16(it, dqm, rd, it.YuvOut.AsSpan(Vp8EncIterator.YOffEnc), intra16Mode); } // ... and UV! if (refineUvMode) { int bestMode = -1; long bestUvScore = Vp8ModeScore.MaxCost; Span <byte> src = it.YuvIn.AsSpan(Vp8EncIterator.UOffEnc); for (mode = 0; mode < WebpConstants.NumPredModes; ++mode) { Span <byte> reference = it.YuvP.AsSpan(Vp8Encoding.Vp8UvModeOffsets[mode]); long score = (LossyUtils.Vp8_Sse16X8(src, reference) * WebpConstants.RdDistoMult) + (WebpConstants.Vp8FixedCostsUv[mode] * lambdaDuv); if (score < bestUvScore) { bestMode = mode; bestUvScore = score; } } it.SetIntraUvMode(bestMode); } nz |= ReconstructUv(it, dqm, rd, it.YuvOut.AsSpan(Vp8EncIterator.UOffEnc), it.CurrentMacroBlockInfo.UvMode); rd.Nz = (uint)nz; rd.Score = bestScore; }