Skip to content
Snippets Groups Projects
UnitTools.cpp 160 KiB
Newer Older
  • Learn to ignore specific revisions
  • 
      return;
    }
    
    const int getAvailableAffineNeighboursForLeftPredictor( const PredictionUnit &pu, const PredictionUnit* npu[] )
    {
      const Position posLB = pu.Y().bottomLeft();
      int num = 0;
    
      const PredictionUnit *puLeftBottom = pu.cs->getPURestricted( posLB.offset( -1, 1 ), pu, pu.chType );
      if ( puLeftBottom && puLeftBottom->cu->affine
    #if JVET_L0369_SUBBLOCK_MERGE
        && puLeftBottom->mergeType == MRG_TYPE_DEFAULT_N
    #endif
        )
      {
        npu[num++] = puLeftBottom;
        return num;
      }
    
      const PredictionUnit* puLeft = pu.cs->getPURestricted( posLB.offset( -1, 0 ), pu, pu.chType );
      if ( puLeft && puLeft->cu->affine
    #if JVET_L0369_SUBBLOCK_MERGE
        && puLeft->mergeType == MRG_TYPE_DEFAULT_N
    #endif
        )
      {
        npu[num++] = puLeft;
        return num;
      }
    
      return num;
    }
    
    const int getAvailableAffineNeighboursForAbovePredictor( const PredictionUnit &pu, const PredictionUnit* npu[], int numAffNeighLeft )
    {
      const Position posLT = pu.Y().topLeft();
      const Position posRT = pu.Y().topRight();
      int num = numAffNeighLeft;
    
      const PredictionUnit* puAboveRight = pu.cs->getPURestricted( posRT.offset( 1, -1 ), pu, pu.chType );
      if ( puAboveRight && puAboveRight->cu->affine
    #if JVET_L0369_SUBBLOCK_MERGE
        && puAboveRight->mergeType == MRG_TYPE_DEFAULT_N
    #endif
        )
      {
        npu[num++] = puAboveRight;
        return num;
      }
    
      const PredictionUnit* puAbove = pu.cs->getPURestricted( posRT.offset( 0, -1 ), pu, pu.chType );
      if ( puAbove && puAbove->cu->affine
    #if JVET_L0369_SUBBLOCK_MERGE
        && puAbove->mergeType == MRG_TYPE_DEFAULT_N
    #endif
        )
      {
        npu[num++] = puAbove;
        return num;
      }
    
      const PredictionUnit *puAboveLeft = pu.cs->getPURestricted( posLT.offset( -1, -1 ), pu, pu.chType );
      if ( puAboveLeft && puAboveLeft->cu->affine
    #if JVET_L0369_SUBBLOCK_MERGE
        && puAboveLeft->mergeType == MRG_TYPE_DEFAULT_N
    #endif
        )
      {
        npu[num++] = puAboveLeft;
        return num;
      }
    
      return num;
    }
    
    void PU::getAffineMergeCand( const PredictionUnit &pu, AffineMergeCtx& affMrgCtx, const int mrgCandIdx )
    {
      const CodingStructure &cs = *pu.cs;
      const Slice &slice = *pu.cs->slice;
      const uint32_t maxNumAffineMergeCand = slice.getMaxNumAffineMergeCand();
    
      for ( int i = 0; i < maxNumAffineMergeCand; i++ )
      {
        for ( int mvNum = 0; mvNum < 3; mvNum++ )
        {
          affMrgCtx.mvFieldNeighbours[(i << 1) + 0][mvNum].setMvField( Mv(), -1 );
          affMrgCtx.mvFieldNeighbours[(i << 1) + 1][mvNum].setMvField( Mv(), -1 );
        }
        affMrgCtx.interDirNeighbours[i] = 0;
        affMrgCtx.affineType[i] = AFFINEMODEL_4PARAM;
    #if JVET_L0369_SUBBLOCK_MERGE
        affMrgCtx.mergeType[i] = MRG_TYPE_DEFAULT_N;
    #endif
    #if JVET_L0646_GBI
        affMrgCtx.GBiIdx[i] = GBI_DEFAULT;
    #endif
      }
    
      affMrgCtx.numValidMergeCand = 0;
      affMrgCtx.maxNumMergeCand = maxNumAffineMergeCand;
    
    #if JVET_L0369_SUBBLOCK_MERGE ///> insert ATMVP candidate
      bool enableSubPuMvp = slice.getSPS()->getSpsNext().getUseSubPuMvp();
      bool isAvailableSubPu = false;
      if ( enableSubPuMvp && slice.getEnableTMVPFlag() )
      {
        MergeCtx mrgCtx = *affMrgCtx.mrgCtx;
        bool tmpLICFlag = false;
    
        CHECK( mrgCtx.subPuMvpMiBuf.area() == 0 || !mrgCtx.subPuMvpMiBuf.buf, "Buffer not initialized" );
        mrgCtx.subPuMvpMiBuf.fill( MotionInfo() );
    
        int pos = 0;
        // Get spatial MV
        const Position posCurRT = pu.Y().topRight();
        const Position posCurLB = pu.Y().bottomLeft();
        MotionInfo miAbove, miLeft, miAboveRight, miBelowLeft;
    
        //left
        const PredictionUnit* puLeft = cs.getPURestricted( posCurLB.offset( -1, 0 ), pu, pu.chType );
        const bool isAvailableA1 = puLeft && isDiffMER( pu, *puLeft ) && pu.cu != puLeft->cu && CU::isInter( *puLeft->cu );
        if ( isAvailableA1 )
        {
          miLeft = puLeft->getMotionInfo( posCurLB.offset( -1, 0 ) );
          // get Inter Dir
          mrgCtx.interDirNeighbours[pos] = miLeft.interDir;
    
          // get Mv from Left
          mrgCtx.mvFieldNeighbours[pos << 1].setMvField( miLeft.mv[0], miLeft.refIdx[0] );
    
          if ( slice.isInterB() )
          {
            mrgCtx.mvFieldNeighbours[(pos << 1) + 1].setMvField( miLeft.mv[1], miLeft.refIdx[1] );
          }
          pos++;
        }
    
        // above
        const PredictionUnit *puAbove = cs.getPURestricted( posCurRT.offset( 0, -1 ), pu, pu.chType );
        bool isAvailableB1 = puAbove && isDiffMER( pu, *puAbove ) && pu.cu != puAbove->cu && CU::isInter( *puAbove->cu );
        if ( isAvailableB1 )
        {
          miAbove = puAbove->getMotionInfo( posCurRT.offset( 0, -1 ) );
    
          if ( !isAvailableA1 || (miAbove != miLeft) )
          {
            // get Inter Dir
            mrgCtx.interDirNeighbours[pos] = miAbove.interDir;
            // get Mv from Left
            mrgCtx.mvFieldNeighbours[pos << 1].setMvField( miAbove.mv[0], miAbove.refIdx[0] );
    
            if ( slice.isInterB() )
            {
              mrgCtx.mvFieldNeighbours[(pos << 1) + 1].setMvField( miAbove.mv[1], miAbove.refIdx[1] );
            }
    
            pos++;
          }
        }
    
        // above right
        const PredictionUnit *puAboveRight = cs.getPURestricted( posCurRT.offset( 1, -1 ), pu, pu.chType );
        bool isAvailableB0 = puAboveRight && isDiffMER( pu, *puAboveRight ) && CU::isInter( *puAboveRight->cu );
        if ( isAvailableB0 )
        {
          miAboveRight = puAboveRight->getMotionInfo( posCurRT.offset( 1, -1 ) );
    
    #if HM_JEM_MERGE_CANDS
          if ( (!isAvailableB1 || (miAbove != miAboveRight)) && (!isAvailableA1 || (miLeft != miAboveRight)) )
    #else
          if ( !isAvailableB1 || (miAbove != miAboveRight) )
    #endif
          {
            // get Inter Dir
            mrgCtx.interDirNeighbours[pos] = miAboveRight.interDir;
            // get Mv from Left
            mrgCtx.mvFieldNeighbours[pos << 1].setMvField( miAboveRight.mv[0], miAboveRight.refIdx[0] );
    
            if ( slice.isInterB() )
            {
              mrgCtx.mvFieldNeighbours[(pos << 1) + 1].setMvField( miAboveRight.mv[1], miAboveRight.refIdx[1] );
            }
    
            pos++;
          }
        }
    
        //left bottom
        const PredictionUnit *puLeftBottom = cs.getPURestricted( posCurLB.offset( -1, 1 ), pu, pu.chType );
        bool isAvailableA0 = puLeftBottom && isDiffMER( pu, *puLeftBottom ) && CU::isInter( *puLeftBottom->cu );
        if ( isAvailableA0 )
        {
          miBelowLeft = puLeftBottom->getMotionInfo( posCurLB.offset( -1, 1 ) );
    
    #if HM_JEM_MERGE_CANDS
          if ( (!isAvailableA1 || (miBelowLeft != miLeft)) && (!isAvailableB1 || (miBelowLeft != miAbove)) && (!isAvailableB0 || (miBelowLeft != miAboveRight)) )
    #else
          if ( !isAvailableA1 || (miBelowLeft != miLeft) )
    #endif
          {
            // get Inter Dir
            mrgCtx.interDirNeighbours[pos] = miBelowLeft.interDir;
            // get Mv from Bottom-Left
            mrgCtx.mvFieldNeighbours[pos << 1].setMvField( miBelowLeft.mv[0], miBelowLeft.refIdx[0] );
    
            if ( slice.isInterB() )
            {
              mrgCtx.mvFieldNeighbours[(pos << 1) + 1].setMvField( miBelowLeft.mv[1], miBelowLeft.refIdx[1] );
            }
            pos++;
          }
        }
        mrgCtx.numValidMergeCand = pos;
    
        isAvailableSubPu = getInterMergeSubPuMvpCand( pu, mrgCtx, tmpLICFlag, pos
    #if JVET_L0054_MMVD
          , 0
    #endif
        );
        if ( isAvailableSubPu )
        {
          for ( int mvNum = 0; mvNum < 3; mvNum++ )
          {
            affMrgCtx.mvFieldNeighbours[(affMrgCtx.numValidMergeCand << 1) + 0][mvNum].setMvField( mrgCtx.mvFieldNeighbours[(pos << 1) + 0].mv, mrgCtx.mvFieldNeighbours[(pos << 1) + 0].refIdx );
            affMrgCtx.mvFieldNeighbours[(affMrgCtx.numValidMergeCand << 1) + 1][mvNum].setMvField( mrgCtx.mvFieldNeighbours[(pos << 1) + 1].mv, mrgCtx.mvFieldNeighbours[(pos << 1) + 1].refIdx );
          }
          affMrgCtx.interDirNeighbours[affMrgCtx.numValidMergeCand] = mrgCtx.interDirNeighbours[pos];
    
          affMrgCtx.affineType[affMrgCtx.numValidMergeCand] = AFFINE_MODEL_NUM;
          affMrgCtx.mergeType[affMrgCtx.numValidMergeCand] = MRG_TYPE_SUBPU_ATMVP;
          if ( affMrgCtx.numValidMergeCand == mrgCandIdx )
          {
            return;
          }
    
          affMrgCtx.numValidMergeCand++;
    
          // early termination
          if ( affMrgCtx.numValidMergeCand == maxNumAffineMergeCand )
          {
            return;
          }
        }
      }
    #endif
    
    #if JVET_L0369_SUBBLOCK_MERGE
      if ( slice.getSPS()->getSpsNext().getUseAffine() )
      {
    #endif
        ///> Start: inherited affine candidates
        const PredictionUnit* npu[5];
        int numAffNeighLeft = getAvailableAffineNeighboursForLeftPredictor( pu, npu );
        int numAffNeigh = getAvailableAffineNeighboursForAbovePredictor( pu, npu, numAffNeighLeft );
        for ( int idx = 0; idx < numAffNeigh; idx++ )
        {
          // derive Mv from Neigh affine PU
          Mv cMv[2][3];
          const PredictionUnit* puNeigh = npu[idx];
          pu.cu->affineType = puNeigh->cu->affineType;
          if ( puNeigh->interDir != 2 )
          {
            xInheritedAffineMv( pu, puNeigh, REF_PIC_LIST_0, cMv[0] );
          }
          if ( slice.isInterB() )
          {
            if ( puNeigh->interDir != 1 )
            {
              xInheritedAffineMv( pu, puNeigh, REF_PIC_LIST_1, cMv[1] );
            }
          }
    
          for ( int mvNum = 0; mvNum < 3; mvNum++ )
          {
            affMrgCtx.mvFieldNeighbours[(affMrgCtx.numValidMergeCand << 1) + 0][mvNum].setMvField( cMv[0][mvNum], puNeigh->refIdx[0] );
            affMrgCtx.mvFieldNeighbours[(affMrgCtx.numValidMergeCand << 1) + 1][mvNum].setMvField( cMv[1][mvNum], puNeigh->refIdx[1] );
          }
          affMrgCtx.interDirNeighbours[affMrgCtx.numValidMergeCand] = puNeigh->interDir;
          affMrgCtx.affineType[affMrgCtx.numValidMergeCand] = (EAffineModel)(puNeigh->cu->affineType);
    #if JVET_L0646_GBI
          affMrgCtx.GBiIdx[affMrgCtx.numValidMergeCand] = puNeigh->cu->GBiIdx;
    #endif
    
          if ( affMrgCtx.numValidMergeCand == mrgCandIdx )
          {
            return;
          }
    
          // early termination
          affMrgCtx.numValidMergeCand++;
          if ( affMrgCtx.numValidMergeCand == maxNumAffineMergeCand )
          {
            return;
          }
        }
        ///> End: inherited affine candidates
    
        ///> Start: Constructed affine candidates
        {
          MotionInfo mi[4];
    
          bool isAvailable[4] = { false };
    
    
          // control point: LT B2->B3->A2
          const Position posLT[3] = { pu.Y().topLeft().offset( -1, -1 ), pu.Y().topLeft().offset( 0, -1 ), pu.Y().topLeft().offset( -1, 0 ) };
          for ( int i = 0; i < 3; i++ )
          {
            const Position pos = posLT[i];
            const PredictionUnit* puNeigh = cs.getPURestricted( pos, pu, pu.chType );
            if ( puNeigh && CU::isInter( *puNeigh->cu ) )
            {
    
              mi[0] = puNeigh->getMotionInfo( pos );
    #if !REMOVE_MV_ADAPT_PREC
              mi[0].mv[0].setHighPrec();
              mi[0].mv[1].setHighPrec();
    #endif
              break;
            }
          }
    
          // control point: RT B1->B0
          const Position posRT[2] = { pu.Y().topRight().offset( 0, -1 ), pu.Y().topRight().offset( 1, -1 ) };
          for ( int i = 0; i < 2; i++ )
          {
            const Position pos = posRT[i];
            const PredictionUnit* puNeigh = cs.getPURestricted( pos, pu, pu.chType );
            if ( puNeigh && CU::isInter( *puNeigh->cu ) )
            {
    
              mi[1] = puNeigh->getMotionInfo( pos );
    #if !REMOVE_MV_ADAPT_PREC
              mi[1].mv[0].setHighPrec();
              mi[1].mv[1].setHighPrec();
    #endif
              break;
            }
          }
    
          // control point: LB A1->A0
          const Position posLB[2] = { pu.Y().bottomLeft().offset( -1, 0 ), pu.Y().bottomLeft().offset( -1, 1 ) };
          for ( int i = 0; i < 2; i++ )
          {
            const Position pos = posLB[i];
            const PredictionUnit* puNeigh = cs.getPURestricted( pos, pu, pu.chType );
            if ( puNeigh && CU::isInter( *puNeigh->cu ) )
            {
    
              mi[2] = puNeigh->getMotionInfo( pos );
    #if !REMOVE_MV_ADAPT_PREC
              mi[2].mv[0].setHighPrec();
              mi[2].mv[1].setHighPrec();
    #endif
              break;
            }
          }
    
          // control point: RB
          if ( slice.getEnableTMVPFlag() )
          {
            //>> MTK colocated-RightBottom
            // offset the pos to be sure to "point" to the same position the uiAbsPartIdx would've pointed to
            Position posRB = pu.Y().bottomRight().offset( -3, -3 );
    
            const PreCalcValues& pcv = *cs.pcv;
            Position posC0;
            bool C0Avail = false;
    
            if ( ((posRB.x + pcv.minCUWidth) < pcv.lumaWidth) && ((posRB.y + pcv.minCUHeight) < pcv.lumaHeight) )
            {
              Position posInCtu( posRB.x & pcv.maxCUWidthMask, posRB.y & pcv.maxCUHeightMask );
    
              if ( (posInCtu.x + 4 < pcv.maxCUWidth) &&  // is not at the last column of CTU
                (posInCtu.y + 4 < pcv.maxCUHeight) )     // is not at the last row    of CTU
              {
                posC0 = posRB.offset( 4, 4 );
                C0Avail = true;
              }
              else if ( posInCtu.x + 4 < pcv.maxCUWidth ) // is not at the last column of CTU But is last row of CTU
              {
                posC0 = posRB.offset( 4, 4 );
                // in the reference the CTU address is not set - thus probably resulting in no using this C0 possibility
              }
              else if ( posInCtu.y + 4 < pcv.maxCUHeight ) // is not at the last row of CTU But is last column of CTU
              {
                posC0 = posRB.offset( 4, 4 );
                C0Avail = true;
              }
              else //is the right bottom corner of CTU
              {
                posC0 = posRB.offset( 4, 4 );
                // same as for last column but not last row
              }
            }
    
            Mv        cColMv;
            int       refIdx = 0;
            bool      bExistMV = C0Avail && getColocatedMVP( pu, REF_PIC_LIST_0, posC0, cColMv, refIdx );
            if ( bExistMV )
            {
              mi[3].mv[0] = cColMv;
    #if !REMOVE_MV_ADAPT_PREC
              mi[3].mv[0].setHighPrec();
    #endif
              mi[3].refIdx[0] = refIdx;
              mi[3].interDir = 1;
    
            }
    
            if ( slice.isInterB() )
            {
              bExistMV = C0Avail && getColocatedMVP( pu, REF_PIC_LIST_1, posC0, cColMv, refIdx );
              if ( bExistMV )
              {
                mi[3].mv[1] = cColMv;
    #if !REMOVE_MV_ADAPT_PREC
                mi[3].mv[1].setHighPrec();
    #endif
                mi[3].refIdx[1] = refIdx;
                mi[3].interDir |= 2;
    
              }
            }
          }
    
          //-------------------  insert model  -------------------//
          int order[6] = { 0, 1, 2, 3, 4, 5 };
          int modelNum = 6;
          int model[6][4] = {
            { 0, 1, 2 },          // 0:  LT, RT, LB
            { 0, 1, 3 },          // 1:  LT, RT, RB
            { 0, 2, 3 },          // 2:  LT, LB, RB
            { 1, 2, 3 },          // 3:  RT, LB, RB
            { 0, 1 },             // 4:  LT, RT
            { 0, 2 },             // 5:  LT, LB
          };
    
          int verNum[6] = { 3, 3, 3, 3, 2, 2 };
          int startIdx = pu.cs->sps->getSpsNext().getUseAffineType() ? 0 : 4;
          for ( int idx = startIdx; idx < modelNum; idx++ )
          {
            int modelIdx = order[idx];
    
            getAffineControlPointCand( pu, mi, isAvailable, model[modelIdx], modelIdx, verNum[modelIdx], affMrgCtx );
    
            if ( affMrgCtx.numValidMergeCand != 0 && affMrgCtx.numValidMergeCand - 1 == mrgCandIdx )
            {
              return;
            }
    
            // early termination
            if ( affMrgCtx.numValidMergeCand == maxNumAffineMergeCand )
            {
              return;
            }
          }
        }
        ///> End: Constructed affine candidates
    #if JVET_L0369_SUBBLOCK_MERGE
      }
    #endif
    
      ///> zero padding
      int cnt = affMrgCtx.numValidMergeCand;
      while ( cnt < maxNumAffineMergeCand )
      {
        for ( int mvNum = 0; mvNum < 3; mvNum++ )
        {
          affMrgCtx.mvFieldNeighbours[(cnt << 1) + 0][mvNum].setMvField( Mv( 0, 0 ), 0 );
        }
        affMrgCtx.interDirNeighbours[cnt] = 1;
    
        if ( slice.isInterB() )
        {
          for ( int mvNum = 0; mvNum < 3; mvNum++ )
          {
            affMrgCtx.mvFieldNeighbours[(cnt << 1) + 1][mvNum].setMvField( Mv( 0, 0 ), 0 );
          }
          affMrgCtx.interDirNeighbours[cnt] = 3;
        }
        affMrgCtx.affineType[cnt] = AFFINEMODEL_4PARAM;
        cnt++;
    
        if ( cnt == maxNumAffineMergeCand )
        {
          return;
        }
      }
    }
    #else
    
    const PredictionUnit* getFirstAvailableAffineNeighbour( const PredictionUnit &pu )
    {
      const Position posLT = pu.Y().topLeft();
      const Position posRT = pu.Y().topRight();
      const Position posLB = pu.Y().bottomLeft();
    
      const PredictionUnit* puLeft = pu.cs->getPURestricted( posLB.offset( -1, 0 ), pu, pu.chType );
      if( puLeft && puLeft->cu->affine )
      {
        return puLeft;
      }
      const PredictionUnit* puAbove = pu.cs->getPURestricted( posRT.offset( 0, -1 ), pu, pu.chType );
      if( puAbove && puAbove->cu->affine )
      {
        return puAbove;
      }
      const PredictionUnit* puAboveRight = pu.cs->getPURestricted( posRT.offset( 1, -1 ), pu, pu.chType );
      if( puAboveRight && puAboveRight->cu->affine )
      {
        return puAboveRight;
      }
      const PredictionUnit *puLeftBottom = pu.cs->getPURestricted( posLB.offset( -1, 1 ), pu, pu.chType );
      if( puLeftBottom && puLeftBottom->cu->affine )
      {
        return puLeftBottom;
      }
      const PredictionUnit *puAboveLeft = pu.cs->getPURestricted( posLT.offset( -1, -1 ), pu, pu.chType );
      if( puAboveLeft && puAboveLeft->cu->affine )
      {
        return puAboveLeft;
      }
      return nullptr;
    }
    
    bool PU::isAffineMrgFlagCoded( const PredictionUnit &pu )
    {
      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 + blockWidth) >> 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
    
    #if JVET_L0694_AFFINE_LINEBUFFER_CLEANUP
      pu.mvAffi[eRefList][0] = affLT;
      pu.mvAffi[eRefList][1] = affRT;
      pu.mvAffi[eRefList][2] = affLB;
    #else
    
      // 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();
          }
    
    #if JVET_L0257_ATMVP_COLBLK_CLIP
    void clipColPos(int& posX, int& posY, const PredictionUnit& pu)
    {
      Position puPos = pu.lumaPos();
      int log2CtuSize = g_aucLog2[pu.cs->sps->getSpsNext().getCTUSize()];
      int ctuX = ((puPos.x >> log2CtuSize) << log2CtuSize);
      int ctuY = ((puPos.y >> log2CtuSize) << log2CtuSize);
    
      int horMax = std::min((int)pu.cs->sps->getPicWidthInLumaSamples() - 1, ctuX + (int)pu.cs->sps->getSpsNext().getCTUSize() + 3);
      int horMin = std::max((int)0, ctuX);
      int verMax = std::min((int)pu.cs->sps->getPicHeightInLumaSamples() - 1, ctuY + (int)pu.cs->sps->getSpsNext().getCTUSize() - 1);
      int verMin = std::max((int)0, ctuY);
    
      posX = std::min(horMax, std::max(horMin, posX));
      posY = std::min(verMax, std::max(verMin, posY));
    }
    #else
    
    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
    
    #if JVET_L0054_MMVD
      , int mmvdList
    #endif
    
    {
      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++)
      {
    
    #if JVET_L0198_ATMVP_SCAN_SIMP
        if ( count )
        {
          RefPicList currRefPicList = RefPicList(slice.getCheckLDC() ? (slice.getColFromL0Flag() ? currRefListId : 1 - currRefListId) : currRefListId);
            
          if ((mrgCtx.interDirNeighbours[0] & (1 << currRefPicList)) && slice.getRefPic(currRefPicList, mrgCtx.mvFieldNeighbours[0 * 2 + currRefPicList].refIdx) == pColPic)
          {
            cTMv = mrgCtx.mvFieldNeighbours[0 * 2 + currRefPicList].mv;
            terminate = true;
            fetchRefPicList = currRefPicList;
            break;
          }
        }
    #else
    
        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
    
    
      Mv cTempVector = cTMv;
      bool  tempLICFlag = false;
    
      // compute the location of the current PU
      Position puPos = pu.lumaPos();
      Size puSize = pu.lumaSize();
    
    #if JVET_L0198_L0468_L0104_ATMVP_8x8SUB_BLOCK
      int numPartLine = std::max(puSize.width >> ATMVP_SUB_BLOCK_SIZE, 1u);
      int numPartCol = std::max(puSize.height >> ATMVP_SUB_BLOCK_SIZE, 1u);
      int puHeight = numPartCol == 1 ? puSize.height : 1 << ATMVP_SUB_BLOCK_SIZE;
      int puWidth = numPartLine == 1 ? puSize.width : 1 << ATMVP_SUB_BLOCK_SIZE;
    #else 
    
      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;
    
    #if JVET_L0257_ATMVP_COLBLK_CLIP
      int tempX = cTempVector.getHor() >> mvPrec;
      int tempY = cTempVector.getVer() >> mvPrec;
    
      centerPos.x = puPos.x + (puSize.width >> 1) + tempX;
      centerPos.y = puPos.y + (puSize.height >> 1) + tempY;
    
      clipColPos(centerPos.x, centerPos.y, pu);
    #else
    
      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;
      }
    
    #if JVET_L0054_MMVD
      if (mmvdList != 1)
      {
    #endif
    
    #if JVET_L0257_ATMVP_COLBLK_CLIP
      int xOff = (puWidth >> 1) + tempX;
      int yOff = (puHeight >> 1) + tempY;
    #else
    
      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 };
    
    
    #if JVET_L0257_ATMVP_COLBLK_CLIP
          clipColPos(colPos.x, colPos.y, pu);
    #else
    
          colPos.x = Clip3(0, iPicWidth, colPos.x);
          colPos.y = Clip3(0, iPicHeight, colPos.y);
    
    
          colPos = Position{ PosType(colPos.x & mask), PosType(colPos.y & mask) };