Skip to content
Snippets Groups Projects
UnitTools.cpp 89.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • {
      if ( pu.cu->lumaSize().width < 8 || pu.cu->lumaSize().height < 8 )
      {
        return false;
      }
      return getFirstAvailableAffineNeighbour( pu ) != nullptr;
    }
    
    #if JVET_L0646_GBI
    void PU::getAffineMergeCand( const PredictionUnit &pu, MvField(*mvFieldNeighbours)[3], unsigned char &interDirNeighbours, unsigned char &gbiIdx, int &numValidMergeCand )
    #else
    
    void PU::getAffineMergeCand( const PredictionUnit &pu, MvField (*mvFieldNeighbours)[3], unsigned char &interDirNeighbours, int &numValidMergeCand )
    
    {
      for ( int mvNum = 0; mvNum < 3; mvNum++ )
      {
        mvFieldNeighbours[0][mvNum].setMvField( Mv(), -1 );
        mvFieldNeighbours[1][mvNum].setMvField( Mv(), -1 );
      }
    
      const PredictionUnit* puFirstNeighbour = getFirstAvailableAffineNeighbour( pu );
      if( puFirstNeighbour == nullptr )
      {
        numValidMergeCand = -1;
    
    #if JVET_L0646_GBI
        gbiIdx = GBI_DEFAULT;
    #endif
    
        return;
      }
      else
      {
        numValidMergeCand = 1;
      }
    
      // get Inter Dir
      interDirNeighbours = puFirstNeighbour->getMotionInfo().interDir;
    
      pu.cu->affineType = puFirstNeighbour->cu->affineType;
    
      // derive Mv from neighbor affine block
      Mv cMv[3];
      if ( interDirNeighbours != 2 )
      {
        xInheritedAffineMv( pu, puFirstNeighbour, REF_PIC_LIST_0, cMv );
        for ( int mvNum = 0; mvNum < 3; mvNum++ )
        {
          mvFieldNeighbours[0][mvNum].setMvField( cMv[mvNum], puFirstNeighbour->refIdx[0] );
        }
      }
    
      if ( pu.cs->slice->isInterB() )
      {
        if ( interDirNeighbours != 1 )
        {
          xInheritedAffineMv( pu, puFirstNeighbour, REF_PIC_LIST_1, cMv );
          for ( int mvNum = 0; mvNum < 3; mvNum++ )
          {
            mvFieldNeighbours[1][mvNum].setMvField( cMv[mvNum], puFirstNeighbour->refIdx[1] );
          }
        }
      }
    
    #if JVET_L0646_GBI
      gbiIdx = puFirstNeighbour->cu->GBiIdx;
    #endif
    
    }
    
    void PU::setAllAffineMvField( PredictionUnit &pu, MvField *mvField, RefPicList eRefList )
    {
      // Set Mv
      Mv mv[3];
      for ( int i = 0; i < 3; i++ )
      {
        mv[i] = mvField[i].mv;
      }
      setAllAffineMv( pu, mv[0], mv[1], mv[2], eRefList );
    
      // Set RefIdx
      CHECK( mvField[0].refIdx != mvField[1].refIdx || mvField[0].refIdx != mvField[2].refIdx, "Affine mv corners don't have the same refIdx." );
      pu.refIdx[eRefList] = mvField[0].refIdx;
    }
    
    
    void PU::setAllAffineMv( PredictionUnit& pu, Mv affLT, Mv affRT, Mv affLB, RefPicList eRefList 
    #if REMOVE_MV_ADAPT_PREC
      , bool setHighPrec
    #endif
    )
    
    #if REMOVE_MV_ADAPT_PREC
      if (setHighPrec)
      {
        affLT.hor = affLT.hor << VCEG_AZ07_MV_ADD_PRECISION_BIT_FOR_STORE;
        affLT.ver = affLT.ver << VCEG_AZ07_MV_ADD_PRECISION_BIT_FOR_STORE;
        affRT.hor = affRT.hor << VCEG_AZ07_MV_ADD_PRECISION_BIT_FOR_STORE;
        affRT.ver = affRT.ver << VCEG_AZ07_MV_ADD_PRECISION_BIT_FOR_STORE;
        affLB.hor = affLB.hor << VCEG_AZ07_MV_ADD_PRECISION_BIT_FOR_STORE;
        affLB.ver = affLB.ver << VCEG_AZ07_MV_ADD_PRECISION_BIT_FOR_STORE;
      }
    #else
    
      affLT.setHighPrec();
      affRT.setHighPrec();
      affLB.setHighPrec();
    
      int deltaMvHorX, deltaMvHorY, deltaMvVerX, deltaMvVerY;
      deltaMvHorX = (affRT - affLT).getHor() << (shift - g_aucLog2[width]);
      deltaMvHorY = (affRT - affLT).getVer() << (shift - g_aucLog2[width]);
      int height = pu.Y().height;
      if ( pu.cu->affineType == AFFINEMODEL_6PARAM )
      {
        deltaMvVerX = (affLB - affLT).getHor() << (shift - g_aucLog2[height]);
        deltaMvVerY = (affLB - affLT).getVer() << (shift - g_aucLog2[height]);
      }
      else
      {
        deltaMvVerX = -deltaMvHorY;
        deltaMvVerY = deltaMvHorX;
      }
    
      int mvScaleHor = affLT.getHor() << shift;
      int mvScaleVer = affLT.getVer() << shift;
    
      int blockWidth = AFFINE_MIN_BLOCK_SIZE;
      int blockHeight = AFFINE_MIN_BLOCK_SIZE;
      const int halfBW = blockWidth >> 1;
      const int halfBH = blockHeight >> 1;
    
      MotionBuf mb = pu.getMotionBuf();
      int mvScaleTmpHor, mvScaleTmpVer;
      for ( int h = 0; h < pu.Y().height; h += blockHeight )
      {
        for ( int w = 0; w < pu.Y().width; w += blockWidth )
        {
          mvScaleTmpHor = mvScaleHor + deltaMvHorX * (halfBW + w) + deltaMvVerX * (halfBH + h);
          mvScaleTmpVer = mvScaleVer + deltaMvHorY * (halfBW + w) + deltaMvVerY * (halfBH + h);
          roundAffineMv( mvScaleTmpHor, mvScaleTmpVer, shift );
    
          for ( int y = (h >> MIN_CU_LOG2); y < ((h + blockHeight) >> MIN_CU_LOG2); y++ )
          {
            for ( int x = (w >> MIN_CU_LOG2); x < ((w + blockHeight) >> MIN_CU_LOG2); x++ )
            {
    
    #if REMOVE_MV_ADAPT_PREC
              mb.at(x, y).mv[eRefList].hor = mvScaleTmpHor;
              mb.at(x, y).mv[eRefList].ver = mvScaleTmpVer;
    #else
              mb.at(x, y).mv[eRefList] = Mv(mvScaleTmpHor, mvScaleTmpVer, true);
    #endif
    
            }
          }
        }
      }
    
      // Set AffineMvField for affine motion compensation LT, RT, LB and RB
      mb.at(            0,             0 ).mv[eRefList] = affLT;
      mb.at( mb.width - 1,             0 ).mv[eRefList] = affRT;
    
      if ( pu.cu->affineType == AFFINEMODEL_6PARAM )
      {
        mb.at( 0, mb.height - 1 ).mv[eRefList] = affLB;
      }
    }
    
    static bool deriveScaledMotionTemporal( const Slice&      slice,
                                            const Position&   colPos,
                                            const Picture*    pColPic,
                                            const RefPicList  eCurrRefPicList,
                                            Mv&         cColMv,
                                            const RefPicList  eFetchRefPicList)
    {
      const MotionInfo &mi = pColPic->cs->getMotionInfo(colPos);
      const Slice *pColSlice = nullptr;
    
      for (const auto &pSlice : pColPic->slices)
      {
        if (pSlice->getIndependentSliceIdx() == mi.sliceIdx)
        {
          pColSlice = pSlice;
          break;
        }
      }
    
      CHECK(pColSlice == nullptr, "Couldn't find the colocated slice");
    
      int iColPOC, iColRefPOC, iCurrPOC, iCurrRefPOC, iScale;
      bool bAllowMirrorMV = true;
      RefPicList eColRefPicList = slice.getCheckLDC() ? eCurrRefPicList : RefPicList(1 - eFetchRefPicList);
      if (pColPic == slice.getRefPic(RefPicList(slice.isInterB() ? 1 - slice.getColFromL0Flag() : 0), slice.getColRefIdx()))
      {
        eColRefPicList = eCurrRefPicList;   //67 -> disable, 64 -> enable
        bAllowMirrorMV = false;
      }
    
      // Although it might make sense to keep the unavailable motion field per direction still be unavailable, I made the MV prediction the same way as in TMVP
      // So there is an interaction between MV0 and MV1 of the corresponding blocks identified by TV.
    
      // Grab motion and do necessary scaling.{{
      iCurrPOC = slice.getPOC();
    
      int iColRefIdx = mi.refIdx[eColRefPicList];
    
      if (iColRefIdx < 0 && (slice.getCheckLDC() || bAllowMirrorMV))
      {
        eColRefPicList = RefPicList(1 - eColRefPicList);
        iColRefIdx = mi.refIdx[eColRefPicList];
    
        if (iColRefIdx < 0)
        {
          return false;
        }
      }
    
      if (iColRefIdx >= 0 && slice.getNumRefIdx(eCurrRefPicList) > 0)
      {
        iColPOC = pColSlice->getPOC();
        iColRefPOC = pColSlice->getRefPOC(eColRefPicList, iColRefIdx);
        ///////////////////////////////////////////////////////////////
        // Set the target reference index to 0, may be changed later //
        ///////////////////////////////////////////////////////////////
        iCurrRefPOC = slice.getRefPic(eCurrRefPicList, 0)->getPOC();
        // Scale the vector.
        cColMv = mi.mv[eColRefPicList];
        //pcMvFieldSP[2*iPartition + eCurrRefPicList].getMv();
        // Assume always short-term for now
        iScale = xGetDistScaleFactor(iCurrPOC, iCurrRefPOC, iColPOC, iColRefPOC);
    
        if (iScale != 4096)
        {
    
    #if !REMOVE_MV_ADAPT_PREC
    
          if (slice.getSPS()->getSpsNext().getUseHighPrecMv())
          {
            cColMv.setHighPrec();
          }
    
    
          cColMv = cColMv.scaleMv(iScale);
        }
    
        return true;
      }
      return false;
    }
    
    void clipColBlkMv(int& mvX, int& mvY, const PredictionUnit& pu)
    {
      Position puPos = pu.lumaPos();
      Size     puSize = pu.lumaSize();
    
      int ctuSize = pu.cs->sps->getSpsNext().getCTUSize();
      int ctuX = puPos.x / ctuSize*ctuSize;
      int ctuY = puPos.y / ctuSize*ctuSize;
    
      int horMax = std::min((int)pu.cs->sps->getPicWidthInLumaSamples(), ctuX + ctuSize + 4) - puSize.width;
      int horMin = std::max((int)0, ctuX);
      int verMax = std::min((int)pu.cs->sps->getPicHeightInLumaSamples(), ctuY + ctuSize) - puSize.height;
      int verMin = std::min((int)0, ctuY);
    
      horMax = horMax - puPos.x;
      horMin = horMin - puPos.x;
      verMax = verMax - puPos.y;
      verMin = verMin - puPos.y;
    
      mvX = std::min(horMax, std::max(horMin, mvX));
      mvY = std::min(verMax, std::max(verMin, mvY));
    }
    
    
    bool PU::getInterMergeSubPuMvpCand(const PredictionUnit &pu, MergeCtx& mrgCtx, bool& LICFlag, const int count
    )
    
    {
      const Slice   &slice = *pu.cs->slice;
      const unsigned scale = 4 * std::max<int>(1, 4 * AMVP_DECIMATION_FACTOR / 4);
      const unsigned mask = ~(scale - 1);
    
      const Picture *pColPic = slice.getRefPic(RefPicList(slice.isInterB() ? 1 - slice.getColFromL0Flag() : 0), slice.getColRefIdx());
      Mv cTMv;
      RefPicList fetchRefPicList = RefPicList(slice.isInterB() ? 1 - slice.getColFromL0Flag() : 0);
    
      bool terminate = false;
      for (unsigned currRefListId = 0; currRefListId < (slice.getSliceType() == B_SLICE ? 2 : 1) && !terminate; currRefListId++)
      {
        for (int uiN = 0; uiN < count && !terminate; uiN++)
        {
          RefPicList currRefPicList = RefPicList(slice.getCheckLDC() ? (slice.getColFromL0Flag() ? currRefListId : 1 - currRefListId) : currRefListId);
    
          if ((mrgCtx.interDirNeighbours[uiN] & (1 << currRefPicList)) && slice.getRefPic(currRefPicList, mrgCtx.mvFieldNeighbours[uiN * 2 + currRefPicList].refIdx) == pColPic)
          {
            cTMv = mrgCtx.mvFieldNeighbours[uiN * 2 + currRefPicList].mv;
            terminate = true;
            fetchRefPicList = currRefPicList;
            break;
          }
        }
      }
    
      ///////////////////////////////////////////////////////////////////////
      ////////          GET Initial Temporal Vector                  ////////
      ///////////////////////////////////////////////////////////////////////
      int mvPrec = 2;
    
    #if !REMOVE_MV_ADAPT_PREC
    
      if (pu.cs->sps->getSpsNext().getUseHighPrecMv())
      {
        cTMv.setHighPrec();
    
        mvPrec += VCEG_AZ07_MV_ADD_PRECISION_BIT_FOR_STORE;
    
    #if !REMOVE_MV_ADAPT_PREC
    
      int mvRndOffs = (1 << mvPrec) >> 1;
    
      Mv cTempVector = cTMv;
      bool  tempLICFlag = false;
    
      // compute the location of the current PU
      Position puPos = pu.lumaPos();
      Size puSize = pu.lumaSize();
      int numPartLine = std::max(puSize.width >> slice.getSubPuMvpSubblkLog2Size(), 1u);
      int numPartCol = std::max(puSize.height >> slice.getSubPuMvpSubblkLog2Size(), 1u);
      int puHeight = numPartCol == 1 ? puSize.height : 1 << slice.getSubPuMvpSubblkLog2Size();
      int puWidth = numPartLine == 1 ? puSize.width : 1 << slice.getSubPuMvpSubblkLog2Size();
    
      Mv cColMv;
      // use coldir.
      bool     bBSlice = slice.isInterB();
    
      Position centerPos;
    
      bool found = false;
      cTempVector = cTMv;
      int tempX = ((cTempVector.getHor() + mvRndOffs) >> mvPrec);
      int tempY = ((cTempVector.getVer() + mvRndOffs) >> mvPrec);
      clipColBlkMv(tempX, tempY, pu);
    
      if (puSize.width == puWidth && puSize.height == puHeight)
      {
        centerPos.x = puPos.x + (puSize.width >> 1) + tempX;
        centerPos.y = puPos.y + (puSize.height >> 1) + tempY;
      }
      else
      {
        centerPos.x = puPos.x + ((puSize.width / puWidth) >> 1)   * puWidth + (puWidth >> 1) + tempX;
        centerPos.y = puPos.y + ((puSize.height / puHeight) >> 1) * puHeight + (puHeight >> 1) + tempY;
      }
    
      centerPos.x = Clip3(0, (int)pColPic->lwidth() - 1, centerPos.x);
      centerPos.y = Clip3(0, (int)pColPic->lheight() - 1, centerPos.y);
    
      centerPos = Position{ PosType(centerPos.x & mask), PosType(centerPos.y & mask) };
    
      // derivation of center motion parameters from the collocated CU
      const MotionInfo &mi = pColPic->cs->getMotionInfo(centerPos);
    
      if (mi.isInter)
      {
        for (unsigned currRefListId = 0; currRefListId < (bBSlice ? 2 : 1); currRefListId++)
        {
          RefPicList  currRefPicList = RefPicList(currRefListId);
    
          if (deriveScaledMotionTemporal(slice, centerPos, pColPic, currRefPicList, cColMv, fetchRefPicList))
          {
            // set as default, for further motion vector field spanning
            mrgCtx.mvFieldNeighbours[(count << 1) + currRefListId].setMvField(cColMv, 0);
            mrgCtx.interDirNeighbours[count] |= (1 << currRefListId);
            LICFlag = tempLICFlag;
    
    #if JVET_L0646_GBI
            mrgCtx.GBiIdx[count] = GBI_DEFAULT;
    #endif
    
            found = true;
          }
          else
          {
            mrgCtx.mvFieldNeighbours[(count << 1) + currRefListId].setMvField(Mv(), NOT_VALID);
            mrgCtx.interDirNeighbours[count] &= ~(1 << currRefListId);
          }
        }
      }
    
      if (!found)
      {
        return false;
      }
      
      int xOff = puWidth / 2;
      int yOff = puHeight / 2;
    
      // compute the location of the current PU
      xOff += tempX;
      yOff += tempY;
    
      int iPicWidth = pColPic->lwidth() - 1;
      int iPicHeight = pColPic->lheight() - 1;
    
      MotionBuf& mb = mrgCtx.subPuMvpMiBuf;
    
      const bool isBiPred = isBipredRestriction(pu);
    
      for (int y = puPos.y; y < puPos.y + puSize.height; y += puHeight)
      {
        for (int x = puPos.x; x < puPos.x + puSize.width; x += puWidth)
        {
          Position colPos{ x + xOff, y + yOff };
    
          colPos.x = Clip3(0, iPicWidth, colPos.x);
          colPos.y = Clip3(0, iPicHeight, colPos.y);
    
          colPos = Position{ PosType(colPos.x & mask), PosType(colPos.y & mask) };
    
          const MotionInfo &colMi = pColPic->cs->getMotionInfo(colPos);
    
          MotionInfo mi;
    
          mi.isInter = true;
          mi.sliceIdx = slice.getIndependentSliceIdx();
    
          if (colMi.isInter)
          {
            for (unsigned currRefListId = 0; currRefListId < (bBSlice ? 2 : 1); currRefListId++)
            {
              RefPicList currRefPicList = RefPicList(currRefListId);
              if (deriveScaledMotionTemporal(slice, colPos, pColPic, currRefPicList, cColMv, fetchRefPicList))
              {
                mi.refIdx[currRefListId] = 0;
                mi.mv[currRefListId] = cColMv;
              }
            }
            }
          else
          {
            // intra coded, in this case, no motion vector is available for list 0 or list 1, so use default
            mi.mv[0] = mrgCtx.mvFieldNeighbours[(count << 1) + 0].mv;
            mi.mv[1] = mrgCtx.mvFieldNeighbours[(count << 1) + 1].mv;
            mi.refIdx[0] = mrgCtx.mvFieldNeighbours[(count << 1) + 0].refIdx;
            mi.refIdx[1] = mrgCtx.mvFieldNeighbours[(count << 1) + 1].refIdx;
          }
    
          mi.interDir = (mi.refIdx[0] != -1 ? 1 : 0) + (mi.refIdx[1] != -1 ? 2 : 0);
    
          if (isBiPred && mi.interDir == 3)
          {
            mi.interDir = 1;
            mi.mv[1] = Mv();
            mi.refIdx[1] = NOT_VALID;
          }
    
          mb.subBuf(g_miScaling.scale(Position{ x, y } -pu.lumaPos()), g_miScaling.scale(Size(puWidth, puHeight))).fill(mi);
          }
        }
    
      return true;
      }
    
    void PU::spanMotionInfo( PredictionUnit &pu, const MergeCtx &mrgCtx )
    {
      MotionBuf mb = pu.getMotionBuf();
    
      if( !pu.mergeFlag || pu.mergeType == MRG_TYPE_DEFAULT_N )
      {
        MotionInfo mi;
    
        mi.isInter  = CU::isInter( *pu.cu );
        mi.sliceIdx = pu.cu->slice->getIndependentSliceIdx();
    
        if( mi.isInter )
        {
          mi.interDir = pu.interDir;
    
          for( int i = 0; i < NUM_REF_PIC_LIST_01; i++ )
          {
            mi.mv[i]     = pu.mv[i];
            mi.refIdx[i] = pu.refIdx[i];
          }
        }
    
        if( pu.cu->affine )
        {
          for( int y = 0; y < mb.height; y++ )
          {
            for( int x = 0; x < mb.width; x++ )
            {
              MotionInfo &dest = mb.at( x, y );
              dest.isInter  = mi.isInter;
              dest.interDir = mi.interDir;
              dest.sliceIdx = mi.sliceIdx;
              for( int i = 0; i < NUM_REF_PIC_LIST_01; i++ )
              {
                if( mi.refIdx[i] == -1 )
                {
                  dest.mv[i] = Mv();
                }
                dest.refIdx[i] = mi.refIdx[i];
              }
            }
          }
        }
        else
        {
          mb.fill( mi );
        }
      }
      else if (pu.mergeType == MRG_TYPE_SUBPU_ATMVP)
      {
        CHECK(mrgCtx.subPuMvpMiBuf.area() == 0 || !mrgCtx.subPuMvpMiBuf.buf, "Buffer not initialized");
        mb.copyFrom(mrgCtx.subPuMvpMiBuf);
      }
      else
      {
    
        if( isBipredRestriction( pu ) )
        {
          for( int y = 0; y < mb.height; y++ )
          {
            for( int x = 0; x < mb.width; x++ )
            {
              MotionInfo &mi = mb.at( x, y );
              if( mi.interDir == 3 )
              {
                mi.interDir  = 1;
                mi.mv    [1] = Mv();
                mi.refIdx[1] = NOT_VALID;
              }
            }
          }
        }
      }
    }
    
    void PU::applyImv( PredictionUnit& pu, MergeCtx &mrgCtx, InterPrediction *interPred )
    {
      if( !pu.mergeFlag )
      {
        unsigned imvShift = pu.cu->imv << 1;
        if( pu.interDir != 2 /* PRED_L1 */ )
        {
          if (pu.cu->imv)
          {
    
    #if !REMOVE_MV_ADAPT_PREC
            CHECK(pu.mvd[0].highPrec, "Motion vector difference should never be high precision");
    #endif
    
            pu.mvd[0] = Mv( pu.mvd[0].hor << imvShift, pu.mvd[0].ver << imvShift );
          }
          unsigned mvp_idx = pu.mvpIdx[0];
          AMVPInfo amvpInfo;
          PU::fillMvpCand(pu, REF_PIC_LIST_0, pu.refIdx[0], amvpInfo);
          pu.mvpNum[0] = amvpInfo.numCand;
          pu.mvpIdx[0] = mvp_idx;
          pu.mv    [0] = amvpInfo.mvCand[mvp_idx] + pu.mvd[0];
    
    #if REMOVE_MV_ADAPT_PREC
          pu.mv[0].hor = pu.mv[0].hor << VCEG_AZ07_MV_ADD_PRECISION_BIT_FOR_STORE;
          pu.mv[0].ver = pu.mv[0].ver << VCEG_AZ07_MV_ADD_PRECISION_BIT_FOR_STORE;
    #endif
    
        }
    
        if (pu.interDir != 1 /* PRED_L0 */)
        {
          if( !( pu.cu->cs->slice->getMvdL1ZeroFlag() && pu.interDir == 3 ) && pu.cu->imv )/* PRED_BI */
          {
    
    #if !REMOVE_MV_ADAPT_PREC
            CHECK(pu.mvd[1].highPrec, "Motion vector difference should never be high precision");
    #endif
    
            pu.mvd[1] = Mv( pu.mvd[1].hor << imvShift, pu.mvd[1].ver << imvShift );
          }
          unsigned mvp_idx = pu.mvpIdx[1];
          AMVPInfo amvpInfo;
          PU::fillMvpCand(pu, REF_PIC_LIST_1, pu.refIdx[1], amvpInfo);
          pu.mvpNum[1] = amvpInfo.numCand;
          pu.mvpIdx[1] = mvp_idx;
          pu.mv    [1] = amvpInfo.mvCand[mvp_idx] + pu.mvd[1];
    
    #if REMOVE_MV_ADAPT_PREC
          pu.mv[1].hor = pu.mv[1].hor << VCEG_AZ07_MV_ADD_PRECISION_BIT_FOR_STORE;
          pu.mv[1].ver = pu.mv[1].ver << VCEG_AZ07_MV_ADD_PRECISION_BIT_FOR_STORE;
    #endif
    
        }
      }
      else
      {
        // this function is never called for merge
        THROW("unexpected");
        PU::getInterMergeCandidates ( pu, mrgCtx );
        PU::restrictBiPredMergeCands( pu, mrgCtx );
    
        mrgCtx.setMergeInfo( pu, pu.mergeIdx );
      }
    
      PU::spanMotionInfo( pu, mrgCtx );
    }
    
    bool PU::isBiPredFromDifferentDir( const PredictionUnit& pu )
    {
      if ( pu.refIdx[0] >= 0 && pu.refIdx[1] >= 0 )
      {
        const int iPOC0 = pu.cu->slice->getRefPOC( REF_PIC_LIST_0, pu.refIdx[0] );
        const int iPOC1 = pu.cu->slice->getRefPOC( REF_PIC_LIST_1, pu.refIdx[1] );
        const int iPOC  = pu.cu->slice->getPOC();
        if ( (iPOC - iPOC0)*(iPOC - iPOC1) < 0 )
        {
          return true;
        }
      }
    
      return false;
    }
    
    void PU::restrictBiPredMergeCands( const PredictionUnit &pu, MergeCtx& mergeCtx )
    {
      if( PU::isBipredRestriction( pu ) )
      {
        for( uint32_t mergeCand = 0; mergeCand < mergeCtx.numValidMergeCand; ++mergeCand )
        {
          if( mergeCtx.interDirNeighbours[ mergeCand ] == 3 )
          {
            mergeCtx.interDirNeighbours[ mergeCand ] = 1;
            mergeCtx.mvFieldNeighbours[( mergeCand << 1 ) + 1].setMvField( Mv( 0, 0 ), -1 );
    
    #if JVET_L0646_GBI
            mergeCtx.GBiIdx[mergeCand] = GBI_DEFAULT;
    #endif
    
          }
        }
      }
    }
    
    void CU::resetMVDandMV2Int( CodingUnit& cu, InterPrediction *interPred )
    {
      for( auto &pu : CU::traversePUs( cu ) )
      {
        MergeCtx mrgCtx;
    
        if( !pu.mergeFlag )
        {
          unsigned imvShift = cu.imv << 1;
          if( pu.interDir != 2 /* PRED_L1 */ )
          {
            Mv mv        = pu.mv[0];
            Mv mvPred;
            AMVPInfo amvpInfo;
            PU::fillMvpCand(pu, REF_PIC_LIST_0, pu.refIdx[0], amvpInfo);
            pu.mvpNum[0] = amvpInfo.numCand;
    
            mvPred       = amvpInfo.mvCand[pu.mvpIdx[0]];
            roundMV      ( mv, imvShift );
            pu.mv[0]     = mv;
            Mv mvDiff    = mv - mvPred;
            pu.mvd[0]    = mvDiff;
          }
          if( pu.interDir != 1 /* PRED_L0 */ )
          {
            Mv mv        = pu.mv[1];
            Mv mvPred;
            AMVPInfo amvpInfo;
            PU::fillMvpCand(pu, REF_PIC_LIST_1, pu.refIdx[1], amvpInfo);
            pu.mvpNum[1] = amvpInfo.numCand;
    
            mvPred       = amvpInfo.mvCand[pu.mvpIdx[1]];
            roundMV      ( mv, imvShift );
            Mv mvDiff    = mv - mvPred;
    
            if( pu.cu->cs->slice->getMvdL1ZeroFlag() && pu.interDir == 3 /* PRED_BI */ )
            {
              pu.mvd[1] = Mv();
              mv = mvPred;
            }
            else
            {
              pu.mvd[1] = mvDiff;
            }
            pu.mv[1] = mv;
          }
    
        }
        else
        {
            PU::getInterMergeCandidates ( pu, mrgCtx );
            PU::restrictBiPredMergeCands( pu, mrgCtx );
    
            mrgCtx.setMergeInfo( pu, pu.mergeIdx );
        }
    
        PU::spanMotionInfo( pu, mrgCtx );
      }
    }
    
    bool CU::hasSubCUNonZeroMVd( const CodingUnit& cu )
    {
      bool bNonZeroMvd = false;
    
      for( const auto &pu : CU::traversePUs( cu ) )
      {
        if( ( !pu.mergeFlag ) && ( !cu.skip ) )
        {
          if( pu.interDir != 2 /* PRED_L1 */ )
          {
            bNonZeroMvd |= pu.mvd[REF_PIC_LIST_0].getHor() != 0;
            bNonZeroMvd |= pu.mvd[REF_PIC_LIST_0].getVer() != 0;
          }
          if( pu.interDir != 1 /* PRED_L0 */ )
          {
            if( !pu.cu->cs->slice->getMvdL1ZeroFlag() || pu.interDir != 3 /* PRED_BI */ )
            {
              bNonZeroMvd |= pu.mvd[REF_PIC_LIST_1].getHor() != 0;
              bNonZeroMvd |= pu.mvd[REF_PIC_LIST_1].getVer() != 0;
            }
          }
        }
      }
    
      return bNonZeroMvd;
    }
    
    int CU::getMaxNeighboriMVCandNum( const CodingStructure& cs, const Position& pos )
    {
      const int  numDefault     = 0;
      int        maxImvNumCand  = 0;
    
      // Get BCBP of left PU
    #if HEVC_TILES_WPP
      const CodingUnit *cuLeft  = cs.getCURestricted( pos.offset( -1, 0 ), cs.slice->getIndependentSliceIdx(), cs.picture->tileMap->getTileIdxMap( pos ), CH_L );
    #else
      const CodingUnit *cuLeft  = cs.getCURestricted( pos.offset( -1, 0 ), cs.slice->getIndependentSliceIdx(), CH_L );
    #endif
      maxImvNumCand = ( cuLeft ) ? cuLeft->imvNumCand : numDefault;
    
      // Get BCBP of above PU
    #if HEVC_TILES_WPP
      const CodingUnit *cuAbove = cs.getCURestricted( pos.offset( 0, -1 ), cs.slice->getIndependentSliceIdx(), cs.picture->tileMap->getTileIdxMap( pos ), CH_L );
    #else
      const CodingUnit *cuAbove = cs.getCURestricted( pos.offset( 0, -1 ), cs.slice->getIndependentSliceIdx(), CH_L );
    #endif
      maxImvNumCand = std::max( maxImvNumCand, ( cuAbove ) ? cuAbove->imvNumCand : numDefault );
    
      return maxImvNumCand;
    }
    
    
    #if JVET_L0646_GBI
    bool CU::isGBiIdxCoded( const CodingUnit &cu )
    {
      if( cu.cs->sps->getSpsNext().getUseGBi() == false )
      {
        CHECK(cu.GBiIdx != GBI_DEFAULT, "Error: cu.GBiIdx != GBI_DEFAULT");
        return false;
      }
    
      if( cu.predMode == MODE_INTRA || cu.cs->slice->isInterP() )
      {
        return false;
      }
    
      if( cu.lwidth() * cu.lheight() < GBI_SIZE_CONSTRAINT )
      {
        return false;
      }
    
      if( cu.firstPU->interDir == 3 && !cu.firstPU->mergeFlag )
      {
        return true;
      }
    
      return false;
    }
    
    uint8_t CU::getValidGbiIdx( const CodingUnit &cu )
    {
      if( cu.firstPU->interDir == 3 && !cu.firstPU->mergeFlag )
      {
        return cu.GBiIdx;
      }
      else if( cu.firstPU->interDir == 3 && cu.firstPU->mergeFlag && cu.firstPU->mergeType == MRG_TYPE_DEFAULT_N )
      {
        // This is intended to do nothing here.
      }
      else if( cu.firstPU->mergeFlag && cu.firstPU->mergeType == MRG_TYPE_SUBPU_ATMVP )
      {
        CHECK(cu.GBiIdx != GBI_DEFAULT, " cu.GBiIdx != GBI_DEFAULT ");
      }
      else
      {
        CHECK(cu.GBiIdx != GBI_DEFAULT, " cu.GBiIdx != GBI_DEFAULT ");
      }
    
      return GBI_DEFAULT;
    }
    
    void CU::setGbiIdx( CodingUnit &cu, uint8_t uh )
    {
      int8_t uhCnt = 0;
    
      if( cu.firstPU->interDir == 3 && !cu.firstPU->mergeFlag )
      {
        cu.GBiIdx = uh;
        ++uhCnt;
      }
      else if( cu.firstPU->interDir == 3 && cu.firstPU->mergeFlag && cu.firstPU->mergeType == MRG_TYPE_DEFAULT_N )
      {
        // This is intended to do nothing here.
      }
      else if( cu.firstPU->mergeFlag && cu.firstPU->mergeType == MRG_TYPE_SUBPU_ATMVP )
      {
        cu.GBiIdx = GBI_DEFAULT;
      }
      else
      {
        cu.GBiIdx = GBI_DEFAULT;
      }
    
      CHECK(uhCnt <= 0, " uhCnt <= 0 ");
    }
    
    uint8_t CU::deriveGbiIdx( uint8_t gbiLO, uint8_t gbiL1 )
    {
      if( gbiLO == gbiL1 )
      {
        return gbiLO;
      }
      const int8_t w0 = getGbiWeight(gbiLO, REF_PIC_LIST_0);
      const int8_t w1 = getGbiWeight(gbiL1, REF_PIC_LIST_1);
      const int8_t th = g_GbiWeightBase >> 1;
      const int8_t off = 1;
    
      if( w0 == w1 || (w0 < (th - off) && w1 < (th - off)) || (w0 >(th + off) && w1 >(th + off)) )
      {
        return GBI_DEFAULT;
      }
      else
      {
        if( w0 > w1 )
        {
          return ( w0 >= th ? gbiLO : gbiL1 );
        }
        else
        {
          return ( w1 >= th ? gbiL1 : gbiLO );
        }
      }
    }
    #endif
    
    
    // TU tools
    
    #if HEVC_USE_4x4_DSTVII
    bool TU::useDST(const TransformUnit &tu, const ComponentID &compID)
    {
      return isLuma(compID) && tu.cu->predMode == MODE_INTRA;
    }
    
    #endif
    
    bool TU::isNonTransformedResidualRotated(const TransformUnit &tu, const ComponentID &compID)
    {
      return tu.cs->sps->getSpsRangeExtension().getTransformSkipRotationEnabledFlag() && tu.blocks[compID].width == 4 && tu.cu->predMode == MODE_INTRA;
    }
    
    bool TU::getCbf( const TransformUnit &tu, const ComponentID &compID )
    {
    #if ENABLE_BMS
      return getCbfAtDepth( tu, compID, tu.depth );
    #else
      return tu.cbf[compID];
    #endif
    }
    
    #if ENABLE_BMS
    bool TU::getCbfAtDepth(const TransformUnit &tu, const ComponentID &compID, const unsigned &depth)
    {
      return ((tu.cbf[compID] >> depth) & 1) == 1;
    }
    
    void TU::setCbfAtDepth(TransformUnit &tu, const ComponentID &compID, const unsigned &depth, const bool &cbf)
    {
      // first clear the CBF at the depth
      tu.cbf[compID] &= ~(1  << depth);
      // then set the CBF
      tu.cbf[compID] |= ((cbf ? 1 : 0) << depth);
    }
    #else
    void TU::setCbf( TransformUnit &tu, const ComponentID &compID, const bool &cbf )
    {
      tu.cbf[compID] = cbf;
    }
    #endif
    
    bool TU::hasTransformSkipFlag(const CodingStructure& cs, const CompArea& area)
    {
      uint32_t transformSkipLog2MaxSize = cs.pps->getPpsRangeExtension().getLog2MaxTransformSkipBlockSize();
    
      if( cs.pcv->rectCUs )
      {
        return ( area.width * area.height <= (1 << ( transformSkipLog2MaxSize << 1 )) );
      }
      return ( area.width <= (1 << transformSkipLog2MaxSize) );
    }
    
    uint32_t TU::getGolombRiceStatisticsIndex(const TransformUnit &tu, const ComponentID &compID)
    {
      const bool transformSkip    = tu.transformSkip[compID];
      const bool transquantBypass = tu.cu->transQuantBypass;
    
      //--------
    
      const uint32_t channelTypeOffset = isChroma(compID) ? 2 : 0;
      const uint32_t nonTransformedOffset = (transformSkip || transquantBypass) ? 1 : 0;
    
      //--------
    
      const uint32_t selectedIndex = channelTypeOffset + nonTransformedOffset;
      CHECK( selectedIndex >= RExt__GOLOMB_RICE_ADAPTATION_STATISTICS_SETS, "Invalid golomb rice adaptation statistics set" );
    
      return selectedIndex;
    }
    
    #if HEVC_USE_MDCS
    uint32_t TU::getCoefScanIdx(const TransformUnit &tu, const ComponentID &compID)
    {
      //------------------------------------------------
    
      //this mechanism is available for intra only
    
      if( !CU::isIntra( *tu.cu ) )
      {
        return SCAN_DIAG;
      }
    
      //------------------------------------------------
    
      //check that MDCS can be used for this TU
    
    
      const CompArea &area      = tu.blocks[compID];
      const SPS &sps            = *tu.cs->sps;
      const ChromaFormat format = sps.getChromaFormatIdc();
    
    
      const uint32_t maximumWidth  = MDCS_MAXIMUM_WIDTH  >> getComponentScaleX(compID, format);
      const uint32_t maximumHeight = MDCS_MAXIMUM_HEIGHT >> getComponentScaleY(compID, format);
    
      if ((area.width > maximumWidth) || (area.height > maximumHeight))
      {
        return SCAN_DIAG;
      }
    
      //------------------------------------------------
    
      //otherwise, select the appropriate mode
    
      const PredictionUnit &pu = *tu.cs->getPU( area.pos(), toChannelType( compID ) );
    
      uint32_t uiDirMode = PU::getFinalIntraMode(pu, toChannelType(compID));
    
      //------------------
    
           if (abs((int) uiDirMode - VER_IDX) <= MDCS_ANGLE_LIMIT)
      {
        return SCAN_HOR;
      }
      else if (abs((int) uiDirMode - HOR_IDX) <= MDCS_ANGLE_LIMIT)
      {
        return SCAN_VER;
      }
      else
      {
        return SCAN_DIAG;
      }
    }
    
    #endif
    bool TU::hasCrossCompPredInfo( const TransformUnit &tu, const ComponentID &compID )
    {
      return ( isChroma(compID) && tu.cs->pps->getPpsRangeExtension().getCrossComponentPredictionEnabledFlag() && TU::getCbf( tu, COMPONENT_Y ) &&
             ( CU::isInter(*tu.cu) || PU::isChromaIntraModeCrossCheckMode( *tu.cs->getPU( tu.blocks[compID].pos(), toChannelType( compID ) ) ) ) );
    }
    
    uint32_t TU::getNumNonZeroCoeffsNonTS( const TransformUnit& tu, const bool bLuma, const bool bChroma )
    {
      uint32_t count = 0;
      for( uint32_t i = 0; i < ::getNumberValidTBlocks( *tu.cs->pcv ); i++ )
      {
        if( tu.blocks[i].valid() && !tu.transformSkip[i] && TU::getCbf( tu, ComponentID( i ) ) )
        {
          if( isLuma  ( tu.blocks[i].compID ) && !bLuma   ) continue;
          if( isChroma( tu.blocks[i].compID ) && !bChroma ) continue;
    
          uint32_t area = tu.blocks[i].area();
          const TCoeff* coeff = tu.getCoeffs( ComponentID( i ) ).buf;
          for( uint32_t j = 0; j < area; j++ )
          {
            count += coeff[j] != 0;
          }
        }
      }
      return count;
    }
    
    bool TU::needsSqrt2Scale( const Size& size )
    {
      return (((g_aucLog2[size.width] + g_aucLog2[size.height]) & 1) == 1);
    }
    
    #if HM_QTBT_AS_IN_JEM_QUANT
    
    bool TU::needsBlockSizeTrafoScale( const Size& size )
    {
      return needsSqrt2Scale( size ) || isNonLog2BlockSize( size );
    }
    #else
    bool TU::needsQP3Offset(const TransformUnit &tu, const ComponentID &compID)
    {