Skip to content
Snippets Groups Projects
CABACWriter.cpp 112 KiB
Newer Older
  • Learn to ignore specific revisions
  • /* The copyright in this software is being made available under the BSD
    * License, included below. This software may be subject to other third party
    * and contributor rights, including patent rights, and no such rights are
    * granted under this license.
    *
    
    * Copyright (c) 2010-2025, ITU/ISO/IEC
    
    * All rights reserved.
    *
    * Redistribution and use in source and binary forms, with or without
    * modification, are permitted provided that the following conditions are met:
    *
    *  * Redistributions of source code must retain the above copyright notice,
    *    this list of conditions and the following disclaimer.
    *  * Redistributions in binary form must reproduce the above copyright notice,
    *    this list of conditions and the following disclaimer in the documentation
    *    and/or other materials provided with the distribution.
    *  * Neither the name of the ITU/ISO/IEC nor the names of its contributors may
    *    be used to endorse or promote products derived from this software without
    *    specific prior written permission.
    *
    * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
    * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
    * THE POSSIBILITY OF SUCH DAMAGE.
    */
    
    /** \file     CABACWriter.cpp
     *  \brief    Writer for low level syntax
     */
    
    #include "CommonLib/Contexts.h"
    #include "CABACWriter.h"
    
    #include "EncLib.h"
    
    #include "CommonLib/UnitTools.h"
    #include "CommonLib/dtrace_buffer.h"
    
    #include <map>
    #include <algorithm>
    #include <limits>
    
    
    //! \ingroup EncoderLib
    //! \{
    
    void CABACWriter::initCtxModels( const Slice& slice )
    {
      int       qp                = slice.getSliceQp();
      SliceType sliceType         = slice.getSliceType();
      SliceType encCABACTableIdx  = slice.getEncCABACTableIdx();
      if( !slice.isIntra() && (encCABACTableIdx==B_SLICE || encCABACTableIdx==P_SLICE) && slice.getPPS()->getCabacInitPresentFlag() )
      {
        sliceType = encCABACTableIdx;
      }
    
    Frank Bossen's avatar
    Frank Bossen committed
      m_binEncoder.reset(qp, (int) sliceType);
      m_binEncoder.setBaseLevel(slice.getRiceBaseLevel());
    
      m_binEncoder.riceStatReset(slice.getSPS()->getBitDepth(ChannelType::LUMA),
    
    Frank Bossen's avatar
    Frank Bossen committed
                                 slice.getSPS()->getSpsRangeExtension().getPersistentRiceAdaptationEnabledFlag());
    
    }
    
    template <class BinProbModel>
    SliceType xGetCtxInitId( const Slice& slice, const BinEncIf& binEncoder, Ctx& ctxTest )
    {
      const CtxStore<BinProbModel>& ctxStoreTest = static_cast<const CtxStore<BinProbModel>&>( ctxTest );
      const CtxStore<BinProbModel>& ctxStoreRef  = static_cast<const CtxStore<BinProbModel>&>( binEncoder.getCtx() );
      int qp = slice.getSliceQp();
      if( !slice.isIntra() )
      {
        SliceType aSliceTypeChoices[] = { B_SLICE, P_SLICE };
        uint64_t  bestCost            = std::numeric_limits<uint64_t>::max();
        SliceType bestSliceType       = aSliceTypeChoices[0];
        for (uint32_t idx=0; idx<2; idx++)
        {
          uint64_t  curCost           = 0;
          SliceType curSliceType      = aSliceTypeChoices[idx];
          ctxTest.init( qp, (int)curSliceType );
          for( int k = 0; k < Ctx::NumberOfContexts; k++ )
          {
            if( binEncoder.getNumBins(k) > 0 )
            {
              curCost += uint64_t( binEncoder.getNumBins(k) ) * ctxStoreRef[k].estFracExcessBits( ctxStoreTest[k] );
            }
          }
          if (curCost < bestCost)
          {
            bestSliceType = curSliceType;
            bestCost      = curCost;
          }
        }
        return bestSliceType;
      }
      else
      {
        return I_SLICE;
      }
    }
    
    SliceType CABACWriter::getCtxInitId( const Slice& slice )
    {
    
      switch (m_testCtx.getBpmType())
    
      case BpmType::STD: return xGetCtxInitId<BinProbModel_Std>(slice, m_binEncoder, m_testCtx);
    
      default:        return  NUMBER_OF_SLICE_TYPES;
      }
    }
    
    unsigned estBits( BinEncIf& binEnc, const std::vector<bool>& bins, const Ctx& ctx, const int ctxId, const uint8_t winSize )
    {
      binEnc.initCtxAndWinSize( ctxId, ctx, winSize );
      binEnc.start();
      const std::size_t numBins   = bins.size();
      unsigned          startBits = binEnc.getNumWrittenBits();
      for( std::size_t binId = 0; binId < numBins; binId++ )
      {
        unsigned  bin = ( bins[binId] ? 1 : 0 );
        binEnc.encodeBin( bin, ctxId );
      }
      unsigned endBits    = binEnc.getNumWrittenBits();
      unsigned codedBits  = endBits - startBits;
      return   codedBits;
    }
    
    //================================================================================
    //  clause 7.3.8.1
    //--------------------------------------------------------------------------------
    //    void  end_of_slice()
    //================================================================================
    
    void CABACWriter::end_of_slice()
    {
    
    Frank Bossen's avatar
    Frank Bossen committed
      m_binEncoder.encodeBinTrm(1);
      m_binEncoder.finish();
    
    }
    
    //================================================================================
    //  clause 7.3.8.2
    //--------------------------------------------------------------------------------
    
    //    bool  coding_tree_unit( cs, area, qp, ctuRsAddr, skipSao, skipAlf )
    
    //================================================================================
    
    
    void CABACWriter::coding_tree_unit(CodingStructure &cs, const UnitArea &area, EnumArray<int, ChannelType> &qps,
                                       unsigned ctuRsAddr, bool skipSao /* = false */, bool skipAlf /* = false */)
    
      CUCtx           cuCtx(qps[ChannelType::LUMA]);
    
      QTBTPartitioner partitioner;
    
      partitioner.initCtu(area, ChannelType::LUMA, *cs.slice);
    
      if (!skipAlf)
    
        for (int compIdx = 0; compIdx < MAX_NUM_COMPONENT; compIdx++)
    
          codeAlfCtuEnableFlag(cs, ctuRsAddr, compIdx, nullptr);
    
          if (isLuma(ComponentID(compIdx)))
          {
    
            codeAlfCtuFilterIndex(cs, ctuRsAddr, cs.slice->getAlfEnabledFlag(COMPONENT_Y));
    
            AlfMode *alfModes =
              cs.slice->getAlfEnabledFlag((ComponentID) compIdx) ? cs.slice->getPic()->getAlfModes(compIdx) : nullptr;
            if (alfModes != nullptr && alfModes[ctuRsAddr] != AlfMode::OFF)
    
            {
              codeAlfCtuAlternative( cs, ctuRsAddr, compIdx );
            }
          }
    
      if ( !skipAlf )
      {
        for ( int compIdx = 1; compIdx < getNumberValidComponents( cs.pcv->chrFormat ); compIdx++ )
        {
          if (cs.slice->m_ccAlfFilterParam.ccAlfFilterEnabled[compIdx - 1])
          {
            const int filterCount   = cs.slice->m_ccAlfFilterParam.ccAlfFilterCount[compIdx - 1];
    
            const int      ry = ctuRsAddr / cs.pcv->widthInCtus;
            const int      rx = ctuRsAddr % cs.pcv->widthInCtus;
            const Position lumaPos(rx * cs.pcv->maxCUWidth, ry * cs.pcv->maxCUHeight);
    
            codeCcAlfFilterControlIdc(cs.slice->m_ccAlfFilterControl[compIdx - 1][ctuRsAddr], cs, ComponentID(compIdx),
                                      ctuRsAddr, cs.slice->m_ccAlfFilterControl[compIdx - 1], lumaPos, filterCount);
          }
        }
      }
    
    
      if (CS::isDualITree(cs) && isChromaEnabled(cs.pcv->chrFormat) && cs.pcv->maxCUWidth > 64)
    
        CUCtx           chromaCuCtx(qps[ChannelType::CHROMA]);
    
        QTBTPartitioner chromaPartitioner;
    
        chromaPartitioner.initCtu(area, ChannelType::CHROMA, *cs.slice);
    
        coding_tree(cs, partitioner, cuCtx, &chromaPartitioner, &chromaCuCtx);
    
        qps[ChannelType::LUMA]   = cuCtx.qp;
        qps[ChannelType::CHROMA] = chromaCuCtx.qp;
    
        coding_tree(cs, partitioner, cuCtx);
    
        qps[ChannelType::LUMA] = cuCtx.qp;
    
        if (CS::isDualITree(cs) && isChromaEnabled(cs.pcv->chrFormat))
    
          CUCtx cuCtxChroma(qps[ChannelType::CHROMA]);
          partitioner.initCtu(area, ChannelType::CHROMA, *cs.slice);
    
          coding_tree(cs, partitioner, cuCtxChroma);
    
          qps[ChannelType::CHROMA] = cuCtxChroma.qp;
    
    }
    
    //================================================================================
    //  clause 7.3.8.3
    //--------------------------------------------------------------------------------
    //    void  sao             ( slice, ctuRsAddr )
    
    //    void  sao_block_params  ( saoPars, bitDepths, sliceEnabled, leftMergeAvail, aboveMergeAvail, onlyEstMergeInfo )
    //    void  sao_offset_params ( ctbPars, compID, sliceEnabled, bitDepth )
    
    //================================================================================
    
    void CABACWriter::sao( const Slice& slice, unsigned ctuRsAddr )
    {
      const SPS& sps = *slice.getSPS();
    
      if( !sps.getSAOEnabledFlag() )
    
      CodingStructure     &cs           = *slice.getPic()->cs;
      const PreCalcValues &pcv          = *cs.pcv;
      const SAOBlkParam   &saoCtuParams = cs.picture->getSAO()[ctuRsAddr];
    
    
      const bool sliceSaoLumaFlag = slice.getSaoEnabledFlag(ChannelType::LUMA);
    
      const bool sliceSaoChromaFlag =
    
        slice.getSaoEnabledFlag(ChannelType::CHROMA) && isChromaEnabled(sps.getChromaFormatIdc());
    
    
      if (!sliceSaoLumaFlag && !sliceSaoChromaFlag)
    
      const bool sliceEnabled[3] = { sliceSaoLumaFlag, sliceSaoChromaFlag, sliceSaoChromaFlag };
    
      const int frameWidthInCtus = pcv.widthInCtus;
    
      const int ry = ctuRsAddr / frameWidthInCtus;
      const int rx = ctuRsAddr - ry * frameWidthInCtus;
    
      const Position pos(rx * cs.pcv->maxCUWidth, ry * cs.pcv->maxCUHeight);
    
      const unsigned curSliceIdx = slice.getIndependentSliceIdx();
    
      const TileIdx  curTileIdx  = cs.pps->getTileIdx(pos);
    
    
      const bool leftMergeAvail =
    
        cs.getCURestricted(pos.offset(-(int) pcv.maxCUWidth, 0), pos, curSliceIdx, curTileIdx, ChannelType::LUMA)
        != nullptr;
    
      const bool aboveMergeAvail =
    
        cs.getCURestricted(pos.offset(0, -(int) pcv.maxCUHeight), pos, curSliceIdx, curTileIdx, ChannelType::LUMA)
        != nullptr;
    
    
      sao_block_params(saoCtuParams, sps.getBitDepths(), sliceEnabled, leftMergeAvail, aboveMergeAvail, false);
    
    void CABACWriter::sao_block_params(const SAOBlkParam &saoPars, const BitDepths &bitDepths, const bool *sliceEnabled,
                                       bool leftMergeAvail, bool aboveMergeAvail, bool onlyEstMergeInfo)
    
    {
      bool isLeftMerge  = false;
      bool isAboveMerge = false;
      if( leftMergeAvail )
      {
        // sao_merge_left_flag
    
        isLeftMerge = (saoPars[COMPONENT_Y].modeIdc == SAOMode::MERGE
                       && saoPars[COMPONENT_Y].typeIdc.mergeType == SAOModeMergeTypes::LEFT);
    
    Frank Bossen's avatar
    Frank Bossen committed
        m_binEncoder.encodeBin((isLeftMerge), Ctx::SaoMergeFlag());
    
      }
      if( aboveMergeAvail && !isLeftMerge )
      {
        // sao_merge_above_flag
    
        isAboveMerge = (saoPars[COMPONENT_Y].modeIdc == SAOMode::MERGE
                        && saoPars[COMPONENT_Y].typeIdc.mergeType == SAOModeMergeTypes::ABOVE);
    
    Frank Bossen's avatar
    Frank Bossen committed
        m_binEncoder.encodeBin((isAboveMerge), Ctx::SaoMergeFlag());
    
      }
      if( onlyEstMergeInfo )
      {
        return; //only for RDO
      }
      if( !isLeftMerge && !isAboveMerge )
      {
        // explicit parameters
        for( int compIdx=0; compIdx < MAX_NUM_COMPONENT; compIdx++ )
        {
    
          sao_offset_params(saoPars[compIdx], ComponentID(compIdx), sliceEnabled[compIdx],
    
                            bitDepths[toChannelType(ComponentID(compIdx))]);
    
    void CABACWriter::sao_offset_params(const SAOOffset &ctbPars, ComponentID compID, bool sliceEnabled, int bitDepth)
    
        CHECK(ctbPars.modeIdc != SAOMode::OFF, "Sao must be off, if it is disabled on slice level");
    
        return;
      }
      const bool isFirstCompOfChType = ( getFirstComponentOfChannel( toChannelType(compID) ) == compID );
    
      if( isFirstCompOfChType )
      {
        // sao_type_idx_luma / sao_type_idx_chroma
    
        if (ctbPars.modeIdc == SAOMode::OFF)
    
    Frank Bossen's avatar
    Frank Bossen committed
          m_binEncoder.encodeBin(0, Ctx::SaoTypeIdx());
    
        else if (ctbPars.typeIdc.newType == SAOModeNewTypes::BO)
    
    Frank Bossen's avatar
    Frank Bossen committed
          m_binEncoder.encodeBin(1, Ctx::SaoTypeIdx());
          m_binEncoder.encodeBinEP(0);
    
          CHECK(!(ctbPars.typeIdc.newType < SAOModeNewTypes::START_BO), "Unspecified error");
    
    Frank Bossen's avatar
    Frank Bossen committed
          m_binEncoder.encodeBin(1, Ctx::SaoTypeIdx());
          m_binEncoder.encodeBinEP(1);
    
      if (ctbPars.modeIdc == SAOMode::NEW)
    
      {
        const int maxOffsetQVal = SampleAdaptiveOffset::getMaxOffsetQVal( bitDepth );
    
        int       numClasses    = (ctbPars.typeIdc.newType == SAOModeNewTypes::BO ? 4 : NUM_SAO_EO_CLASSES);
    
        int       k             = 0;
        int       offset[4];
        for( int i = 0; i < numClasses; i++ )
        {
    
          if (ctbPars.typeIdc.newType != SAOModeNewTypes::BO && i == SAO_CLASS_EO_PLAIN)
    
          int classIdx =
            (ctbPars.typeIdc.newType == SAOModeNewTypes::BO ? (ctbPars.typeAuxInfo + i) % NUM_SAO_BO_CLASSES : i);
    
          offset[k++]  = ctbPars.offset[classIdx];
        }
    
        // sao_offset_abs
        for( int i = 0; i < 4; i++ )
        {
          unsigned absOffset = ( offset[i] < 0 ? -offset[i] : offset[i] );
          unary_max_eqprob( absOffset, maxOffsetQVal );
        }
    
        // band offset mode
    
        if (ctbPars.typeIdc.newType == SAOModeNewTypes::BO)
    
        {
          // sao_offset_sign
          for( int i = 0; i < 4; i++ )
          {
            if( offset[i] )
            {
    
    Frank Bossen's avatar
    Frank Bossen committed
              m_binEncoder.encodeBinEP((offset[i] < 0));
    
    Frank Bossen's avatar
    Frank Bossen committed
          m_binEncoder.encodeBinsEP(ctbPars.typeAuxInfo, NUM_SAO_BO_CLASSES_LOG2);
    
        }
        // edge offset mode
        else
        {
          if( isFirstCompOfChType )
          {
            // sao_eo_class_luma / sao_eo_class_chroma
    
            CHECK(ctbPars.typeIdc.newType < SAOModeNewTypes::START_EO, "sao edge offset class is outside valid range");
            m_binEncoder.encodeBinsEP(to_underlying(ctbPars.typeIdc.newType) - to_underlying(SAOModeNewTypes::START_EO),
                                      NUM_SAO_EO_TYPES_LOG2);
    
          }
        }
      }
    }
    
    //================================================================================
    //  clause 7.3.8.4
    //--------------------------------------------------------------------------------
    //    void  coding_tree       ( cs, partitioner, cuCtx )
    //    void  split_cu_flag     ( split, cs, partitioner )
    //    void  split_cu_mode_mt  ( split, cs, partitioner )
    //================================================================================
    
    void CABACWriter::coding_tree(const CodingStructure& cs, Partitioner& partitioner, CUCtx& cuCtx, Partitioner* pPartitionerChroma, CUCtx* pCuCtxChroma)
    {
      const PPS      &pps         = *cs.pps;
      const UnitArea &currArea    = partitioner.currArea();
    
      const CodingUnit &cu          = *cs.getCU(currArea.block(partitioner.chType), partitioner.chType);
    
    
      // Reset delta QP coding flag and ChromaQPAdjustemt coding flag
    
    Yin Zhao's avatar
    Yin Zhao committed
      //Note: do not reset qg at chroma CU
      if( pps.getUseDQP() && partitioner.currQgEnable() && !isChroma( partitioner.chType ) )
    
      {
        cuCtx.qgStart    = true;
        cuCtx.isDQPCoded          = false;
      }
      if( cs.slice->getUseChromaQpAdj() && partitioner.currQgChromaEnable() )
    
      {
        cuCtx.isChromaQpAdjCoded  = false;
      }
      // Reset delta QP coding flag and ChromaQPAdjustemt coding flag
      if (CS::isDualITree(cs) && pPartitionerChroma != nullptr)
      {
    
        if (pps.getUseDQP() && pPartitionerChroma->currQgEnable())
        {
          pCuCtxChroma->qgStart    = true;
          pCuCtxChroma->isDQPCoded = false;
        }
        if (cs.slice->getUseChromaQpAdj() && pPartitionerChroma->currQgChromaEnable())
    
    Adam Wieckowski's avatar
    Adam Wieckowski committed
      const PartSplit splitMode = CU::getSplitAtDepth( cu, partitioner.currDepth );
    
      split_cu_mode( splitMode, cs, partitioner );
    
      CHECK( !partitioner.canSplit( splitMode, cs ), "The chosen split mode is invalid!" );
    
      if( splitMode != CU_DONT_SPLIT )
      {
    
        if (CS::isDualITree(cs) && pPartitionerChroma != nullptr
            && (partitioner.currArea().lwidth() >= 64 || partitioner.currArea().lheight() >= 64))
        {
          partitioner.splitCurrArea(CU_QUAD_SPLIT, cs);
          pPartitionerChroma->splitCurrArea(CU_QUAD_SPLIT, cs);
          bool beContinue     = true;
          bool lumaContinue   = true;
          bool chromaContinue = true;
    
          while (beContinue)
          {
            if (partitioner.currArea().lwidth() > 64 || partitioner.currArea().lheight() > 64)
    
              if (cs.picture->block(partitioner.chType).contains(partitioner.currArea().block(partitioner.chType).pos()))
    
                coding_tree(cs, partitioner, cuCtx, pPartitionerChroma, pCuCtxChroma);
    
              lumaContinue   = partitioner.nextPart(cs);
              chromaContinue = pPartitionerChroma->nextPart(cs);
              CHECK(lumaContinue != chromaContinue, "luma chroma partition should be matched");
              beContinue = lumaContinue;
            }
            else
            {
              // dual tree coding under 64x64 block
    
              if (cs.picture->block(partitioner.chType).contains(partitioner.currArea().block(partitioner.chType).pos()))
    
                coding_tree(cs, partitioner, cuCtx);
              }
              lumaContinue = partitioner.nextPart(cs);
    
              if (cs.picture->block(pPartitionerChroma->chType)
                    .contains(pPartitionerChroma->currArea().block(pPartitionerChroma->chType).pos()))
    
              {
                coding_tree(cs, *pPartitionerChroma, *pCuCtxChroma);
    
              chromaContinue = pPartitionerChroma->nextPart(cs);
              CHECK(lumaContinue != chromaContinue, "luma chroma partition should be matched");
              beContinue = lumaContinue;
    
          partitioner.exitCurrSplit();
          pPartitionerChroma->exitCurrSplit();
        }
        else
        {
          const ModeType modeTypeParent = partitioner.modeType;
          const ModeType modeTypeChild  = CU::getModeTypeAtDepth(cu, partitioner.currDepth);
          mode_constraint(splitMode, cs, partitioner, modeTypeChild);
          partitioner.modeType = modeTypeChild;
    
          bool chromaNotSplit = modeTypeParent == MODE_TYPE_ALL && modeTypeChild == MODE_TYPE_INTRA ? true : false;
    
          CHECK(chromaNotSplit && partitioner.chType != ChannelType::LUMA, "chType must be luma");
    
          if (partitioner.treeType == TREE_D)
          {
            partitioner.treeType = chromaNotSplit ? TREE_L : TREE_D;
          }
    
    Adam Wieckowski's avatar
    Adam Wieckowski committed
          partitioner.splitCurrArea( splitMode, cs );
    
            if (cs.picture->block(partitioner.chType).contains(partitioner.currArea().block(partitioner.chType).pos()))
    
            {
              coding_tree( cs, partitioner, cuCtx );
            }
          } while( partitioner.nextPart( cs ) );
    
          partitioner.exitCurrSplit();
    
            if (isChromaEnabled(cs.pcv->chrFormat))
            {
    
              CHECK(partitioner.chType != ChannelType::LUMA, "must be luma status");
              partitioner.chType   = ChannelType::CHROMA;
    
              partitioner.treeType = TREE_C;
    
              if (cs.picture->block(partitioner.chType).contains(partitioner.currArea().block(partitioner.chType).pos()))
    
              {
                coding_tree(cs, partitioner, cuCtx);
              }
    
            partitioner.chType   = ChannelType::LUMA;
    
            partitioner.treeType = TREE_D;
          }
          partitioner.modeType = modeTypeParent;
    
        }
        return;
    
      if( cuCtx.qgStart )
      {
        cuCtx.qgStart = false;
    
      CHECK( cu.treeType != partitioner.treeType, "treeType mismatch" );
    
      if (cu.chType == ChannelType::CHROMA)
    
    Yin Zhao's avatar
    Yin Zhao committed
      {
        DTRACE_COND( (isEncoding()), g_trace_ctx, D_QP, "[chroma CU]x=%d, y=%d, w=%d, h=%d, qp=%d\n", cu.Cb().x, cu.Cb().y, cu.Cb().width, cu.Cb().height, cu.qp );
      }
      else
      {
    
        DTRACE_COND((isEncoding()), g_trace_ctx, D_QP, "x=%d, y=%d, w=%d, h=%d, qp=%d\n", cu.Y().x, cu.Y().y, cu.Y().width,
                    cu.Y().height, cu.qp);
    
      DTRACE_BLOCK_REC_COND( ( !isEncoding() ), cs.picture->getRecoBuf( cu ), cu, cu.predMode );
    }
    
    
    void CABACWriter::mode_constraint( const PartSplit split, const CodingStructure& cs, Partitioner& partitioner, const ModeType modeType )
    {
      CHECK( split == CU_DONT_SPLIT, "splitMode shall not be no split" );
      int val = cs.signalModeCons( split, partitioner, partitioner.modeType );
    
      if( val == LDT_MODE_TYPE_SIGNAL )
    
      {
        CHECK( modeType == MODE_TYPE_ALL, "shall not be no constraint case" );
        bool flag = modeType == MODE_TYPE_INTRA;
        int ctxIdx = DeriveCtx::CtxModeConsFlag( cs, partitioner );
    
    Frank Bossen's avatar
    Frank Bossen committed
        m_binEncoder.encodeBin(flag, Ctx::ModeConsFlag(ctxIdx));
    
        DTRACE( g_trace_ctx, D_SYNTAX, "mode_cons_flag() flag=%d\n", flag );
      }
    
      else if( val == LDT_MODE_TYPE_INFER )
    
      {
        assert( modeType == MODE_TYPE_INTRA );
      }
      else
      {
        assert( modeType == partitioner.modeType );
      }
    }
    
    
    Adam Wieckowski's avatar
    Adam Wieckowski committed
    void CABACWriter::split_cu_mode( const PartSplit split, const CodingStructure& cs, Partitioner& partitioner )
    {
      bool canNo, canQt, canBh, canBv, canTh, canTv;
      partitioner.canSplit( cs, canNo, canQt, canBh, canBv, canTh, canTv );
    
      bool canSpl[6] = { canNo, canQt, canBh, canBv, canTh, canTv };
    
      unsigned ctxSplit = 0, ctxQtSplit = 0, ctxBttHV = 0, ctxBttH12 = 0, ctxBttV12;
      DeriveCtx::CtxSplit( cs, partitioner, ctxSplit, ctxQtSplit, ctxBttHV, ctxBttH12, ctxBttV12, canSpl );
    
    Adam Wieckowski's avatar
    Adam Wieckowski committed
      const bool canSplit = canBh || canBv || canTh || canTv || canQt;
      const bool isNo     = split == CU_DONT_SPLIT;
    
      if( canNo && canSplit )
      {
    
    Frank Bossen's avatar
    Frank Bossen committed
        m_binEncoder.encodeBin(!isNo, Ctx::SplitFlag(ctxSplit));
    
    Adam Wieckowski's avatar
    Adam Wieckowski committed
      }
    
      DTRACE( g_trace_ctx, D_SYNTAX, "split_cu_mode() ctx=%d split=%d\n", ctxSplit, !isNo );
    
      if( isNo )
      {
        return;
      }
    
      const bool canBtt = canBh || canBv || canTh || canTv;
      const bool isQt   = split == CU_QUAD_SPLIT;
    
      if( canQt && canBtt )
      {
    
    Frank Bossen's avatar
    Frank Bossen committed
        m_binEncoder.encodeBin(isQt, Ctx::SplitQtFlag(ctxQtSplit));
    
    Adam Wieckowski's avatar
    Adam Wieckowski committed
      }
    
      DTRACE( g_trace_ctx, D_SYNTAX, "split_cu_mode() ctx=%d qt=%d\n", ctxQtSplit, isQt );
    
      if( isQt )
      {
        return;
      }
    
      const bool canHor = canBh || canTh;
      const bool canVer = canBv || canTv;
      const bool  isVer = split == CU_VERT_SPLIT || split == CU_TRIV_SPLIT;
    
      if( canVer && canHor )
      {
    
    Frank Bossen's avatar
    Frank Bossen committed
        m_binEncoder.encodeBin(isVer, Ctx::SplitHvFlag(ctxBttHV));
    
    Adam Wieckowski's avatar
    Adam Wieckowski committed
      }
    
      const bool can14 = isVer ? canTv : canTh;
      const bool can12 = isVer ? canBv : canBh;
      const bool  is12 = isVer ? ( split == CU_VERT_SPLIT ) : ( split == CU_HORZ_SPLIT );
    
      if( can12 && can14 )
      {
    
    Frank Bossen's avatar
    Frank Bossen committed
        m_binEncoder.encodeBin(is12, Ctx::Split12Flag(isVer ? ctxBttV12 : ctxBttH12));
    
    Adam Wieckowski's avatar
    Adam Wieckowski committed
      }
    
      DTRACE( g_trace_ctx, D_SYNTAX, "split_cu_mode() ctxHv=%d ctx12=%d mode=%d\n", ctxBttHV, isVer ? ctxBttV12 : ctxBttH12, split );
    }
    
    
    //================================================================================
    //  clause 7.3.8.5
    //--------------------------------------------------------------------------------
    //    void  coding_unit               ( cu, partitioner, cuCtx )
    //    void  cu_skip_flag              ( cu )
    //    void  pred_mode                 ( cu )
    //    void  part_mode                 ( cu )
    //    void  cu_pred_data              ( pus )
    //    void  cu_lic_flag               ( cu )
    //    void  intra_luma_pred_modes     ( pus )
    //    void  intra_chroma_pred_mode    ( pu )
    //    void  cu_residual               ( cu, partitioner, cuCtx )
    //    void  rqt_root_cbf              ( cu )
    //    void  end_of_ctu                ( cu, cuCtx )
    //================================================================================
    
    void CABACWriter::coding_unit( const CodingUnit& cu, Partitioner& partitioner, CUCtx& cuCtx )
    {
    
      DTRACE( g_trace_ctx, D_SYNTAX, "coding_unit() treeType=%d modeType=%d\n", cu.treeType, cu.modeType );
    
    Yu Han's avatar
    Yu Han committed
      if ((!cs.slice->isIntra() || cs.slice->getSPS()->getIBCFlag()) && cu.Y().valid())
    
      {
        cu_skip_flag( cu );
      }
    
    
      // skip data
      if( cu.skip )
      {
        CHECK( !cu.firstPU->mergeFlag, "Merge flag has to be on!" );
    
        CHECK(cu.colorTransform, "ACT should not be enabled for skip mode");
    
        PredictionUnit&   pu = *cu.firstPU;
        prediction_unit ( pu );
        end_of_ctu      ( cu, cuCtx );
        return;
      }
    
      // prediction mode and partitioning data
      pred_mode ( cu );
    
      if (CU::isIntra(cu))
      {
        adaptive_color_transform(cu);
      }
    
        CHECK(cu.colorTransform, "ACT should not be enabled for PLT mode");
    
        {
          if (isLuma(partitioner.chType))
          {
            cu_palette_info(cu, COMPONENT_Y, 1, cuCtx);
          }
    
          if (isChromaEnabled(cu.chromaFormat) && partitioner.chType == ChannelType::CHROMA)
    
          {
            cu_palette_info(cu, COMPONENT_Cb, 2, cuCtx);
          }
        }
        else
        {
    
          cu_palette_info(cu, COMPONENT_Y, getNumberValidComponents(cu.chromaFormat), cuCtx);
    
    
      // prediction data ( intra prediction modes / reference indexes + motion vectors )
      cu_pred_data( cu );
    
      // residual data ( coded block flags + transform coefficient levels )
      cu_residual( cu, partitioner, cuCtx );
    
      // end of cu
      end_of_ctu( cu, cuCtx );
    }
    
    void CABACWriter::cu_skip_flag( const CodingUnit& cu )
    {
      unsigned ctxId = DeriveCtx::CtxSkipFlag( cu );
    
      if ((cu.slice->isIntra() || cu.isConsIntra()) && cu.cs->slice->getSPS()->getIBCFlag())
    
        if (CU::canUseIbc(cu))   // disable IBC mode larger than 64x64
    
    Yu Han's avatar
    Yu Han committed
        {
    
    Frank Bossen's avatar
    Frank Bossen committed
          m_binEncoder.encodeBin((cu.skip), Ctx::SkipFlag(ctxId));
    
          DTRACE(g_trace_ctx, D_SYNTAX, "cu_skip_flag() ctx=%d skip=%d\n", ctxId, cu.skip ? 1 : 0);
    
    Yu Han's avatar
    Yu Han committed
        }
    
    Yu Han's avatar
    Yu Han committed
        return;
      }
    
      if ( !cu.cs->slice->getSPS()->getIBCFlag() && cu.lwidth() == 4 && cu.lheight() == 4 )
      {
        return;
      }
    
      if( !cu.cs->slice->getSPS()->getIBCFlag() && cu.isConsIntra() )
      {
        return;
      }
    
    Frank Bossen's avatar
    Frank Bossen committed
      m_binEncoder.encodeBin((cu.skip), Ctx::SkipFlag(ctxId));
    
    
      DTRACE( g_trace_ctx, D_SYNTAX, "cu_skip_flag() ctx=%d skip=%d\n", ctxId, cu.skip ? 1 : 0 );
    
    Yu Han's avatar
    Yu Han committed
      if (cu.skip && cu.cs->slice->getSPS()->getIBCFlag())
    
        // disable IBC mode larger than 64x64 and disable IBC when only allowing inter mode
    
        if (CU::canUseIbc(cu) && !cu.isConsInter())
    
    Yu Han's avatar
    Yu Han committed
        {
    
          if ( cu.lwidth() == 4 && cu.lheight() == 4 )
          {
            return;
          }
    
          unsigned ctxidx = DeriveCtx::CtxIBCFlag(cu);
    
    Frank Bossen's avatar
    Frank Bossen committed
          m_binEncoder.encodeBin(CU::isIBC(cu) ? 1 : 0, Ctx::IBCFlag(ctxidx));
    
          DTRACE(g_trace_ctx, D_SYNTAX, "ibc() ctx=%d cu.predMode=%d\n", ctxidx, cu.predMode);
    
    Yu Han's avatar
    Yu Han committed
        }
    
      if (cu.cs->slice->getSPS()->getIBCFlag() && cu.chType != ChannelType::CHROMA)
    
        if( cu.isConsInter() )
        {
          assert( CU::isInter( cu ) );
          return;
        }
    
        if ( cu.cs->slice->isIntra() || ( cu.lwidth() == 4 && cu.lheight() == 4 ) || cu.isConsIntra() )
    
          if (CU::canUseIbc(cu))
    
    Yu Han's avatar
    Yu Han committed
          {
    
            unsigned ctxidx = DeriveCtx::CtxIBCFlag(cu);
    
    Frank Bossen's avatar
    Frank Bossen committed
            m_binEncoder.encodeBin(CU::isIBC(cu), Ctx::IBCFlag(ctxidx));
    
    Yu Han's avatar
    Yu Han committed
          }
    
          if (!CU::isIBC(cu) && cu.cs->slice->getSPS()->getPLTMode() && cu.lwidth() <= 64 && cu.lheight() <= 64 && (cu.lumaSize().width * cu.lumaSize().height > 16) )
    
    Frank Bossen's avatar
    Frank Bossen committed
            m_binEncoder.encodeBin(CU::isPLT(cu), Ctx::PLTFlag(0));
    
          if( cu.isConsInter() )
          {
            return;
          }
    
    Frank Bossen's avatar
    Frank Bossen committed
          m_binEncoder.encodeBin((CU::isIntra(cu) || CU::isPLT(cu)), Ctx::PredMode(DeriveCtx::CtxPredModeFlag(cu)));
    
          if (CU::isIntra(cu) || CU::isPLT(cu))
          {
    
            if (cu.cs->slice->getSPS()->getPLTMode() && cu.lwidth() <= 64 && cu.lheight() <= 64 && (cu.lumaSize().width * cu.lumaSize().height > 16) )
    
    Frank Bossen's avatar
    Frank Bossen committed
              m_binEncoder.encodeBin(CU::isPLT(cu), Ctx::PLTFlag(0));
    
            if (CU::canUseIbc(cu))   // disable IBC mode larger than 64x64
    
    Yu Han's avatar
    Yu Han committed
            {
    
              unsigned ctxidx = DeriveCtx::CtxIBCFlag(cu);
    
    Frank Bossen's avatar
    Frank Bossen committed
              m_binEncoder.encodeBin(CU::isIBC(cu), Ctx::IBCFlag(ctxidx));
    
    Yu Han's avatar
    Yu Han committed
            }
    
          assert( CU::isInter( cu ) );
    
    
        if ( cu.cs->slice->isIntra() || ( cu.lwidth() == 4 && cu.lheight() == 4 ) || cu.isConsIntra() )
    
          if (cu.cs->slice->getSPS()->getPLTMode() && cu.lwidth() <= 64 && cu.lheight() <= 64 && ( ( (!isLuma(cu.chType)) && (cu.chromaSize().width * cu.chromaSize().height > 16) ) || ((isLuma(cu.chType)) && ((cu.lumaSize().width * cu.lumaSize().height) > 16 ) )  ) && (!cu.isLocalSepTree() || isLuma(cu.chType)  ) )
    
    Frank Bossen's avatar
    Frank Bossen committed
            m_binEncoder.encodeBin((CU::isPLT(cu)), Ctx::PLTFlag(0));
    
    Yu Han's avatar
    Yu Han committed
          return;
        }
    
    Frank Bossen's avatar
    Frank Bossen committed
        m_binEncoder.encodeBin((CU::isIntra(cu) || CU::isPLT(cu)), Ctx::PredMode(DeriveCtx::CtxPredModeFlag(cu)));
    
        if ((CU::isIntra(cu) || CU::isPLT(cu)) && cu.cs->slice->getSPS()->getPLTMode() && cu.lwidth() <= 64 && cu.lheight() <= 64 && ( ( (!isLuma(cu.chType)) && (cu.chromaSize().width * cu.chromaSize().height > 16) ) || ((isLuma(cu.chType)) && ((cu.lumaSize().width * cu.lumaSize().height) > 16 ) )  ) && (!cu.isLocalSepTree() || isLuma(cu.chType)  )  )
    
    Frank Bossen's avatar
    Frank Bossen committed
          m_binEncoder.encodeBin((CU::isPLT(cu)), Ctx::PLTFlag(0));
    
    void CABACWriter::bdpcm_mode( const CodingUnit& cu, const ComponentID compID )
    {
    
      if (!cu.cs->sps->getBDPCMEnabledFlag())
      {
        return;
      }
      if (!CU::bdpcmAllowed(cu, compID))
      {
        return;
      }
    
      const BdpcmMode bdpcmMode = cu.getBdpcmMode(compID);
    
      unsigned ctxId = isLuma(compID) ? 0 : 2;
    
      m_binEncoder.encodeBin(bdpcmMode != BdpcmMode::NONE ? 1 : 0, Ctx::BDPCMMode(ctxId));
    
      if (bdpcmMode != BdpcmMode::NONE)
    
        m_binEncoder.encodeBin(bdpcmMode != BdpcmMode::HOR ? 1 : 0, Ctx::BDPCMMode(ctxId + 1));
    
      }
      if (isLuma(compID))
      {
    
        DTRACE(g_trace_ctx, D_SYNTAX, "bdpcm_mode(%d) x=%d, y=%d, w=%d, h=%d, bdpcm=%d\n", ChannelType::LUMA,
               cu.lumaPos().x, cu.lumaPos().y, cu.lwidth(), cu.lheight(), cu.bdpcmMode);
    
        DTRACE(g_trace_ctx, D_SYNTAX, "bdpcm_mode(%d) x=%d, y=%d, w=%d, h=%d, bdpcm=%d\n", ChannelType::CHROMA,
               cu.chromaPos().x, cu.chromaPos().y, cu.chromaSize().width, cu.chromaSize().height, cu.bdpcmModeChroma);
    
    
    void CABACWriter::cu_pred_data( const CodingUnit& cu )
    {
      if( CU::isIntra( cu ) )
      {
    
        if( cu.Y().valid() )
        {
          bdpcm_mode( cu, COMPONENT_Y );
        }
    
    
        if( ( !cu.Y().valid() || ( !cu.isSepTree() && cu.Y().valid() ) ) && isChromaEnabled(cu.chromaFormat) )
    
          bdpcm_mode(cu, ComponentID(ChannelType::CHROMA));
    
    Xiaozhong Xu's avatar
    Xiaozhong Xu committed
      if (!cu.Y().valid()) // dual tree chroma CU
      {
        return;
      }
    
      for( auto &pu : CU::traversePUs( cu ) )
      {
        prediction_unit( pu );
      }
    
      imv_mode   ( cu );
    
      affine_amvr_mode( cu );
    
    void CABACWriter::cu_bcw_flag(const CodingUnit& cu)
    
      if(!CU::isBcwIdxCoded(cu))
    
      CHECK(!(BCW_NUM > 1 && (BCW_NUM == 2 || (BCW_NUM & 0x01) == 1)), " !( BCW_NUM > 1 && ( BCW_NUM == 2 || ( BCW_NUM & 0x01 ) == 1 ) ) ");
      const uint8_t bcwCodingIdx = (uint8_t)g_BcwCodingOrder[CU::getValidBcwIdx(cu)];
    
      const int32_t numBcw = (cu.slice->getCheckLDC()) ? 5 : 3;
    
    Frank Bossen's avatar
    Frank Bossen committed
      m_binEncoder.encodeBin((bcwCodingIdx == 0 ? 0 : 1), Ctx::bcwIdx(0));
    
      if(numBcw > 2 && bcwCodingIdx != 0)
    
        const uint32_t prefixNumBits = numBcw - 2;
    
        const uint32_t step = 1;
    
    
        uint8_t idx = 1;
        for(int ui = 0; ui < prefixNumBits; ++ui)
        {
    
          if (bcwCodingIdx == idx)
    
    Frank Bossen's avatar
    Frank Bossen committed
            m_binEncoder.encodeBinEP(0);
    
    Frank Bossen's avatar
    Frank Bossen committed
            m_binEncoder.encodeBinEP(1);
    
      DTRACE(g_trace_ctx, D_SYNTAX, "cu_bcw_flag() bcw_idx=%d\n", cu.bcwIdx ? 1 : 0);
    
    void CABACWriter::xWriteTruncBinCode(const uint32_t symbol, const uint32_t numSymbols)
    
      CHECKD(symbol >= numSymbols, "symbol must be less than numSymbols");
    
      const int thresh = floorLog2(numSymbols);
    
      const int val = 1 << thresh;
    
      const int b = numSymbols - val;
    
    ling's avatar
    ling committed
      if (symbol < val - b)
    
    Frank Bossen's avatar
    Frank Bossen committed
        m_binEncoder.encodeBinsEP(symbol, thresh);
    
        m_binEncoder.encodeBinsEP(symbol + val - b, thresh + 1);
    
    void CABACWriter::extend_ref_line(const PredictionUnit& pu)
    {
    
      const CodingUnit& cu = *pu.cu;
    
      if (!cu.Y().valid() || !CU::isIntra(cu) || !isLuma(cu.chType) || cu.bdpcmMode != BdpcmMode::NONE)
    
      if( !cu.cs->sps->getUseMRL() )
      {
        return;
      }
    
      bool isFirstLineOfCtu = (((cu.block(COMPONENT_Y).y)&((cu.cs->sps)->getMaxCUWidth() - 1)) == 0);
      if (isFirstLineOfCtu)
      {
        return;
      }
      int multiRefIdx = pu.multiRefIdx;
      if (MRL_NUM_REF_LINES > 1)
      {
    
    Frank Bossen's avatar
    Frank Bossen committed
        m_binEncoder.encodeBin(multiRefIdx != MULTI_REF_LINE_IDX[0], Ctx::MultiRefLineIdx(0));
    
        if (MRL_NUM_REF_LINES > 2 && multiRefIdx != MULTI_REF_LINE_IDX[0])
        {
    
    Frank Bossen's avatar
    Frank Bossen committed
          m_binEncoder.encodeBin(multiRefIdx != MULTI_REF_LINE_IDX[1], Ctx::MultiRefLineIdx(1));
    
        }
      }
    }
    
    void CABACWriter::extend_ref_line(const CodingUnit& cu)
    {
    
      if (!cu.Y().valid() || !CU::isIntra(cu) || !isLuma(cu.chType) || cu.bdpcmMode != BdpcmMode::NONE)
    
      if( !cu.cs->sps->getUseMRL() )
      {
        return;
      }
    
    
      const int numBlocks = CU::getNumPUs(cu);
      const PredictionUnit* pu = cu.firstPU;
    
      for (int k = 0; k < numBlocks; k++)
      {
        bool isFirstLineOfCtu = (((cu.block(COMPONENT_Y).y)&((cu.cs->sps)->getMaxCUWidth() - 1)) == 0);
        if (isFirstLineOfCtu)
        {
          return;
        }
        int multiRefIdx = pu->multiRefIdx;
        if (MRL_NUM_REF_LINES > 1)
        {
    
    Frank Bossen's avatar
    Frank Bossen committed
          m_binEncoder.encodeBin(multiRefIdx != MULTI_REF_LINE_IDX[0], Ctx::MultiRefLineIdx(0));
    
          if (MRL_NUM_REF_LINES > 2 && multiRefIdx != MULTI_REF_LINE_IDX[0])
          {
    
    Frank Bossen's avatar
    Frank Bossen committed
            m_binEncoder.encodeBin(multiRefIdx != MULTI_REF_LINE_IDX[1], Ctx::MultiRefLineIdx(1));