コード例 #1
0
        private unsafe void AdjustFilterStrength()
        {
            if (this.filterStrength > 0)
            {
                int maxLevel = 0;
                for (int s = 0; s < WebpConstants.NumMbSegments; s++)
                {
                    Vp8SegmentInfo dqm = this.SegmentInfos[s];

                    // this '>> 3' accounts for some inverse WHT scaling
                    int delta = (dqm.MaxEdge * dqm.Y2.Q[1]) >> 3;
                    int level = this.FilterStrengthFromDelta(this.FilterHeader.Sharpness, delta);
                    if (level > dqm.FStrength)
                    {
                        dqm.FStrength = level;
                    }

                    if (maxLevel < dqm.FStrength)
                    {
                        maxLevel = dqm.FStrength;
                    }
                }

                this.FilterHeader.FilterLevel = maxLevel;
            }
        }
コード例 #2
0
        private void SetupFilterStrength()
        {
            int filterSharpness = 0; // TODO: filterSharpness is hardcoded
            int filterType      = 1; // TODO: filterType is hardcoded

            // level0 is in [0..500]. Using '-f 50' as filter_strength is mid-filtering.
            int level0 = 5 * this.filterStrength;

            for (int i = 0; i < WebpConstants.NumMbSegments; i++)
            {
                Vp8SegmentInfo m = this.SegmentInfos[i];

                // We focus on the quantization of AC coeffs.
                int qstep        = WebpLookupTables.AcTable[Numerics.Clamp(m.Quant, 0, 127)] >> 2;
                int baseStrength = this.FilterStrengthFromDelta(this.FilterHeader.Sharpness, qstep);

                // Segments with lower complexity ('beta') will be less filtered.
                int f = baseStrength * level0 / (256 + m.Beta);
                m.FStrength = f < WebpConstants.FilterStrengthCutoff ? 0 : f > 63 ? 63 : f;
            }

            // We record the initial strength (mainly for the case of 1-segment only).
            this.FilterHeader.FilterLevel = this.SegmentInfos[0].FStrength;
            this.FilterHeader.Simple      = filterType == 0;
            this.FilterHeader.Sharpness   = filterSharpness;
        }
コード例 #3
0
ファイル: QuantEnc.cs プロジェクト: br3aker/ImageSharp
        public static int ReconstructUv(Vp8EncIterator it, Vp8SegmentInfo dqm, Vp8ModeScore rd, Span <byte> yuvOut, int mode)
        {
            Span <byte>  reference = it.YuvP.AsSpan(Vp8Encoding.Vp8UvModeOffsets[mode]);
            Span <byte>  src       = it.YuvIn.AsSpan(Vp8EncIterator.UOffEnc);
            int          nz        = 0;
            int          n;
            Span <short> tmp     = it.Scratch2.AsSpan(0, 8 * 16);
            Span <int>   scratch = it.Scratch3.AsSpan(0, 16);

            for (n = 0; n < 8; n += 2)
            {
                Vp8Encoding.FTransform2(
                    src.Slice(WebpLookupTables.Vp8ScanUv[n]),
                    reference.Slice(WebpLookupTables.Vp8ScanUv[n]),
                    tmp.Slice(n * 16, 16),
                    tmp.Slice((n + 1) * 16, 16),
                    scratch);
            }

            CorrectDcValues(it, ref dqm.Uv, tmp, rd);

            for (n = 0; n < 8; n += 2)
            {
                nz |= Quantize2Blocks(tmp.Slice(n * 16, 32), rd.UvLevels.AsSpan(n * 16, 32), ref dqm.Uv) << n;
            }

            for (n = 0; n < 8; n += 2)
            {
                Vp8Encoding.ITransformTwo(reference.Slice(WebpLookupTables.Vp8ScanUv[n]), tmp.Slice(n * 16, 32), yuvOut.Slice(WebpLookupTables.Vp8ScanUv[n]), scratch);
            }

            return(nz << 16);
        }
コード例 #4
0
ファイル: QuantEnc.cs プロジェクト: br3aker/ImageSharp
        public static int ReconstructIntra4(Vp8EncIterator it, Vp8SegmentInfo dqm, Span <short> levels, Span <byte> src, Span <byte> yuvOut, int mode)
        {
            Span <byte>  reference = it.YuvP.AsSpan(Vp8Encoding.Vp8I4ModeOffsets[mode]);
            Span <short> tmp       = it.Scratch2.AsSpan(0, 16);
            Span <int>   scratch   = it.Scratch3.AsSpan(0, 16);

            Vp8Encoding.FTransform(src, reference, tmp, scratch);
            int nz = QuantizeBlock(tmp, levels, ref dqm.Y1);

            Vp8Encoding.ITransformOne(reference, tmp, yuvOut, scratch);

            return(nz);
        }
コード例 #5
0
ファイル: QuantEnc.cs プロジェクト: br3aker/ImageSharp
        public static int ReconstructIntra16(Vp8EncIterator it, Vp8SegmentInfo dqm, Vp8ModeScore rd, Span <byte> yuvOut, int mode)
        {
            Span <byte>  reference = it.YuvP.AsSpan(Vp8Encoding.Vp8I16ModeOffsets[mode]);
            Span <byte>  src       = it.YuvIn.AsSpan(Vp8EncIterator.YOffEnc);
            int          nz        = 0;
            int          n;
            Span <short> shortScratchSpan = it.Scratch2.AsSpan();
            Span <int>   scratch          = it.Scratch3.AsSpan(0, 16);

            shortScratchSpan.Clear();
            scratch.Clear();
            Span <short> dcTmp = shortScratchSpan.Slice(0, 16);
            Span <short> tmp   = shortScratchSpan.Slice(16, 16 * 16);

            for (n = 0; n < 16; n += 2)
            {
                Vp8Encoding.FTransform2(
                    src.Slice(WebpLookupTables.Vp8Scan[n]),
                    reference.Slice(WebpLookupTables.Vp8Scan[n]),
                    tmp.Slice(n * 16, 16),
                    tmp.Slice((n + 1) * 16, 16),
                    scratch);
            }

            Vp8Encoding.FTransformWht(tmp, dcTmp, scratch);
            nz |= QuantizeBlock(dcTmp, rd.YDcLevels, ref dqm.Y2) << 24;

            for (n = 0; n < 16; n += 2)
            {
                // Zero-out the first coeff, so that: a) nz is correct below, and
                // b) finding 'last' non-zero coeffs in SetResidualCoeffs() is simplified.
                tmp[n * 16] = tmp[(n + 1) * 16] = 0;
                nz         |= Quantize2Blocks(tmp.Slice(n * 16, 32), rd.YAcLevels.AsSpan(n * 16, 32), ref dqm.Y1) << n;
            }

            // Transform back.
            LossyUtils.TransformWht(dcTmp, tmp, scratch);
            for (n = 0; n < 16; n += 2)
            {
                Vp8Encoding.ITransformTwo(reference.Slice(WebpLookupTables.Vp8Scan[n]), tmp.Slice(n * 16, 32), yuvOut.Slice(WebpLookupTables.Vp8Scan[n]), scratch);
            }

            return(nz);
        }
コード例 #6
0
        private unsafe void SetupMatrices(Vp8SegmentInfo[] dqm)
        {
            int tlambdaScale = this.method >= WebpEncodingMethod.Default ? this.spatialNoiseShaping : 0;

            for (int i = 0; i < dqm.Length; i++)
            {
                Vp8SegmentInfo m = dqm[i];
                int            q = m.Quant;

                m.Y1.Q[0] = WebpLookupTables.DcTable[Numerics.Clamp(q + this.DqY1Dc, 0, 127)];
                m.Y1.Q[1] = WebpLookupTables.AcTable[Numerics.Clamp(q, 0, 127)];

                m.Y2.Q[0] = (ushort)(WebpLookupTables.DcTable[Numerics.Clamp(q + this.DqY2Dc, 0, 127)] * 2);
                m.Y2.Q[1] = WebpLookupTables.AcTable2[Numerics.Clamp(q + this.DqY2Ac, 0, 127)];

                m.Uv.Q[0] = WebpLookupTables.DcTable[Numerics.Clamp(q + this.DqUvDc, 0, 117)];
                m.Uv.Q[1] = WebpLookupTables.AcTable[Numerics.Clamp(q + this.DqUvAc, 0, 127)];

                int qi4  = m.Y1.Expand(0);
                int qi16 = m.Y2.Expand(1);
                int quv  = m.Uv.Expand(2);

                m.LambdaI16  = 3 * qi16 * qi16;
                m.LambdaI4   = (3 * qi4 * qi4) >> 7;
                m.LambdaUv   = (3 * quv * quv) >> 6;
                m.LambdaMode = (1 * qi4 * qi4) >> 7;
                m.TLambda    = (tlambdaScale * qi4) >> 5;

                // none of these constants should be < 1.
                m.LambdaI16  = m.LambdaI16 < 1 ? 1 : m.LambdaI16;
                m.LambdaI4   = m.LambdaI4 < 1 ? 1 : m.LambdaI4;
                m.LambdaUv   = m.LambdaUv < 1 ? 1 : m.LambdaUv;
                m.LambdaMode = m.LambdaMode < 1 ? 1 : m.LambdaMode;
                m.TLambda    = m.TLambda < 1 ? 1 : m.TLambda;

                m.MinDisto = 20 * m.Y1.Q[0];
                m.MaxEdge  = 0;

                m.I4Penalty = 1000 * qi4 * qi4;
            }
        }
コード例 #7
0
ファイル: QuantEnc.cs プロジェクト: br3aker/ImageSharp
        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);
            }
        }
コード例 #8
0
ファイル: QuantEnc.cs プロジェクト: br3aker/ImageSharp
        // 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;
        }
コード例 #9
0
ファイル: QuantEnc.cs プロジェクト: br3aker/ImageSharp
        public static void PickBestUv(Vp8EncIterator it, ref Vp8ModeScore rd, Vp8SegmentInfo[] segmentInfos, Vp8EncProba proba)
        {
            const int      numBlocks = 8;
            Vp8SegmentInfo dqm       = segmentInfos[it.CurrentMacroBlockInfo.Segment];
            int            lambda    = dqm.LambdaUv;
            Span <byte>    src       = it.YuvIn.AsSpan(Vp8EncIterator.UOffEnc);
            Span <byte>    tmpDst    = it.YuvOut2.AsSpan(Vp8EncIterator.UOffEnc);
            Span <byte>    dst0      = it.YuvOut.AsSpan(Vp8EncIterator.UOffEnc);
            Span <byte>    dst       = dst0;
            var            rdBest    = new Vp8ModeScore();
            var            rdUv      = new Vp8ModeScore();
            var            res       = new Vp8Residual();
            int            mode;

            rd.ModeUv = -1;
            rdBest.InitScore();
            for (mode = 0; mode < WebpConstants.NumPredModes; ++mode)
            {
                rdUv.Clear();

                // Reconstruct
                rdUv.Nz = (uint)ReconstructUv(it, dqm, rdUv, tmpDst, mode);

                // Compute RD-score
                rdUv.D  = LossyUtils.Vp8_Sse16X8(src, tmpDst);
                rdUv.SD = 0;    // not calling TDisto here: it tends to flatten areas.
                rdUv.H  = WebpConstants.Vp8FixedCostsUv[mode];
                rdUv.R  = it.GetCostUv(rdUv, proba, res);
                if (mode > 0 && IsFlat(rdUv.UvLevels, numBlocks, WebpConstants.FlatnessLimitIUv))
                {
                    rdUv.R += WebpConstants.FlatnessPenality * numBlocks;
                }

                rdUv.SetRdScore(lambda);
                if (mode == 0 || rdUv.Score < rdBest.Score)
                {
                    rdBest.CopyScore(rdUv);
                    rd.ModeUv = mode;
                    rdUv.UvLevels.CopyTo(rd.UvLevels.AsSpan());
                    for (int i = 0; i < 2; i++)
                    {
                        rd.Derr[i, 0] = rdUv.Derr[i, 0];
                        rd.Derr[i, 1] = rdUv.Derr[i, 1];
                        rd.Derr[i, 2] = rdUv.Derr[i, 2];
                    }

                    Span <byte> tmp = dst;
                    dst    = tmpDst;
                    tmpDst = tmp;
                }
            }

            it.SetIntraUvMode(rd.ModeUv);
            rd.AddScore(rdBest);
            if (dst != dst0)
            {
                // copy 16x8 block if needed.
                LossyUtils.Vp8Copy16X8(dst, dst0);
            }

            // Store diffusion errors for next block.
            it.StoreDiffusionErrors(rd);
        }
コード例 #10
0
ファイル: QuantEnc.cs プロジェクト: br3aker/ImageSharp
        public static bool PickBestIntra4(Vp8EncIterator it, ref Vp8ModeScore rd, Vp8SegmentInfo[] segmentInfos, Vp8EncProba proba, int maxI4HeaderBits)
        {
            Vp8SegmentInfo dqm             = segmentInfos[it.CurrentMacroBlockInfo.Segment];
            int            lambda          = dqm.LambdaI4;
            int            tlambda         = dqm.TLambda;
            Span <byte>    src0            = it.YuvIn.AsSpan(Vp8EncIterator.YOffEnc);
            Span <byte>    bestBlocks      = it.YuvOut2.AsSpan(Vp8EncIterator.YOffEnc);
            Span <int>     scratch         = it.Scratch3;
            int            totalHeaderBits = 0;
            var            rdBest          = new Vp8ModeScore();

            if (maxI4HeaderBits == 0)
            {
                return(false);
            }

            rdBest.InitScore();
            rdBest.H = 211;  // '211' is the value of VP8BitCost(0, 145)
            rdBest.SetRdScore(dqm.LambdaMode);
            it.StartI4();
            var          rdi4      = new Vp8ModeScore();
            var          rdTmp     = new Vp8ModeScore();
            var          res       = new Vp8Residual();
            Span <short> tmpLevels = new short[16];

            do
            {
                int numBlocks = 1;
                rdi4.Clear();
                int         mode;
                int         bestMode  = -1;
                Span <byte> src       = src0.Slice(WebpLookupTables.Vp8Scan[it.I4]);
                short[]     modeCosts = it.GetCostModeI4(rd.ModesI4);
                Span <byte> bestBlock = bestBlocks.Slice(WebpLookupTables.Vp8Scan[it.I4]);
                Span <byte> tmpDst    = it.Scratch.AsSpan();
                tmpDst.Clear();

                rdi4.InitScore();
                it.MakeIntra4Preds();
                for (mode = 0; mode < WebpConstants.NumBModes; ++mode)
                {
                    rdTmp.Clear();
                    tmpLevels.Clear();

                    // Reconstruct.
                    rdTmp.Nz = (uint)ReconstructIntra4(it, dqm, tmpLevels, src, tmpDst, mode);

                    // Compute RD-score.
                    rdTmp.D  = LossyUtils.Vp8_Sse4X4(src, tmpDst);
                    rdTmp.SD = tlambda != 0 ? Mult8B(tlambda, LossyUtils.Vp8Disto4X4(src, tmpDst, WeightY, scratch)) : 0;
                    rdTmp.H  = modeCosts[mode];

                    // Add flatness penalty, to avoid flat area to be mispredicted by a complex mode.
                    if (mode > 0 && IsFlat(tmpLevels, numBlocks, WebpConstants.FlatnessLimitI4))
                    {
                        rdTmp.R = WebpConstants.FlatnessPenality * numBlocks;
                    }
                    else
                    {
                        rdTmp.R = 0;
                    }

                    // Early-out check.
                    rdTmp.SetRdScore(lambda);
                    if (bestMode >= 0 && rdTmp.Score >= rdi4.Score)
                    {
                        continue;
                    }

                    // Finish computing score.
                    rdTmp.R += it.GetCostLuma4(tmpLevels, proba, res);
                    rdTmp.SetRdScore(lambda);

                    if (bestMode < 0 || rdTmp.Score < rdi4.Score)
                    {
                        rdi4.CopyScore(rdTmp);
                        bestMode = mode;
                        Span <byte> tmp = tmpDst;
                        tmpDst    = bestBlock;
                        bestBlock = tmp;
                        tmpLevels.CopyTo(rdBest.YAcLevels.AsSpan(it.I4 * 16, 16));
                    }
                }

                rdi4.SetRdScore(dqm.LambdaMode);
                rdBest.AddScore(rdi4);
                if (rdBest.Score >= rd.Score)
                {
                    return(false);
                }

                totalHeaderBits += (int)rdi4.H;   // <- equal to modeCosts[bestMode];
                if (totalHeaderBits > maxI4HeaderBits)
                {
                    return(false);
                }

                // Copy selected samples to the right place.
                LossyUtils.Vp8Copy4X4(bestBlock, bestBlocks.Slice(WebpLookupTables.Vp8Scan[it.I4]));

                rd.ModesI4[it.I4]   = (byte)bestMode;
                it.TopNz[it.I4 & 3] = it.LeftNz[it.I4 >> 2] = rdi4.Nz != 0 ? 1 : 0;
            }while (it.RotateI4(bestBlocks));

            // Finalize state.
            rd.CopyScore(rdBest);
            it.SetIntra4Mode(rd.ModesI4);
            it.SwapOut();
            rdBest.YAcLevels.AsSpan().CopyTo(rd.YAcLevels);

            // Select intra4x4 over intra16x16.
            return(true);
        }