private void Residual(IMacroblockType macroBlockType, int startIdx, int endIdx)
        {
            // Special case for unavailable macroblock states of intra coded blocks in inter-coded slices
            bool b1 = macroBlockType.IntraCoded && !_sliceState.IntraCoded;
            bool b2 = _mbFlags.TransformSize8X8Flag && (ChromaFormat == ChromaFormat.YCbCr444);

            if (b1 || b2)
            {
                byte unavailableCodedBlockFlags = (byte)(macroBlockType.IntraCoded ? 0xff : 0);
                if ((b1 && ((_mbStateA.Flags & 0x40) == 0x40)) || (b2 && ((_mbStateA.Flags & 0x4) == 0)))
                {
                    _mbStateA.LumaCodedBlockFlags = unavailableCodedBlockFlags;
                    _mbStateA.CrCodedBlockFlags   = unavailableCodedBlockFlags;
                    _mbStateA.CbCodedBlockFlags   = unavailableCodedBlockFlags;
                }
                if ((b1 && ((_mbStateB.Flags & 0x40) == 0x40)) || (b2 && ((_mbStateB.Flags & 0x4) == 0)))
                {
                    _mbStateB.LumaCodedBlockFlags = unavailableCodedBlockFlags;
                    _mbStateB.CrCodedBlockFlags   = unavailableCodedBlockFlags;
                    _mbStateB.CbCodedBlockFlags   = unavailableCodedBlockFlags;
                }
            }

            var codedBlockFlags = new CodedBlockFlags(_mbStateA.LumaCodedBlockFlags, _mbStateB.LumaCodedBlockFlags);

            ResidualLuma(macroBlockType, startIdx, endIdx, ref codedBlockFlags, 0);

            _currMbState.LumaCodedBlockFlags = codedBlockFlags.Flags;

            if (_sequenceState.IsChromaSubsampling)             // Chroma format 4:2:0 or 4:2:2
            {
                if (_codedBlockPattern.IsChromaDcResidualPresent)
                {
// ReSharper disable SuggestUseVarKeywordEvident
                    int numSubBlocks = (int)(4 * ChromaFormat.NumC8X8);                     // in 4x4 blocks
// ReSharper restore SuggestUseVarKeywordEvident
                    var cbCodedBlockFlags = new CodedBlockFlags(_mbStateA.CbCodedBlockFlags, _mbStateB.CbCodedBlockFlags);
                    var crCodedBlockFlags = new CodedBlockFlags(_mbStateA.CrCodedBlockFlags, _mbStateB.CrCodedBlockFlags);

                    if (GetCodedBlockFlag(3, ref cbCodedBlockFlags, 32))
                    {
                        cbCodedBlockFlags.SetCodedBlockFlag(32);

                        ResidualBlockCabac(0, (numSubBlocks - 1), numSubBlocks, 3);
                    }
                    if (GetCodedBlockFlag(3, ref crCodedBlockFlags, 32))
                    {
                        crCodedBlockFlags.SetCodedBlockFlag(32);

                        ResidualBlockCabac(0, (numSubBlocks - 1), numSubBlocks, 3);
                    }
                    if (_codedBlockPattern.IsChromaAcResidualPresent)
                    {
                        uint cbfIndex = 16U + (uint)numSubBlocks;
                        for (uint i = 0; i < numSubBlocks; i++)
                        {
                            if (GetCodedBlockFlag(4, ref cbCodedBlockFlags, cbfIndex + i))
                            {
                                cbCodedBlockFlags.SetCodedBlockFlag(cbfIndex + i);

                                ResidualBlockCabac(Math.Max(0, (startIdx - 1)), (endIdx - 1), 15, 4);
                            }
                        }
                        for (uint i = 0; i < numSubBlocks; i++)
                        {
                            if (GetCodedBlockFlag(4, ref crCodedBlockFlags, cbfIndex + i))
                            {
                                crCodedBlockFlags.SetCodedBlockFlag(cbfIndex + i);

                                ResidualBlockCabac(Math.Max(0, (startIdx - 1)), (endIdx - 1), 15, 4);
                            }
                        }
                    }

                    _currMbState.CbCodedBlockFlags = cbCodedBlockFlags.Flags;
                    _currMbState.CrCodedBlockFlags = crCodedBlockFlags.Flags;
                }
            }
            else if (ChromaFormat == ChromaFormat.YCbCr444)
            {
#if DEBUG
                H264Debug.WriteLine("  [Cb]");
#endif
                var cbCodedBlockFlags = new CodedBlockFlags(_mbStateA.CbCodedBlockFlags, _mbStateB.CbCodedBlockFlags);
                ResidualLuma(macroBlockType, startIdx, endIdx, ref cbCodedBlockFlags, 1);

                _currMbState.CbCodedBlockFlags = cbCodedBlockFlags.Flags;

#if DEBUG
                H264Debug.WriteLine("  [Cr]");
#endif
                var crCodedBlockFlags = new CodedBlockFlags(_mbStateA.CrCodedBlockFlags, _mbStateB.CrCodedBlockFlags);
                ResidualLuma(macroBlockType, startIdx, endIdx, ref crCodedBlockFlags, 2);                 // Cr

                _currMbState.CrCodedBlockFlags = crCodedBlockFlags.Flags;
            }
        }
        private void ResidualLuma(IMacroblockType macroBlockType, int startIdx, int endIdx, ref CodedBlockFlags codedBlockFlags, int comp)
        {
            if ((startIdx == 0) && macroBlockType.IsIntra16X16)
            {
                if (GetCodedBlockFlag(CtxBlockCatTabLuma[comp, 0], ref codedBlockFlags, 32))
                {
                    codedBlockFlags.SetCodedBlockFlag(32);

                    ResidualBlockCabac(0, 15, 16, CtxBlockCatTabLuma[comp, 0]);
                }
            }
            for (uint i8X8 = 0; i8X8 < 4; i8X8++)
            {
                if (_codedBlockPattern.IsLumaBitSet(i8X8))
                {
                    if (_mbFlags.TransformSize8X8Flag)
                    {
                        if ((ChromaFormat != ChromaFormat.YCbCr444) || GetCodedBlockFlag(CtxBlockCatTabLuma[comp, 3], ref codedBlockFlags, i8X8 + 16))
                        {
                            ResidualBlockCabac((4 * startIdx), (4 * endIdx) + 3, 64, CtxBlockCatTabLuma[comp, 3]);

                            // Update coded_block_flag prediction
                            codedBlockFlags.SetCodedBlockFlag(i8X8 + 16);
                        }
                    }
                    else
                    {
                        for (uint i4X4 = 0; i4X4 < 4; i4X4++)
                        {
                            uint ctxBlockCat = macroBlockType.IsIntra16X16 ? CtxBlockCatTabLuma[comp, 1] : CtxBlockCatTabLuma[comp, 2];
                            uint subBlkIdx   = ((i8X8 << 2) + i4X4);
                            if (GetCodedBlockFlag(ctxBlockCat, ref codedBlockFlags, subBlkIdx))
                            {
                                codedBlockFlags.SetCodedBlockFlag(subBlkIdx);

                                if (macroBlockType.IsIntra16X16)
                                {
                                    ResidualBlockCabac(Math.Max(0, (startIdx - 1)), (endIdx - 1), 15, ctxBlockCat);
                                }
                                else
                                {
                                    ResidualBlockCabac(startIdx, endIdx, 16, ctxBlockCat);
                                }
                            }
                        }
                    }
                }
            }
        }
        private bool GetCodedBlockFlag(uint ctxBlockCat, ref CodedBlockFlags codedBlockFlags, uint subBlkIdx)
        {
            uint ctxIdx = CodedBlockFlagCtxBlockCatOffset[ctxBlockCat] + codedBlockFlags.GetCtxIdxInc(subBlkIdx);

            return(_arithmeticDecoder.DecodeDecision(ctxIdx) == 1);
        }