Skip to content
Snippets Groups Projects
IntraSearch.cpp 67.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • // Intra search
    // -------------------------------------------------------------------------------------------------------------------
    
    void IntraSearch::xEncIntraHeader(CodingStructure &cs, Partitioner &partitioner, const bool &bLuma, const bool &bChroma)
    {
      CodingUnit &cu = *cs.getCU( partitioner.chType );
    
      if (bLuma)
      {
        bool isFirst = partitioner.currArea().lumaPos() == cs.area.lumaPos();
    
        // CU header
        if( isFirst )
        {
    
    Yu Han's avatar
    Yu Han committed
    #if JVET_M0483_IBC 
    
    Yu Han's avatar
    Yu Han committed
          if ((!cs.slice->isIntra() || cs.slice->getSPS()->getSpsNext().getIBCMode())
    #else
    
    Yu Han's avatar
    Yu Han committed
    #endif
    
    Xiaozhong Xu's avatar
    Xiaozhong Xu committed
            && cu.Y().valid()
    
          {
            if( cs.pps->getTransquantBypassEnabledFlag() )
            {
              m_CABACEstimator->cu_transquant_bypass_flag( cu );
            }
            m_CABACEstimator->cu_skip_flag( cu );
            m_CABACEstimator->pred_mode   ( cu );
          }
    
          m_CABACEstimator->extend_ref_line(cu);
    
    Karsten Suehring's avatar
    Karsten Suehring committed
          if( CU::isIntra(cu) )
    
            m_CABACEstimator->pcm_data( cu, partitioner );
    
            if( cu.ipcm )
            {
              return;
            }
          }
        }
    
        PredictionUnit &pu = *cs.getPU(partitioner.currArea().lumaPos(), partitioner.chType);
    
        // luma prediction mode
    
    Karsten Suehring's avatar
    Karsten Suehring committed
        if (isFirst)
    
    Karsten Suehring's avatar
    Karsten Suehring committed
          if ( !cu.Y().valid())
            m_CABACEstimator->pred_mode( cu );
          m_CABACEstimator->intra_luma_pred_mode( pu );
    
        }
      }
    
      if (bChroma)
      {
        bool isFirst = partitioner.currArea().Cb().valid() && partitioner.currArea().chromaPos() == cs.area.chromaPos();
    
        PredictionUnit &pu = *cs.getPU( partitioner.currArea().chromaPos(), CHANNEL_TYPE_CHROMA );
    
    
    Karsten Suehring's avatar
    Karsten Suehring committed
        if( isFirst )
    
    Karsten Suehring's avatar
    Karsten Suehring committed
          m_CABACEstimator->intra_chroma_pred_mode( pu );
    
        }
      }
    }
    
    void IntraSearch::xEncSubdivCbfQT(CodingStructure &cs, Partitioner &partitioner, const bool &bLuma, const bool &bChroma)
    {
      const UnitArea &currArea = partitioner.currArea();
      TransformUnit &currTU    = *cs.getTU( currArea.blocks[partitioner.chType], partitioner.chType );
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #if !JVET_M0464_UNI_MTS
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #endif
    
      uint32_t currDepth           = partitioner.currTrDepth;
    
      const bool subdiv        = currTU.depth > currDepth;
    
    
    Karsten Suehring's avatar
    Karsten Suehring committed
      if( partitioner.canSplit( TU_MAX_TR_SPLIT, cs ) )
    
    Karsten Suehring's avatar
    Karsten Suehring committed
        CHECK( !subdiv, "TU split implied" );
      }
      else
      {
        CHECK( subdiv, "No TU subdivision is allowed with QTBT" );
    
      }
    
      if (bChroma)
      {
        const uint32_t numberValidComponents = getNumberValidComponents(currArea.chromaFormat);
    
        for (uint32_t ch = COMPONENT_Cb; ch < numberValidComponents; ch++)
        {
          const ComponentID compID = ComponentID(ch);
    
          if( currDepth == 0 || TU::getCbfAtDepth( currTU, compID, currDepth - 1 ) )
          {
            const bool prevCbf = ( compID == COMPONENT_Cr ? TU::getCbfAtDepth( currTU, COMPONENT_Cb, currDepth ) : false );
            m_CABACEstimator->cbf_comp( cs, TU::getCbfAtDepth( currTU, compID, currDepth ), currArea.blocks[compID], currDepth, prevCbf );
    
          }
        }
      }
    
      if (subdiv)
      {
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #if !JVET_M0464_UNI_MTS
    
        if( currDepth == 0 && bLuma ) m_CABACEstimator->emt_cu_flag( currCU );
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #endif
    
    
        if( partitioner.canSplit( TU_MAX_TR_SPLIT, cs ) )
        {
          partitioner.splitCurrArea( TU_MAX_TR_SPLIT, cs );
        }
        else
        THROW( "Cannot perform an implicit split!" );
    
        do
        {
          xEncSubdivCbfQT( cs, partitioner, bLuma, bChroma );
        } while( partitioner.nextPart( cs ) );
    
        partitioner.exitCurrSplit();
      }
      else
      {
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #if !JVET_M0464_UNI_MTS
    
        if( currDepth == 0 && bLuma && TU::getCbfAtDepth( currTU, COMPONENT_Y, 0 ) ) m_CABACEstimator->emt_cu_flag( currCU );
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #endif
    
        //===== Cbfs =====
        if (bLuma)
        {
          m_CABACEstimator->cbf_comp( cs, TU::getCbfAtDepth( currTU, COMPONENT_Y, currDepth ), currTU.Y(), currTU.depth );
        }
      }
    }
    
    void IntraSearch::xEncCoeffQT(CodingStructure &cs, Partitioner &partitioner, const ComponentID &compID)
    {
      const UnitArea &currArea  = partitioner.currArea();
      TransformUnit &currTU     = *cs.getTU( currArea.blocks[partitioner.chType], partitioner.chType );
      uint32_t      currDepth       = partitioner.currTrDepth;
      const bool subdiv         = currTU.depth > currDepth;
    
      if (subdiv)
      {
        if (partitioner.canSplit(TU_MAX_TR_SPLIT, cs))
        {
          partitioner.splitCurrArea(TU_MAX_TR_SPLIT, cs);
        }
        else
          THROW("Implicit TU split not available!");
    
        do
        {
          xEncCoeffQT( cs, partitioner, compID );
        } while( partitioner.nextPart( cs ) );
    
        partitioner.exitCurrSplit();
      }
      else
    
      if( currArea.blocks[compID].valid() )
      {
        if( TU::hasCrossCompPredInfo( currTU, compID ) )
        {
          m_CABACEstimator->cross_comp_pred( currTU, compID );
        }
        if( TU::getCbf( currTU, compID ) )
        {
          m_CABACEstimator->residual_coding( currTU, compID );
        }
      }
    }
    
    uint64_t IntraSearch::xGetIntraFracBitsQT( CodingStructure &cs, Partitioner &partitioner, const bool &bLuma, const bool &bChroma )
    {
      m_CABACEstimator->resetBits();
    
      xEncIntraHeader( cs, partitioner, bLuma, bChroma );
      xEncSubdivCbfQT( cs, partitioner, bLuma, bChroma );
    
      if( bLuma )
      {
        xEncCoeffQT( cs, partitioner, COMPONENT_Y );
      }
      if( bChroma )
      {
        xEncCoeffQT( cs, partitioner, COMPONENT_Cb );
        xEncCoeffQT( cs, partitioner, COMPONENT_Cr );
      }
    
      uint64_t fracBits = m_CABACEstimator->getEstFracBits();
      return fracBits;
    }
    
    uint64_t IntraSearch::xGetIntraFracBitsQTChroma(TransformUnit& currTU, const ComponentID &compID)
    {
      m_CABACEstimator->resetBits();
    
      if( TU::hasCrossCompPredInfo( currTU, compID ) )
      {
        m_CABACEstimator->cross_comp_pred( currTU, compID );
      }
      if( TU::getCbf( currTU, compID ) )
      {
        m_CABACEstimator->residual_coding( currTU, compID );
      }
    
      uint64_t fracBits = m_CABACEstimator->getEstFracBits();
      return fracBits;
    }
    
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #if JVET_M0464_UNI_MTS
    void IntraSearch::xIntraCodingTUBlock(TransformUnit &tu, const ComponentID &compID, const bool &checkCrossCPrediction, Distortion& ruiDist, const int &default0Save1Load2, uint32_t* numSig, std::vector<TrMode>* trModes, const bool loadTr)
    #else
    
    void IntraSearch::xIntraCodingTUBlock(TransformUnit &tu, const ComponentID &compID, const bool &checkCrossCPrediction, Distortion& ruiDist, const int &default0Save1Load2, uint32_t* numSig )
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #endif
    
    {
      if (!tu.blocks[compID].valid())
      {
        return;
      }
    
      CodingStructure &cs                       = *tu.cs;
    
      const CompArea      &area                 = tu.blocks[compID];
      const SPS           &sps                  = *cs.sps;
      const PPS           &pps                  = *cs.pps;
    
      const ChannelType    chType               = toChannelType(compID);
      const int            bitDepth             = sps.getBitDepth(chType);
    
      PelBuf         piOrg                      = cs.getOrgBuf    (area);
      PelBuf         piPred                     = cs.getPredBuf   (area);
      PelBuf         piResi                     = cs.getResiBuf   (area);
      PelBuf         piOrgResi                  = cs.getOrgResiBuf(area);
      PelBuf         piReco                     = cs.getRecoBuf   (area);
    
      const PredictionUnit &pu                  = *cs.getPU(area.pos(), chType);
      const uint32_t           uiChFinalMode        = PU::getFinalIntraMode(pu, chType);
    
      const bool           bUseCrossCPrediction = pps.getPpsRangeExtension().getCrossComponentPredictionEnabledFlag() && isChroma( compID ) && PU::isChromaIntraModeCrossCheckMode( pu ) && checkCrossCPrediction;
      const bool           ccUseRecoResi        = m_pcEncCfg->getUseReconBasedCrossCPredictionEstimate();
    
    
      //===== init availability pattern =====
      PelBuf sharedPredTS( m_pSharedPredTransformSkip[compID], area );
      if( default0Save1Load2 != 2 )
      {
        const bool bUseFilteredPredictions = IntraPrediction::useFilteredIntraRefSamples( compID, pu, true, tu );
        initIntraPatternChType( *tu.cu, area, bUseFilteredPredictions );
    
        //===== get prediction signal =====
        if( compID != COMPONENT_Y && PU::isLMCMode( uiChFinalMode ) )
        {
          {
            xGetLumaRecPixels( pu, area );
          }
          predIntraChromaLM( compID, piPred, pu, area, uiChFinalMode );
        }
        else
        {
          predIntraAng( compID, piPred, pu, bUseFilteredPredictions );
        }
    
    
        // save prediction
        if( default0Save1Load2 == 1 )
        {
          sharedPredTS.copyFrom( piPred );
        }
      }
      else
      {
        // load prediction
        piPred.copyFrom( sharedPredTS );
      }
    
    
      DTRACE( g_trace_ctx, D_PRED, "@(%4d,%4d) [%2dx%2d] IMode=%d\n", tu.lx(), tu.ly(), tu.lwidth(), tu.lheight(), uiChFinalMode );
      //DTRACE_PEL_BUF( D_PRED, piPred, tu, tu.cu->predMode, COMPONENT_Y );
    
      //===== get residual signal =====
      piResi.copyFrom( piOrg  );
      piResi.subtract( piPred );
    
      if (pps.getPpsRangeExtension().getCrossComponentPredictionEnabledFlag() && isLuma(compID))
      {
        piOrgResi.copyFrom (piResi);
      }
    
      if (bUseCrossCPrediction)
      {
        if (xCalcCrossComponentPredictionAlpha(tu, compID, ccUseRecoResi) == 0)
        {
          return;
        }
        CrossComponentPrediction::crossComponentPrediction(tu, compID, cs.getResiBuf(tu.Y()), piResi, piResi, false);
      }
    
      //===== transform and quantization =====
      //--- init rate estimation arrays for RDOQ ---
      //--- transform and quantization           ---
      TCoeff uiAbsSum = 0;
    
      const QpParam cQP(tu, compID);
    
    #if RDOQ_CHROMA_LAMBDA
      m_pcTrQuant->selectLambda(compID);
    #endif
    
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #if JVET_M0464_UNI_MTS
      if( trModes )
      {
        m_pcTrQuant->transformNxN( tu, compID, cQP, trModes, CU::isIntra( *tu.cu ) ? m_pcEncCfg->getIntraMTSMaxCand() : m_pcEncCfg->getInterMTSMaxCand() );
        tu.mtsIdx = trModes->at(0).first;
      }
      m_pcTrQuant->transformNxN(tu, compID, cQP, uiAbsSum, m_CABACEstimator->getCtx(), loadTr);
    #else
    
      m_pcTrQuant->transformNxN(tu, compID, cQP, uiAbsSum, m_CABACEstimator->getCtx());
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #endif
    
    
    
      DTRACE( g_trace_ctx, D_TU_ABS_SUM, "%d: comp=%d, abssum=%d\n", DTRACE_GET_COUNTER( g_trace_ctx, D_TU_ABS_SUM ), compID, uiAbsSum );
    
    
      //--- inverse transform ---
      if (uiAbsSum > 0)
      {
        m_pcTrQuant->invTransformNxN(tu, compID, piResi, cQP);
      }
      else
      {
        piResi.fill(0);
      }
    
      //===== reconstruction =====
      if (bUseCrossCPrediction)
      {
        CrossComponentPrediction::crossComponentPrediction(tu, compID, cs.getResiBuf(tu.Y()), piResi, piResi, true);
      }
    
      piReco.reconstruct(piPred, piResi, cs.slice->clpRng( compID ));
    
      //===== update distortion =====
    #if WCG_EXT
      if( m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled() )
      {
        const CPelBuf orgLuma = cs.getOrgBuf( cs.area.blocks[COMPONENT_Y] );
        ruiDist += m_pcRdCost->getDistPart( piOrg, piReco, bitDepth, compID, DF_SSE_WTD, &orgLuma );
      }
      else
    #endif
      {
        ruiDist += m_pcRdCost->getDistPart( piOrg, piReco, bitDepth, compID, DF_SSE );
      }
    }
    
    void IntraSearch::xRecurIntraCodingLumaQT( CodingStructure &cs, Partitioner &partitioner )
    {
      const UnitArea &currArea = partitioner.currArea();
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #if !JVET_M0464_UNI_MTS
    
      const CodingUnit &cu     = *cs.getCU(currArea.lumaPos(), partitioner.chType);
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #endif
      uint32_t currDepth       = partitioner.currTrDepth;
    
      const PPS &pps           = *cs.pps;
      const bool keepResi      = pps.getPpsRangeExtension().getCrossComponentPredictionEnabledFlag() || KEEP_PRED_AND_RESI_SIGNALS;
      bool bCheckFull          = true;
      bool bCheckSplit         = false;
    
    Karsten Suehring's avatar
    Karsten Suehring committed
      bCheckFull               = !partitioner.canSplit( TU_MAX_TR_SPLIT, cs );
      bCheckSplit              = partitioner.canSplit( TU_MAX_TR_SPLIT, cs );
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #if JVET_M0464_UNI_MTS
      double     dSingleCost                        = MAX_DOUBLE;
      Distortion uiSingleDistLuma                   = 0;
      uint64_t   singleFracBits                     = 0;
      int        bestModeId[MAX_NUM_COMPONENT]      = { 0, 0, 0 };
    #else
    
      bool    checkInitTrDepth = false, checkInitTrDepthTransformSkipWinner = false;
    
      double     dSingleCost                        = MAX_DOUBLE;
      Distortion uiSingleDistLuma                   = 0;
      uint64_t     singleFracBits                     = 0;
      bool       checkTransformSkip                 = pps.getUseTransformSkip();
      int        bestModeId[MAX_NUM_COMPONENT]      = {0, 0, 0};
      uint8_t      nNumTransformCands                 = cu.emtFlag ? 4 : 1; //4 is the number of transforms of emt
      bool       isAllIntra                         = m_pcEncCfg->getIntraPeriod() == 1;
    
      uint8_t numTransformIndexCands                  = nNumTransformCands;
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #endif
    
    
      const TempCtx ctxStart  ( m_CtxCache, m_CABACEstimator->getCtx() );
      TempCtx       ctxBest   ( m_CtxCache );
    
      CodingStructure *csSplit = nullptr;
      CodingStructure *csFull  = nullptr;
    
      if( bCheckSplit )
      {
        csSplit = &cs;
      }
      else if( bCheckFull )
      {
        csFull = &cs;
      }
    
      if( bCheckFull )
      {
        csFull->cost = 0.0;
    
        TransformUnit &tu = csFull->addTU( CS::getArea( *csFull, currArea, partitioner.chType ), partitioner.chType );
        tu.depth = currDepth;
    
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #if JVET_M0464_UNI_MTS
        const bool tsAllowed  = TU::isTSAllowed ( tu, COMPONENT_Y );
        const bool mtsAllowed = TU::isMTSAllowed( tu, COMPONENT_Y );
        uint8_t nNumTransformCands = 1 + ( tsAllowed ? 1 : 0 ) + ( mtsAllowed ? 4 : 0 ); // DCT + TS + 4 MTS = 6 tests
        std::vector<TrMode> trModes;
        trModes.push_back( TrMode( 0, true ) ); //DCT2
        if( tsAllowed )
        {
          trModes.push_back( TrMode( 1, true ) );
        }
        if( mtsAllowed )
        {
          for( int i = 2; i < 6; i++ )
          {
            trModes.push_back( TrMode( i, true) );
          }
        }
    
        CHECK( !tu.Y().valid(), "Invalid TU" );
    #else
    
        checkTransformSkip &= TU::hasTransformSkipFlag( *tu.cs, tu.Y() );
        checkTransformSkip &= !cu.transQuantBypass;
        checkTransformSkip &= !cu.emtFlag;
    
        CHECK( !tu.Y().valid(), "Invalid TU" );
    
        //this prevents transformSkip from being checked because we already know it's not the best mode
        checkTransformSkip = ( checkInitTrDepth && !checkInitTrDepthTransformSkipWinner ) ? false : checkTransformSkip;
    
    
        CHECK( checkInitTrDepthTransformSkipWinner && !checkTransformSkip, "Transform Skip must be enabled if it was the winner in the previous call of xRecurIntraCodingLumaQT!" );
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #endif
    
    
        CodingStructure &saveCS = *m_pSaveCS[0];
    
        TransformUnit *tmpTU = nullptr;
    
        Distortion singleDistTmpLuma = 0;
        uint64_t     singleTmpFracBits = 0;
        double     singleCostTmp     = 0;
        int        firstCheckId      = 0;
    
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #if JVET_M0464_UNI_MTS
        int       lastCheckId        = trModes[nNumTransformCands-1].first;
        bool isNotOnlyOneMode        = nNumTransformCands != 1;
    #else
    
        //we add the EMT candidates to the loop. TransformSkip will still be the last one to be checked (when modeId == lastCheckId) as long as checkTransformSkip is true
        int        lastCheckId       = numTransformIndexCands - ( firstCheckId + 1 ) + ( int ) checkTransformSkip;
        bool isNotOnlyOneMode        = lastCheckId != firstCheckId && !checkInitTrDepthTransformSkipWinner;
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #endif
    
    
        if( isNotOnlyOneMode )
        {
          saveCS.pcv     = cs.pcv;
          saveCS.picture = cs.picture;
          saveCS.area.repositionTo(cs.area);
          saveCS.clearTUs();
          tmpTU = &saveCS.addTU(currArea, partitioner.chType);
        }
    
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #if JVET_M0464_UNI_MTS
        bool    cbfDCT2  = true;
    #else
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #endif
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #if JVET_M0464_UNI_MTS
        for( int modeId = firstCheckId; modeId < nNumTransformCands; modeId++ )
        {
          if( !cbfDCT2 || ( m_pcEncCfg->getUseTransformSkipFast() && bestModeId[COMPONENT_Y] == 1 ) )
          {
            break;
          }
          if( !trModes[modeId].second )
          {
            continue;
          }
          tu.mtsIdx = trModes[modeId].first;
    #else
    
        for( int modeId = firstCheckId; modeId <= lastCheckId; modeId++ )
        {
          if( checkInitTrDepthTransformSkipWinner )
          {
            //If this is a full RQT call and the winner of the first call (checkFirst=true) was transformSkip, then we skip the first iteration of the loop, since transform skip always comes at the end
            if( modeId == firstCheckId )
            {
              continue;
            }
          }
    
          uint8_t transformIndex = modeId;
    
    
          if( ( transformIndex < lastCheckId ) || ( ( transformIndex == lastCheckId ) && !checkTransformSkip ) ) //we avoid this if the mode is transformSkip
          {
            // Skip checking other transform candidates if zero CBF is encountered and it is the best transform so far
            if( m_pcEncCfg->getFastIntraEMT() && isAllIntra && transformIndex && !cbfBestMode )
            {
              continue;
            }
          }
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #endif
    
    
          if ((modeId != firstCheckId) && isNotOnlyOneMode)
          {
            m_CABACEstimator->getCtx() = ctxStart;
          }
    
          int default0Save1Load2 = 0;
          singleDistTmpLuma = 0;
    
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #if JVET_M0464_UNI_MTS
          if( modeId == firstCheckId && nNumTransformCands > 1 )
    #else
    
          if (modeId == firstCheckId && modeId != lastCheckId && !checkInitTrDepthTransformSkipWinner )
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #endif
    
          {
            default0Save1Load2 = 1;
          }
          else if (modeId != firstCheckId)
          {
            default0Save1Load2 = 2;
          }
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #if JVET_M0464_UNI_MTS
          if( nNumTransformCands > 1 )
          {
            xIntraCodingTUBlock( tu, COMPONENT_Y, false, singleDistTmpLuma, default0Save1Load2, &numSig, modeId == 0 ? &trModes : nullptr, true );
            if( modeId == 0 )
            {
              for( int i = 0; i < nNumTransformCands; i++ )
              {
                if( trModes[i].second )
                {
                  lastCheckId = trModes[i].first;
                }
              }
            }
          }
          else
          {
            xIntraCodingTUBlock( tu, COMPONENT_Y, false, singleDistTmpLuma, default0Save1Load2, &numSig );
          }
    #else
    
          if (cu.emtFlag)
          {
            tu.emtIdx = transformIndex;
          }
          if( !checkTransformSkip )
          {
            tu.transformSkip[COMPONENT_Y] = false;
          }
          else
          {
            tu.transformSkip[COMPONENT_Y] = modeId == lastCheckId;
          }
    
          xIntraCodingTUBlock( tu, COMPONENT_Y, false, singleDistTmpLuma, default0Save1Load2, &numSig );
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #endif
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #if JVET_M0464_UNI_MTS
          if( ( trModes[modeId].first != 0 && !TU::getCbfAtDepth( tu, COMPONENT_Y, currDepth ) ) )
    #else
    
          //the condition (transformIndex != DCT2_EMT) seems to be irrelevant, since DCT2_EMT=7 and the highest value of transformIndex is 4
    
          if( ( modeId == lastCheckId && checkTransformSkip && !TU::getCbfAtDepth( tu, COMPONENT_Y, currDepth ) ) )
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #endif
    
          {
            //In order not to code TS flag when cbf is zero, the case for TS with cbf being zero is forbidden.
            singleCostTmp = MAX_DOUBLE;
          }
          else
          {
            singleTmpFracBits = xGetIntraFracBitsQT( *csFull, partitioner, true, false );
            singleCostTmp     = m_pcRdCost->calcRdCost( singleTmpFracBits, singleDistTmpLuma );
          }
    
          if (singleCostTmp < dSingleCost)
          {
            dSingleCost       = singleCostTmp;
            uiSingleDistLuma  = singleDistTmpLuma;
            singleFracBits    = singleTmpFracBits;
    
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #if JVET_M0464_UNI_MTS
            bestModeId[COMPONENT_Y] = trModes[modeId].first;
            if( trModes[modeId].first == 0 )
            {
              cbfDCT2  = TU::getCbfAtDepth( tu, COMPONENT_Y, currDepth );
            }
    #else
    
            bestModeId[COMPONENT_Y] = modeId;
            cbfBestMode       = TU::getCbfAtDepth( tu, COMPONENT_Y, currDepth );
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #endif
    
    
            if( bestModeId[COMPONENT_Y] != lastCheckId )
            {
    #if KEEP_PRED_AND_RESI_SIGNALS
              saveCS.getPredBuf( tu.Y() ).copyFrom( csFull->getPredBuf( tu.Y() ) );
    #endif
              saveCS.getRecoBuf( tu.Y() ).copyFrom( csFull->getRecoBuf( tu.Y() ) );
    
              if( keepResi )
              {
                saveCS.getResiBuf   ( tu.Y() ).copyFrom( csFull->getResiBuf   ( tu.Y() ) );
                saveCS.getOrgResiBuf( tu.Y() ).copyFrom( csFull->getOrgResiBuf( tu.Y() ) );
              }
    
              tmpTU->copyComponentFrom( tu, COMPONENT_Y );
    
              ctxBest = m_CABACEstimator->getCtx();
            }
          }
        }
    
        if( bestModeId[COMPONENT_Y] != lastCheckId )
        {
    #if KEEP_PRED_AND_RESI_SIGNALS
          csFull->getPredBuf( tu.Y() ).copyFrom( saveCS.getPredBuf( tu.Y() ) );
    #endif
          csFull->getRecoBuf( tu.Y() ).copyFrom( saveCS.getRecoBuf( tu.Y() ) );
    
          if( keepResi )
          {
            csFull->getResiBuf   ( tu.Y() ).copyFrom( saveCS.getResiBuf   ( tu.Y() ) );
            csFull->getOrgResiBuf( tu.Y() ).copyFrom( saveCS.getOrgResiBuf( tu.Y() ) );
          }
    
          tu.copyComponentFrom( *tmpTU, COMPONENT_Y );
    
          if( !bCheckSplit )
          {
            m_CABACEstimator->getCtx() = ctxBest;
          }
        }
        else if( bCheckSplit )
        {
          ctxBest = m_CABACEstimator->getCtx();
        }
    
        csFull->cost     += dSingleCost;
        csFull->dist     += uiSingleDistLuma;
        csFull->fracBits += singleFracBits;
      }
    
      if( bCheckSplit )
      {
        //----- store full entropy coding status, load original entropy coding status -----
        if( bCheckFull )
        {
          m_CABACEstimator->getCtx() = ctxStart;
        }
        //----- code splitted block -----
        csSplit->cost = 0;
    
        bool uiSplitCbfLuma  = false;
        bool splitIsSelected = true;
    
    Karsten Suehring's avatar
    Karsten Suehring committed
        if( partitioner.canSplit( TU_MAX_TR_SPLIT, cs ) )
    
        {
          partitioner.splitCurrArea( TU_MAX_TR_SPLIT, cs );
        }
    
        do
        {
          xRecurIntraCodingLumaQT( *csSplit, partitioner );
    
          csSplit->setDecomp( partitioner.currArea().Y() );
    
          uiSplitCbfLuma |= TU::getCbfAtDepth( *csSplit->getTU( partitioner.currArea().lumaPos(), partitioner.chType ), COMPONENT_Y, partitioner.currTrDepth );
    
    
    
        } while( partitioner.nextPart( *csSplit ) );
    
        partitioner.exitCurrSplit();
    
        if( splitIsSelected )
        {
          for( auto &ptu : csSplit->tus )
          {
            if( currArea.Y().contains( ptu->Y() ) )
            {
              TU::setCbfAtDepth( *ptu, COMPONENT_Y, currDepth, uiSplitCbfLuma ? 1 : 0 );
            }
          }
    
          //----- restore context states -----
          m_CABACEstimator->getCtx() = ctxStart;
    
          //----- determine rate and r-d cost -----
          csSplit->fracBits = xGetIntraFracBitsQT(*csSplit, partitioner, true, false);
    
          //--- update cost ---
          csSplit->cost     = m_pcRdCost->calcRdCost(csSplit->fracBits, csSplit->dist);
        }
      }
    
      if( csFull || csSplit )
      {
        {
          // otherwise this would've happened in useSubStructure
          cs.picture->getRecoBuf( currArea.Y() ).copyFrom( cs.getRecoBuf( currArea.Y() ) );
        }
    
        cs.cost = m_pcRdCost->calcRdCost( cs.fracBits, cs.dist );
      }
    }
    
    ChromaCbfs IntraSearch::xRecurIntraChromaCodingQT(CodingStructure &cs, Partitioner& partitioner)
    {
      UnitArea currArea                   = partitioner.currArea();
      const bool keepResi                 = cs.sps->getSpsNext().getUseLMChroma() || KEEP_PRED_AND_RESI_SIGNALS;
      if( !currArea.Cb().valid() ) return ChromaCbfs( false );
    
    
      TransformUnit &currTU               = *cs.getTU( currArea.chromaPos(), CHANNEL_TYPE_CHROMA );
      const PredictionUnit &pu            = *cs.getPU( currArea.chromaPos(), CHANNEL_TYPE_CHROMA );
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #if !JVET_M0464_UNI_MTS
    
      const TransformUnit &currTULuma     = CS::isDualITree( cs ) ? *cs.picture->cs->getTU( currArea.lumaPos(), CHANNEL_TYPE_LUMA ) : currTU;
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #endif
    
    
      uint32_t     currDepth                  = partitioner.currTrDepth;
      const PPS &pps                      = *cs.pps;
      ChromaCbfs cbfs                     ( false );
    
      if (currDepth == currTU.depth)
      {
        if (!currArea.Cb().valid() || !currArea.Cr().valid())
        {
          return cbfs;
        }
    
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #if !JVET_M0464_UNI_MTS
    
        bool checkTransformSkip = pps.getUseTransformSkip();
        checkTransformSkip &= TU::hasTransformSkipFlag( *currTU.cs, partitioner.currArea().Cb() );
    
        if( m_pcEncCfg->getUseTransformSkipFast() )
        {
          checkTransformSkip &= TU::hasTransformSkipFlag( *currTU.cs, partitioner.currArea().Y() );
    
          if( checkTransformSkip && cs.pcv->noChroma2x2 )
          {
            int nbLumaSkip = currTULuma.transformSkip[0] ? 1 : 0;
    
            {
              // the chroma blocks are co-located with the last luma block, so backwards references are needed
              nbLumaSkip += cs.getTU( currTULuma.Y().topLeft().offset( -1,  0 ), partitioner.chType )->transformSkip[0] ? 1 : 0;
              nbLumaSkip += cs.getTU( currTULuma.Y().topLeft().offset( -1, -1 ), partitioner.chType )->transformSkip[0] ? 1 : 0;
              nbLumaSkip += cs.getTU( currTULuma.Y().topLeft().offset(  0, -1 ), partitioner.chType )->transformSkip[0] ? 1 : 0;
            }
    
            checkTransformSkip &= ( nbLumaSkip > 0 );
          }
        }
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #endif
    
    
        CodingStructure &saveCS = *m_pSaveCS[1];
        saveCS.pcv      = cs.pcv;
        saveCS.picture  = cs.picture;
        saveCS.area.repositionTo( cs.area );
    
        saveCS.initStructData( MAX_INT, false, true );
    
    
        TransformUnit &tmpTU = saveCS.addTU(currArea, partitioner.chType);
    
    
        cs.setDecomp(currArea.Cb(), true); // set in advance (required for Cb2/Cr2 in 4:2:2 video)
    
        const unsigned      numTBlocks  = ::getNumberValidTBlocks( *cs.pcv );
    
        for( uint32_t c = COMPONENT_Cb; c < numTBlocks; c++)
        {
          const ComponentID compID  = ComponentID(c);
          const CompArea&   area    = currTU.blocks[compID];
    
          double     dSingleCost    = MAX_DOUBLE;
          int        bestModeId     = 0;
          Distortion singleDistC    = 0;
          Distortion singleDistCTmp = 0;
          double     singleCostTmp  = 0;
    
          const bool checkCrossComponentPrediction = PU::isChromaIntraModeCrossCheckMode( pu ) && pps.getPpsRangeExtension().getCrossComponentPredictionEnabledFlag() && TU::getCbf( currTU, COMPONENT_Y );
    
          const int  crossCPredictionModesToTest = checkCrossComponentPrediction ? 2 : 1;
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #if JVET_M0464_UNI_MTS
          const int  totalModesToTest            = crossCPredictionModesToTest;
    #else
    
          const int  transformSkipModesToTest    = checkTransformSkip ? 2 : 1;
          const int  totalModesToTest            = crossCPredictionModesToTest * transformSkipModesToTest;
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #endif
    
          const bool isOneMode                   = (totalModesToTest == 1);
    
          int currModeId = 0;
          int default0Save1Load2 = 0;
    
          TempCtx ctxStart  ( m_CtxCache );
          TempCtx ctxBest   ( m_CtxCache );
    
          if (!isOneMode)
          {
            ctxStart = m_CABACEstimator->getCtx();
          }
    
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #if !JVET_M0464_UNI_MTS
    
          for (int transformSkipModeId = 0; transformSkipModeId < transformSkipModesToTest; transformSkipModeId++)
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #endif
    
          {
            for (int crossCPredictionModeId = 0; crossCPredictionModeId < crossCPredictionModesToTest; crossCPredictionModeId++)
            {
              currTU.compAlpha    [compID] = 0;
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #if !JVET_M0464_UNI_MTS
    
              currTU.transformSkip[compID] = transformSkipModeId;
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #endif
    
    
              currModeId++;
    
              const bool isFirstMode = (currModeId == 1);
              const bool isLastMode  = (currModeId == totalModesToTest); // currModeId is indexed from 1
    
              if (isOneMode)
              {
                default0Save1Load2 = 0;
              }
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #if JVET_M0464_UNI_MTS
              else if (!isOneMode && (crossCPredictionModeId == 0))
    #else
    
              else if (!isOneMode && (transformSkipModeId == 0) && (crossCPredictionModeId == 0))
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #endif
    
              {
                default0Save1Load2 = 1; //save prediction on first mode
              }
              else
              {
                default0Save1Load2 = 2; //load it on subsequent modes
              }
    
              if (!isFirstMode) // if not first mode to be tested
              {
                m_CABACEstimator->getCtx() = ctxStart;
              }
    
              singleDistCTmp = 0;
    
              xIntraCodingTUBlock( currTU, compID, crossCPredictionModeId != 0, singleDistCTmp, default0Save1Load2 );
    
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #if JVET_M0464_UNI_MTS
              if( ( ( crossCPredictionModeId == 1 ) && ( currTU.compAlpha[compID] == 0 ) ) ) //In order not to code TS flag when cbf is zero, the case for TS with cbf being zero is forbidden.
    #else
    
              if( ( ( crossCPredictionModeId == 1 ) && ( currTU.compAlpha[compID] == 0 ) ) || ( ( transformSkipModeId == 1 ) && !TU::getCbf( currTU, compID ) ) ) //In order not to code TS flag when cbf is zero, the case for TS with cbf being zero is forbidden.
    
    Tung Nguyen's avatar
    Tung Nguyen committed
    #endif
    
              {
                singleCostTmp = MAX_DOUBLE;
              }
              else if( !isOneMode )
              {
                uint64_t fracBitsTmp = xGetIntraFracBitsQTChroma( currTU, compID );
                singleCostTmp = m_pcRdCost->calcRdCost( fracBitsTmp, singleDistCTmp );
              }
    
              if( singleCostTmp < dSingleCost )
              {
                dSingleCost = singleCostTmp;
                singleDistC = singleDistCTmp;
                bestModeId  = currModeId;
    
                if( !isLastMode )
                {
    #if KEEP_PRED_AND_RESI_SIGNALS
                  saveCS.getPredBuf   (area).copyFrom(cs.getPredBuf   (area));
                  saveCS.getOrgResiBuf(area).copyFrom(cs.getOrgResiBuf(area));
    #endif
                  if( keepResi )
                  {
                    saveCS.getResiBuf (area).copyFrom(cs.getResiBuf   (area));
                  }
                  saveCS.getRecoBuf   (area).copyFrom(cs.getRecoBuf   (area));
    
                  tmpTU.copyComponentFrom(currTU, compID);
    
                  ctxBest = m_CABACEstimator->getCtx();
                }
              }
            }
          }
    
          if (bestModeId < totalModesToTest)
          {
    #if KEEP_PRED_AND_RESI_SIGNALS
            cs.getPredBuf   (area).copyFrom(saveCS.getPredBuf   (area));
            cs.getOrgResiBuf(area).copyFrom(saveCS.getOrgResiBuf(area));
    #endif
            if( keepResi )
            {
              cs.getResiBuf (area).copyFrom(saveCS.getResiBuf   (area));
            }
            cs.getRecoBuf   (area).copyFrom(saveCS.getRecoBuf   (area));
    
            currTU.copyComponentFrom(tmpTU, compID);
    
            m_CABACEstimator->getCtx() = ctxBest;
          }
    
          cs.picture->getRecoBuf(area).copyFrom(cs.getRecoBuf(area));
    
          cbfs.cbf(compID) = TU::getCbf(currTU, compID);
    
          cs.dist += singleDistC;
        }
      }
      else
      {
        unsigned    numValidTBlocks   = ::getNumberValidTBlocks( *cs.pcv );
        ChromaCbfs  SplitCbfs         ( false );
    
        if( partitioner.canSplit( TU_MAX_TR_SPLIT, cs ) )
        {
          partitioner.splitCurrArea( TU_MAX_TR_SPLIT, cs );
        }
        else
          THROW( "Implicit TU split not available" );
    
        do
        {
          ChromaCbfs subCbfs = xRecurIntraChromaCodingQT( cs, partitioner );
    
          for( uint32_t ch = COMPONENT_Cb; ch < numValidTBlocks; ch++ )
          {
            const ComponentID compID = ComponentID( ch );
            SplitCbfs.cbf( compID ) |= subCbfs.cbf( compID );
          }
        } while( partitioner.nextPart( cs ) );
    
        partitioner.exitCurrSplit();
    
        {
    
          cbfs.Cb |= SplitCbfs.Cb;
          cbfs.Cr |= SplitCbfs.Cr;
    
          for( auto &ptu : cs.tus )
          {
            if( currArea.Cb().contains( ptu->Cb() ) || ( !ptu->Cb().valid() && currArea.Y().contains( ptu->Y() ) ) )
            {
              TU::setCbfAtDepth( *ptu, COMPONENT_Cb, currDepth, SplitCbfs.Cb );
              TU::setCbfAtDepth( *ptu, COMPONENT_Cr, currDepth, SplitCbfs.Cr );
            }
          }
        }
      }
    
      return cbfs;
    }
    
    uint64_t IntraSearch::xFracModeBitsIntra(PredictionUnit &pu, const uint32_t &uiMode, const ChannelType &chType)
    {
      uint32_t orgMode = uiMode;
    
    
      if (!pu.mhIntraFlag)
    
      std::swap(orgMode, pu.intraDir[chType]);
    
      m_CABACEstimator->resetBits();
    
      if( isLuma( chType ) )
      {
    
        if ( pu.mhIntraFlag )
    
          m_CABACEstimator->MHIntra_luma_pred_modes(*pu.cu);
        else
        {
    
          m_CABACEstimator->extend_ref_line(pu);
    
          m_CABACEstimator->intra_luma_pred_mode(pu);
        }
    
      if ( !pu.mhIntraFlag )
    
      std::swap(orgMode, pu.intraDir[chType]);
    
      return m_CABACEstimator->getEstFracBits();
    }
    
    
    
    void IntraSearch::encPredIntraDPCM( const ComponentID &compID, PelBuf &pOrg, PelBuf &pDst, const uint32_t &uiDirMode )
    {
      CHECK( pOrg.buf == 0, "Encoder DPCM called without original buffer" );
    
      const int srcStride = m_topRefLength + 1;