Skip to content
Snippets Groups Projects
UnitTools.cpp 93.3 KiB
Newer Older
  • Learn to ignore specific revisions
  •             singleMv.setHighPrec();
              }
    #endif
              mrgCtx.mvFieldNeighbours[cnt * 2 + refListId].setMvField( singleMv, refIdxJ );
            }
          }
    
          mrgCtx.interDirNeighbours[cnt] = interDir;
          if( interDir > 0 )
          {
            cnt++;
          }
        }
    
        // early termination
        if( cnt == maxNumMergeCand )
        {
          return;
        }
      }
    #endif
    
    
      uint32_t uiCutoff    = std::min( uiArrayAddr, 4u );
    
      if (slice.isInterB())
      {
        static const uint32_t NUM_PRIORITY_LIST = 12;
        static const uint32_t uiPriorityList0[NUM_PRIORITY_LIST] = { 0 , 1, 0, 2, 1, 2, 0, 3, 1, 3, 2, 3 };
        static const uint32_t uiPriorityList1[NUM_PRIORITY_LIST] = { 1 , 0, 2, 0, 2, 1, 3, 0, 3, 1, 3, 2 };
    
        for (int idx = 0; idx < uiCutoff * (uiCutoff - 1) && uiArrayAddr != maxNumMergeCand; idx++)
        {
          CHECK( idx >= NUM_PRIORITY_LIST, "Invalid priority list number" );
          int i = uiPriorityList0[idx];
          int j = uiPriorityList1[idx];
          if (isCandInter[i] && isCandInter[j] && (mrgCtx.interDirNeighbours[i] & 0x1) && (mrgCtx.interDirNeighbours[j] & 0x2))
          {
            isCandInter[uiArrayAddr] = true;
            mrgCtx.interDirNeighbours[uiArrayAddr] = 3;
    
    #if JVET_L0646_GBI
            mrgCtx.GBiIdx[uiArrayAddr] = ((mrgCtx.interDirNeighbours[uiArrayAddr] == 3)) ? CU::deriveGbiIdx(mrgCtx.GBiIdx[i], mrgCtx.GBiIdx[j]) : GBI_DEFAULT;
    #endif
    
    
            // get Mv from cand[i] and cand[j]
            mrgCtx.mvFieldNeighbours[ uiArrayAddr << 1     ].setMvField(mrgCtx.mvFieldNeighbours[ i << 1     ].mv, mrgCtx.mvFieldNeighbours[ i << 1     ].refIdx);
            mrgCtx.mvFieldNeighbours[(uiArrayAddr << 1) + 1].setMvField(mrgCtx.mvFieldNeighbours[(j << 1) + 1].mv, mrgCtx.mvFieldNeighbours[(j << 1) + 1].refIdx);
    
            int iRefPOCL0 = slice.getRefPOC(REF_PIC_LIST_0, mrgCtx.mvFieldNeighbours[(uiArrayAddr << 1)    ].refIdx);
            int iRefPOCL1 = slice.getRefPOC(REF_PIC_LIST_1, mrgCtx.mvFieldNeighbours[(uiArrayAddr << 1) + 1].refIdx);
    
            if( iRefPOCL0 == iRefPOCL1 && mrgCtx.mvFieldNeighbours[( uiArrayAddr << 1 )].mv == mrgCtx.mvFieldNeighbours[( uiArrayAddr << 1 ) + 1].mv )
            {
              isCandInter[uiArrayAddr] = false;
            }
            else
            {
              uiArrayAddr++;
            }
          }
        }
      }
    
      // early termination
      if (uiArrayAddr == maxNumMergeCand)
      {
        return;
      }
    
    
      int iNumRefIdx = slice.isInterB() ? std::min(slice.getNumRefIdx(REF_PIC_LIST_0), slice.getNumRefIdx(REF_PIC_LIST_1)) : slice.getNumRefIdx(REF_PIC_LIST_0);
    
      int r = 0;
      int refcnt = 0;
      while (uiArrayAddr < maxNumMergeCand)
      {
    
        mrgCtx.interDirNeighbours [uiArrayAddr     ] = 1;
    
    #if JVET_L0646_GBI
        mrgCtx.GBiIdx             [uiArrayAddr     ] = GBI_DEFAULT;
    #endif
    
        mrgCtx.mvFieldNeighbours  [uiArrayAddr << 1].setMvField(Mv(0, 0), r);
    
        if (slice.isInterB())
        {
          mrgCtx.interDirNeighbours [ uiArrayAddr          ] = 3;
          mrgCtx.mvFieldNeighbours  [(uiArrayAddr << 1) + 1].setMvField(Mv(0, 0), r);
        }
    
        uiArrayAddr++;
    
        if (refcnt == iNumRefIdx - 1)
        {
          r = 0;
        }
        else
        {
          ++r;
          ++refcnt;
        }
      }
      mrgCtx.numValidMergeCand = uiArrayAddr;
    }
    
    
    static int xGetDistScaleFactor(const int &iCurrPOC, const int &iCurrRefPOC, const int &iColPOC, const int &iColRefPOC)
    {
      int iDiffPocD = iColPOC - iColRefPOC;
      int iDiffPocB = iCurrPOC - iCurrRefPOC;
    
      if (iDiffPocD == iDiffPocB)
      {
        return 4096;
      }
      else
      {
        int iTDB = Clip3(-128, 127, iDiffPocB);
        int iTDD = Clip3(-128, 127, iDiffPocD);
        int iX = (0x4000 + abs(iTDD / 2)) / iTDD;
        int iScale = Clip3(-4096, 4095, (iTDB * iX + 32) >> 6);
        return iScale;
      }
    }
    
    #if JVET_L0054_MMVD
    int PU::getDistScaleFactor(const int &currPOC, const int &currRefPOC, const int &colPOC, const int &colRefPOC)
    {
      return xGetDistScaleFactor(currPOC, currRefPOC, colPOC, colRefPOC);
    }
    
    void PU::getInterMMVDMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const int& mrgCandIdx)
    {
      int refIdxList0, refIdxList1;
      int k;
      int currBaseNum = 0;
      const uint16_t maxNumMergeCand = mrgCtx.numValidMergeCand;
    
    #if !REMOVE_MV_ADAPT_PREC
      if (pu.cu->slice->getSPS()->getSpsNext().getUseHighPrecMv())
      {
        for (k = 0; k < maxNumMergeCand; k++)
        {
          if (mrgCtx.mrgTypeNeighbours[k] == MRG_TYPE_DEFAULT_N)
          {
            if ((mrgCtx.mvFieldNeighbours[(k << 1)].mv.highPrec == false) && (mrgCtx.mvFieldNeighbours[(k << 1)].refIdx >= 0))
            {
              mrgCtx.mvFieldNeighbours[(k << 1)].mv.setHighPrec();
            }
            if ((mrgCtx.mvFieldNeighbours[(k << 1) + 1].mv.highPrec == false) && (mrgCtx.mvFieldNeighbours[(k << 1) + 1].refIdx >= 0))
            {
              mrgCtx.mvFieldNeighbours[(k << 1) + 1].mv.setHighPrec();
            }
          }
        }
      }
    #endif
      for (k = 0; k < maxNumMergeCand; k++)
      {
        if (mrgCtx.mrgTypeNeighbours[k] == MRG_TYPE_DEFAULT_N)
        {
          refIdxList0 = mrgCtx.mvFieldNeighbours[(k << 1)].refIdx;
          refIdxList1 = mrgCtx.mvFieldNeighbours[(k << 1) + 1].refIdx;
    
          if ((refIdxList0 >= 0) && (refIdxList1 >= 0))
          {
            mrgCtx.mmvdBaseMv[currBaseNum][0] = mrgCtx.mvFieldNeighbours[(k << 1)];
            mrgCtx.mmvdBaseMv[currBaseNum][1] = mrgCtx.mvFieldNeighbours[(k << 1) + 1];
          }
          else if (refIdxList0 >= 0)
          {
            mrgCtx.mmvdBaseMv[currBaseNum][0] = mrgCtx.mvFieldNeighbours[(k << 1)];
            mrgCtx.mmvdBaseMv[currBaseNum][1] = MvField(Mv(0, 0), -1);
          }
          else if (refIdxList1 >= 0)
          {
            mrgCtx.mmvdBaseMv[currBaseNum][0] = MvField(Mv(0, 0), -1);
            mrgCtx.mmvdBaseMv[currBaseNum][1] = mrgCtx.mvFieldNeighbours[(k << 1) + 1];
          }
    
          currBaseNum++;
    
          if (currBaseNum == MMVD_BASE_MV_NUM)
            break;
        }
      }
    
      if (currBaseNum < MMVD_BASE_MV_NUM)
      {
        for (k = currBaseNum; k < MMVD_BASE_MV_NUM; k++)
        {
          mrgCtx.mmvdBaseMv[k][0] = MvField(Mv(0, 0), 0);
          mrgCtx.mmvdBaseMv[k][0] = MvField(Mv(0, 0), 0);
        }
      }
    }
    #endif
    
    bool PU::getColocatedMVP(const PredictionUnit &pu, const RefPicList &eRefPicList, const Position &_pos, Mv& rcMv, const int &refIdx )
    {
      // don't perform MV compression when generally disabled or subPuMvp is used
      const unsigned scale = ( pu.cs->pcv->noMotComp ? 1 : 4 * std::max<int>(1, 4 * AMVP_DECIMATION_FACTOR / 4) );
      const unsigned mask  = ~( scale - 1 );
    
      const Position pos = Position{ PosType( _pos.x & mask ), PosType( _pos.y & mask ) };
    
      const Slice &slice = *pu.cs->slice;
    
      // use coldir.
      const Picture* const pColPic = slice.getRefPic(RefPicList(slice.isInterB() ? 1 - slice.getColFromL0Flag() : 0), slice.getColRefIdx());
    
      if( !pColPic )
      {
        return false;
      }
    
      RefPicList eColRefPicList = slice.getCheckLDC() ? eRefPicList : RefPicList(slice.getColFromL0Flag());
    
      const MotionInfo& mi = pColPic->cs->getMotionInfo( pos );
    
      if( !mi.isInter )
      {
        return false;
      }
      int iColRefIdx = mi.refIdx[eColRefPicList];
    
      if (iColRefIdx < 0)
      {
        eColRefPicList = RefPicList(1 - eColRefPicList);
        iColRefIdx = mi.refIdx[eColRefPicList];
    
        if (iColRefIdx < 0)
        {
          return false;
        }
      }
    
      const Slice *pColSlice = nullptr;
    
      for( const auto s : pColPic->slices )
      {
        if( s->getIndependentSliceIdx() == mi.sliceIdx )
        {
          pColSlice = s;
          break;
        }
      }
    
      CHECK( pColSlice == nullptr, "Slice segment not found" );
    
      const Slice &colSlice = *pColSlice;
    
      const bool bIsCurrRefLongTerm = slice.getRefPic(eRefPicList, refIdx)->longTerm;
      const bool bIsColRefLongTerm  = colSlice.getIsUsedAsLongTerm(eColRefPicList, iColRefIdx);
    
      if (bIsCurrRefLongTerm != bIsColRefLongTerm)
      {
        return false;
      }
    
    
      // Scale the vector.
      Mv cColMv = mi.mv[eColRefPicList];
    
      if (bIsCurrRefLongTerm /*|| bIsColRefLongTerm*/)
      {
        rcMv = cColMv;
      }
      else
      {
        const int currPOC    = slice.getPOC();
        const int colPOC     = colSlice.getPOC();
        const int colRefPOC  = colSlice.getRefPOC(eColRefPicList, iColRefIdx);
        const int currRefPOC = slice.getRefPic(eRefPicList, refIdx)->getPOC();
        const int distscale  = xGetDistScaleFactor(currPOC, currRefPOC, colPOC, colRefPOC);
    
        if (distscale == 4096)
        {
          rcMv = cColMv;
        }
        else
        {
    
    #if !REMOVE_MV_ADAPT_PREC
    
          if( pu.cs->sps->getSpsNext().getUseHighPrecMv() )
          {
            // allow extended precision for temporal scaling
            cColMv.setHighPrec();
          }
    #endif
          rcMv = cColMv.scaleMv(distscale);
        }
      }
    
      return true;
    }
    
    bool PU::isDiffMER(const PredictionUnit &pu1, const PredictionUnit &pu2)
    {
      const unsigned xN = pu1.lumaPos().x;
      const unsigned yN = pu1.lumaPos().y;
      const unsigned xP = pu2.lumaPos().x;
      const unsigned yP = pu2.lumaPos().y;
    
      unsigned plevel = pu1.cs->pps->getLog2ParallelMergeLevelMinus2() + 2;
    
      if ((xN >> plevel) != (xP >> plevel))
      {
        return true;
      }
    
      if ((yN >> plevel) != (yP >> plevel))
      {
        return true;
      }
    
      return false;
    }
    
    /** Constructs a list of candidates for AMVP (See specification, section "Derivation process for motion vector predictor candidates")
    * \param uiPartIdx
    * \param uiPartAddr
    * \param eRefPicList
    * \param iRefIdx
    * \param pInfo
    */
    void PU::fillMvpCand(PredictionUnit &pu, const RefPicList &eRefPicList, const int &refIdx, AMVPInfo &amvpInfo)
    {
      CodingStructure &cs = *pu.cs;
    
      AMVPInfo *pInfo = &amvpInfo;
    
      pInfo->numCand = 0;
    
      if (refIdx < 0)
      {
        return;
      }
    
      //-- Get Spatial MV
      Position posLT = pu.Y().topLeft();
      Position posRT = pu.Y().topRight();
      Position posLB = pu.Y().bottomLeft();
    
      bool isScaledFlagLX = false; /// variable name from specification; true when the PUs below left or left are available (availableA0 || availableA1).
    
      {
        const PredictionUnit* tmpPU = cs.getPURestricted( posLB.offset( -1, 1 ), pu, pu.chType ); // getPUBelowLeft(idx, partIdxLB);
        isScaledFlagLX = tmpPU != NULL && CU::isInter( *tmpPU->cu );
    
        if( !isScaledFlagLX )
        {
          tmpPU = cs.getPURestricted( posLB.offset( -1, 0 ), pu, pu.chType );
          isScaledFlagLX = tmpPU != NULL && CU::isInter( *tmpPU->cu );
        }
      }
    
      // Left predictor search
      if( isScaledFlagLX )
      {
        bool bAdded = addMVPCandUnscaled( pu, eRefPicList, refIdx, posLB, MD_BELOW_LEFT, *pInfo );
    
        if( !bAdded )
        {
          bAdded = addMVPCandUnscaled( pu, eRefPicList, refIdx, posLB, MD_LEFT, *pInfo );
    
          if( !bAdded )
          {
            bAdded = addMVPCandWithScaling( pu, eRefPicList, refIdx, posLB, MD_BELOW_LEFT, *pInfo );
    
            if( !bAdded )
            {
              addMVPCandWithScaling( pu, eRefPicList, refIdx, posLB, MD_LEFT, *pInfo );
            }
          }
        }
      }
    
      // Above predictor search
      {
        bool bAdded = addMVPCandUnscaled( pu, eRefPicList, refIdx, posRT, MD_ABOVE_RIGHT, *pInfo );
    
        if( !bAdded )
        {
          bAdded = addMVPCandUnscaled( pu, eRefPicList, refIdx, posRT, MD_ABOVE, *pInfo );
    
          if( !bAdded )
          {
            addMVPCandUnscaled( pu, eRefPicList, refIdx, posLT, MD_ABOVE_LEFT, *pInfo );
          }
        }
      }
    
      if( !isScaledFlagLX )
      {
        bool bAdded = addMVPCandWithScaling( pu, eRefPicList, refIdx, posRT, MD_ABOVE_RIGHT, *pInfo );
    
        if( !bAdded )
        {
          bAdded = addMVPCandWithScaling( pu, eRefPicList, refIdx, posRT, MD_ABOVE, *pInfo );
    
          if( !bAdded )
          {
            addMVPCandWithScaling( pu, eRefPicList, refIdx, posLT, MD_ABOVE_LEFT, *pInfo );
          }
        }
      }
    
      if( pu.cu->imv != 0)
      {
        unsigned imvShift = pu.cu->imv << 1;
    
    #if REMOVE_MV_ADAPT_PREC
        imvShift += VCEG_AZ07_MV_ADD_PRECISION_BIT_FOR_STORE;
    #endif
    
        for( int i = 0; i < pInfo->numCand; i++ )
        {
          roundMV( pInfo->mvCand[i], imvShift );
        }
      }
    
      if( pInfo->numCand == 2 )
      {
        if( pInfo->mvCand[0] == pInfo->mvCand[1] )
        {
          pInfo->numCand = 1;
        }
      }
    
      if( cs.slice->getEnableTMVPFlag() )
      {
        // Get Temporal Motion Predictor
        const int refIdx_Col = refIdx;
    
        Position posRB = pu.Y().bottomRight().offset(-3, -3);
    
        const PreCalcValues& pcv = *cs.pcv;
    
        Position posC0;
        bool C0Avail = false;
        Position posC1 = pu.Y().center();
    
        Mv cColMv;
    
        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
          {
            // in the reference the CTU address is not set - thus probably resulting in no using this C0 possibility
            posC0 = posRB.offset(4, 4);
          }
          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
          {
            // same as for last column but not last row
            posC0 = posRB.offset(4, 4);
          }
        }
    
        if ((C0Avail && getColocatedMVP(pu, eRefPicList, posC0, cColMv, refIdx_Col)) || getColocatedMVP(pu, eRefPicList, posC1, cColMv, refIdx_Col))
        {
          pInfo->mvCand[pInfo->numCand++] = cColMv;
        }
      }
      if (pInfo->numCand > AMVP_MAX_NUM_CANDS)
      {
        pInfo->numCand = AMVP_MAX_NUM_CANDS;
      }
    
      while (pInfo->numCand < AMVP_MAX_NUM_CANDS)
      {
    
    #if !REMOVE_MV_ADAPT_PREC
    
        const bool prec = pInfo->mvCand[pInfo->numCand].highPrec;
        pInfo->mvCand[pInfo->numCand] = Mv( 0, 0, prec );
    #else
        pInfo->mvCand[pInfo->numCand] = Mv( 0, 0 );
    #endif
        pInfo->numCand++;
      }
    
    #if !REMOVE_MV_ADAPT_PREC
      if (pu.cs->sps->getSpsNext().getUseHighPrecMv())
    
    #endif
        for (Mv &mv : pInfo->mvCand)
    
    #if REMOVE_MV_ADAPT_PREC
          const int nShift = VCEG_AZ07_MV_ADD_PRECISION_BIT_FOR_STORE;
          const int nOffset = 1 << (nShift - 1);
          mv.hor = mv.hor >= 0 ? (mv.hor + nOffset) >> nShift : -((-mv.hor + nOffset) >> nShift);
          mv.ver = mv.ver >= 0 ? (mv.ver + nOffset) >> nShift : -((-mv.ver + nOffset) >> nShift);
    #else
          if (mv.highPrec) mv.setLowPrec();
    #endif
    
    #if !REMOVE_MV_ADAPT_PREC
    
      if (pu.cu->imv != 0)
      {
        unsigned imvShift = pu.cu->imv << 1;
        for (int i = 0; i < pInfo->numCand; i++)
        {
          roundMV(pInfo->mvCand[i], imvShift);
        }
      }
    
    #if !REMOVE_MV_ADAPT_PREC
    
      if (pu.cs->sps->getSpsNext().getUseHighPrecMv())
      {
        for (Mv &mv : pInfo->mvCand)
        {
          if (mv.highPrec) mv.setLowPrec();
        }
      }
    #endif
    }
    
    
    const int getAvailableAffineNeighbours( const PredictionUnit &pu, const PredictionUnit* npu[] )
    {
      const Position posLT = pu.Y().topLeft();
      const Position posRT = pu.Y().topRight();
      const Position posLB = pu.Y().bottomLeft();
    
      int num = 0;
      const PredictionUnit* puLeft = pu.cs->getPURestricted( posLB.offset( -1, 0 ), pu, pu.chType );
      if ( puLeft && puLeft->cu->affine )
      {
        npu[num++] = puLeft;
      }
    
      const PredictionUnit* puAbove = pu.cs->getPURestricted( posRT.offset( 0, -1 ), pu, pu.chType );
      if ( puAbove && puAbove->cu->affine )
      {
        npu[num++] = puAbove;
      }
    
      const PredictionUnit* puAboveRight = pu.cs->getPURestricted( posRT.offset( 1, -1 ), pu, pu.chType );
      if ( puAboveRight && puAboveRight->cu->affine )
      {
        npu[num++] = puAboveRight;
      }
    
      const PredictionUnit *puLeftBottom = pu.cs->getPURestricted( posLB.offset( -1, 1 ), pu, pu.chType );
      if ( puLeftBottom && puLeftBottom->cu->affine )
      {
        npu[num++] = puLeftBottom;
      }
    
      const PredictionUnit *puAboveLeft = pu.cs->getPURestricted( posLT.offset( -1, -1 ), pu, pu.chType );
      if ( puAboveLeft && puAboveLeft->cu->affine )
      {
        npu[num++] = puAboveLeft;
      }
    
      return num;
    }
    
    void PU::xInheritedAffineMv( const PredictionUnit &pu, const PredictionUnit* puNeighbour, RefPicList eRefPicList, Mv rcMv[3] )
    {
      int posNeiX = puNeighbour->Y().pos().x;
      int posNeiY = puNeighbour->Y().pos().y;
      int posCurX = pu.Y().pos().x;
      int posCurY = pu.Y().pos().y;
    
      int neiW = puNeighbour->Y().width;
      int curW = pu.Y().width;
      int neiH = puNeighbour->Y().height;
      int curH = pu.Y().height;
      
      Mv mvLT, mvRT, mvLB;
      const Position posLT = puNeighbour->Y().topLeft();
      const Position posRT = puNeighbour->Y().topRight();
      const Position posLB = puNeighbour->Y().bottomLeft();
      mvLT = puNeighbour->getMotionInfo( posLT ).mv[eRefPicList];
      mvRT = puNeighbour->getMotionInfo( posRT ).mv[eRefPicList];
      mvLB = puNeighbour->getMotionInfo( posLB ).mv[eRefPicList];
    
      int shift = MAX_CU_DEPTH;
      int iDMvHorX, iDMvHorY, iDMvVerX, iDMvVerY;
    
      iDMvHorX = (mvRT - mvLT).getHor() << (shift - g_aucLog2[neiW]);
      iDMvHorY = (mvRT - mvLT).getVer() << (shift - g_aucLog2[neiW]);
      if ( puNeighbour->cu->affineType == AFFINEMODEL_6PARAM )
      {
        iDMvVerX = (mvLB - mvLT).getHor() << (shift - g_aucLog2[neiH]);
        iDMvVerY = (mvLB - mvLT).getVer() << (shift - g_aucLog2[neiH]);
      }
      else
      {
        iDMvVerX = -iDMvHorY;
        iDMvVerY = iDMvHorX;
      }
    
      int iMvScaleHor = mvLT.getHor() << shift;
      int iMvScaleVer = mvLT.getVer() << shift;
      int horTmp, verTmp;
    
      // v0
      horTmp = iMvScaleHor + iDMvHorX * (posCurX - posNeiX) + iDMvVerX * (posCurY - posNeiY);
      verTmp = iMvScaleVer + iDMvHorY * (posCurX - posNeiX) + iDMvVerY * (posCurY - posNeiY);
      roundAffineMv( horTmp, verTmp, shift );
    
    #if REMOVE_MV_ADAPT_PREC
      rcMv[0].hor = horTmp;
      rcMv[0].ver = verTmp;
    #else
      rcMv[0] = Mv(horTmp, verTmp, true);
    #endif
    
    
      // v1
      horTmp = iMvScaleHor + iDMvHorX * (posCurX + curW - posNeiX) + iDMvVerX * (posCurY - posNeiY);
      verTmp = iMvScaleVer + iDMvHorY * (posCurX + curW - posNeiX) + iDMvVerY * (posCurY - posNeiY);
      roundAffineMv( horTmp, verTmp, shift );
    
    #if REMOVE_MV_ADAPT_PREC
      rcMv[1].hor = horTmp;
      rcMv[1].ver = verTmp;
    #else
      rcMv[1] = Mv(horTmp, verTmp, true);
    #endif
    
    
      // v2
      if ( pu.cu->affineType == AFFINEMODEL_6PARAM )
      {
        horTmp = iMvScaleHor + iDMvHorX * (posCurX - posNeiX) + iDMvVerX * (posCurY + curH - posNeiY);
        verTmp = iMvScaleVer + iDMvHorY * (posCurX - posNeiX) + iDMvVerY * (posCurY + curH - posNeiY);
        roundAffineMv( horTmp, verTmp, shift );
    
    #if REMOVE_MV_ADAPT_PREC
        rcMv[2].hor = horTmp;
        rcMv[2].ver = verTmp;
    #else
        rcMv[2] = Mv(horTmp, verTmp, true);
    #endif
    
      }
    }
    
    
    void PU::fillAffineMvpCand(PredictionUnit &pu, const RefPicList &eRefPicList, const int &refIdx, AffineAMVPInfo &affiAMVPInfo)
    {
    
    #if REMOVE_MV_ADAPT_PREC
      const int nShift = VCEG_AZ07_MV_ADD_PRECISION_BIT_FOR_STORE;
      const int nOffset = 1 << (nShift - 1);
    #endif
    
      affiAMVPInfo.numCand = 0;
    
      if (refIdx < 0)
      {
        return;
      }
    
      const int curWidth = pu.Y().width;
      const int curHeight = pu.Y().height;
    
      // insert inherited affine candidates
      Mv outputAffineMv[3];
      const int maxNei = 5;
      const PredictionUnit* npu[maxNei];
      int numAffNeigh = getAvailableAffineNeighbours( pu, npu );
      int targetRefPOC = pu.cu->slice->getRefPOC( eRefPicList, refIdx );
    
      for ( int refPicList = 0; refPicList < 2 && affiAMVPInfo.numCand < AMVP_MAX_NUM_CANDS; refPicList++ )
      {
        RefPicList eTestRefPicList = (refPicList == 0) ? eRefPicList : RefPicList( 1 - eRefPicList );
    
        for ( int neighIdx = 0; neighIdx < numAffNeigh && affiAMVPInfo.numCand < AMVP_MAX_NUM_CANDS; neighIdx++ )
        {
          const PredictionUnit* puNeighbour = npu[neighIdx];
    
          if ( ((puNeighbour->interDir & (eTestRefPicList + 1)) == 0) || pu.cu->slice->getRefPOC( eTestRefPicList, puNeighbour->refIdx[eTestRefPicList] ) != targetRefPOC )
          {
            continue;
          }
    
          xInheritedAffineMv( pu, puNeighbour, eTestRefPicList, outputAffineMv );
    
          outputAffineMv[0].roundMV2SignalPrecision();
          outputAffineMv[1].roundMV2SignalPrecision();
          if ( pu.cu->affineType == AFFINEMODEL_6PARAM )
          {
            outputAffineMv[2].roundMV2SignalPrecision();
          }
    
          if ( affiAMVPInfo.numCand == 0
            || (pu.cu->affineType == AFFINEMODEL_4PARAM && (outputAffineMv[0] != affiAMVPInfo.mvCandLT[0] || outputAffineMv[1] != affiAMVPInfo.mvCandRT[0]))
            || (pu.cu->affineType == AFFINEMODEL_6PARAM && (outputAffineMv[0] != affiAMVPInfo.mvCandLT[0] || outputAffineMv[1] != affiAMVPInfo.mvCandRT[0] || outputAffineMv[2] != affiAMVPInfo.mvCandLB[0]))
            )
          {
            affiAMVPInfo.mvCandLT[affiAMVPInfo.numCand] = outputAffineMv[0];
            affiAMVPInfo.mvCandRT[affiAMVPInfo.numCand] = outputAffineMv[1];
            affiAMVPInfo.mvCandLB[affiAMVPInfo.numCand] = outputAffineMv[2];
            affiAMVPInfo.numCand++;
          }
        }
      }
    
      if ( affiAMVPInfo.numCand >= AMVP_MAX_NUM_CANDS )
      {
    
    #if REMOVE_MV_ADAPT_PREC
        for (int i = 0; i < affiAMVPInfo.numCand; i++)
        {
          affiAMVPInfo.mvCandLT[i].hor = affiAMVPInfo.mvCandLT[i].hor >= 0 ? (affiAMVPInfo.mvCandLT[i].hor + nOffset) >> nShift : -((-affiAMVPInfo.mvCandLT[i].hor + nOffset) >> nShift);
          affiAMVPInfo.mvCandLT[i].ver = affiAMVPInfo.mvCandLT[i].ver >= 0 ? (affiAMVPInfo.mvCandLT[i].ver + nOffset) >> nShift : -((-affiAMVPInfo.mvCandLT[i].ver + nOffset) >> nShift);
          affiAMVPInfo.mvCandRT[i].hor = affiAMVPInfo.mvCandRT[i].hor >= 0 ? (affiAMVPInfo.mvCandRT[i].hor + nOffset) >> nShift : -((-affiAMVPInfo.mvCandRT[i].hor + nOffset) >> nShift);
          affiAMVPInfo.mvCandRT[i].ver = affiAMVPInfo.mvCandRT[i].ver >= 0 ? (affiAMVPInfo.mvCandRT[i].ver + nOffset) >> nShift : -((-affiAMVPInfo.mvCandRT[i].ver + nOffset) >> nShift);
          affiAMVPInfo.mvCandLB[i].hor = affiAMVPInfo.mvCandLB[i].hor >= 0 ? (affiAMVPInfo.mvCandLB[i].hor + nOffset) >> nShift : -((-affiAMVPInfo.mvCandLB[i].hor + nOffset) >> nShift);
          affiAMVPInfo.mvCandLB[i].ver = affiAMVPInfo.mvCandLB[i].ver >= 0 ? (affiAMVPInfo.mvCandLB[i].ver + nOffset) >> nShift : -((-affiAMVPInfo.mvCandLB[i].ver + nOffset) >> nShift);
        }
    #endif
    
        return;
      }
    
      // insert constructed affine candidates
      int cornerMVPattern = 0;
      Position posLT = pu.Y().topLeft();
      Position posRT = pu.Y().topRight();
      Position posLB = pu.Y().bottomLeft();
    
      //-------------------  V0 (START) -------------------//
      AMVPInfo amvpInfo0;
      amvpInfo0.numCand = 0;
    
      // A->C: Above Left, Above, Left
      addMVPCandUnscaled( pu, eRefPicList, refIdx, posLT, MD_ABOVE_LEFT, amvpInfo0, true );
      if ( amvpInfo0.numCand < 1 )
      {
        addMVPCandUnscaled( pu, eRefPicList, refIdx, posLT, MD_ABOVE, amvpInfo0, true );
      }
      if ( amvpInfo0.numCand < 1 )
      {
        addMVPCandUnscaled( pu, eRefPicList, refIdx, posLT, MD_LEFT, amvpInfo0, true );
      }
      cornerMVPattern = cornerMVPattern | amvpInfo0.numCand;
    
      //-------------------  V1 (START) -------------------//
      AMVPInfo amvpInfo1;
      amvpInfo1.numCand = 0;
    
      // D->E: Above, Above Right
      addMVPCandUnscaled( pu, eRefPicList, refIdx, posRT, MD_ABOVE, amvpInfo1, true );
      if ( amvpInfo1.numCand < 1 )
      {
        addMVPCandUnscaled( pu, eRefPicList, refIdx, posRT, MD_ABOVE_RIGHT, amvpInfo1, true );
      }
      cornerMVPattern = cornerMVPattern | (amvpInfo1.numCand << 1);
    
      //-------------------  V2 (START) -------------------//
      AMVPInfo amvpInfo2;
      amvpInfo2.numCand = 0;
    
      // F->G: Left, Below Left
      addMVPCandUnscaled( pu, eRefPicList, refIdx, posLB, MD_LEFT, amvpInfo2, true );
      if ( amvpInfo2.numCand < 1 )
      {
        addMVPCandUnscaled( pu, eRefPicList, refIdx, posLB, MD_BELOW_LEFT, amvpInfo2, true );
      }
      cornerMVPattern = cornerMVPattern | (amvpInfo2.numCand << 2);
    
      outputAffineMv[0] = amvpInfo0.mvCand[0];
      outputAffineMv[1] = amvpInfo1.mvCand[0];
      outputAffineMv[2] = amvpInfo2.mvCand[0];
    
    
    #if !REMOVE_MV_ADAPT_PREC
    
      outputAffineMv[0].setHighPrec();
      outputAffineMv[1].setHighPrec();
      outputAffineMv[2].setHighPrec();
    
    
      outputAffineMv[0].roundMV2SignalPrecision();
      outputAffineMv[1].roundMV2SignalPrecision();
      outputAffineMv[2].roundMV2SignalPrecision();
    
      if ( cornerMVPattern == 7 || cornerMVPattern == 3 || cornerMVPattern == 5 )
      {
        if ( cornerMVPattern == 3 && pu.cu->affineType == AFFINEMODEL_6PARAM ) // V0 V1 are available, derived V2 for 6-para
        {
          int shift = MAX_CU_DEPTH;
          int vx2 = (outputAffineMv[0].getHor() << shift) - ((outputAffineMv[1].getVer() - outputAffineMv[0].getVer()) << (shift + g_aucLog2[curHeight] - g_aucLog2[curWidth]));
          int vy2 = (outputAffineMv[0].getVer() << shift) + ((outputAffineMv[1].getHor() - outputAffineMv[0].getHor()) << (shift + g_aucLog2[curHeight] - g_aucLog2[curWidth]));
          roundAffineMv( vx2, vy2, shift );
          outputAffineMv[2].set( vx2, vy2 );
          outputAffineMv[2].roundMV2SignalPrecision();
        }
    
        if ( cornerMVPattern == 5 ) // V0 V2 are available, derived V1
        {
          int shift = MAX_CU_DEPTH;
          int vx1 = (outputAffineMv[0].getHor() << shift) + ((outputAffineMv[2].getVer() - outputAffineMv[0].getVer()) << (shift + g_aucLog2[curWidth] - g_aucLog2[curHeight]));
          int vy1 = (outputAffineMv[0].getVer() << shift) - ((outputAffineMv[2].getHor() - outputAffineMv[0].getHor()) << (shift + g_aucLog2[curWidth] - g_aucLog2[curHeight]));
          roundAffineMv( vx1, vy1, shift );
          outputAffineMv[1].set( vx1, vy1 );
          outputAffineMv[1].roundMV2SignalPrecision();
        }
    
        if ( affiAMVPInfo.numCand == 0
          || (pu.cu->affineType == AFFINEMODEL_4PARAM && (outputAffineMv[0] != affiAMVPInfo.mvCandLT[0] || outputAffineMv[1] != affiAMVPInfo.mvCandRT[0]))
          || (pu.cu->affineType == AFFINEMODEL_6PARAM && (outputAffineMv[0] != affiAMVPInfo.mvCandLT[0] || outputAffineMv[1] != affiAMVPInfo.mvCandRT[0] || outputAffineMv[2] != affiAMVPInfo.mvCandLB[0]))
          )
        {
          affiAMVPInfo.mvCandLT[affiAMVPInfo.numCand] = outputAffineMv[0];
          affiAMVPInfo.mvCandRT[affiAMVPInfo.numCand] = outputAffineMv[1];
          affiAMVPInfo.mvCandLB[affiAMVPInfo.numCand] = outputAffineMv[2];
          affiAMVPInfo.numCand++;
        }
      }
    
    #if REMOVE_MV_ADAPT_PREC
      for (int i = 0; i < affiAMVPInfo.numCand; i++)
      {
        affiAMVPInfo.mvCandLT[i].hor = affiAMVPInfo.mvCandLT[i].hor >= 0 ? (affiAMVPInfo.mvCandLT[i].hor + nOffset) >> nShift : -((-affiAMVPInfo.mvCandLT[i].hor + nOffset) >> nShift);
        affiAMVPInfo.mvCandLT[i].ver = affiAMVPInfo.mvCandLT[i].ver >= 0 ? (affiAMVPInfo.mvCandLT[i].ver + nOffset) >> nShift : -((-affiAMVPInfo.mvCandLT[i].ver + nOffset) >> nShift);
        affiAMVPInfo.mvCandRT[i].hor = affiAMVPInfo.mvCandRT[i].hor >= 0 ? (affiAMVPInfo.mvCandRT[i].hor + nOffset) >> nShift : -((-affiAMVPInfo.mvCandRT[i].hor + nOffset) >> nShift);
        affiAMVPInfo.mvCandRT[i].ver = affiAMVPInfo.mvCandRT[i].ver >= 0 ? (affiAMVPInfo.mvCandRT[i].ver + nOffset) >> nShift : -((-affiAMVPInfo.mvCandRT[i].ver + nOffset) >> nShift);
        affiAMVPInfo.mvCandLB[i].hor = affiAMVPInfo.mvCandLB[i].hor >= 0 ? (affiAMVPInfo.mvCandLB[i].hor + nOffset) >> nShift : -((-affiAMVPInfo.mvCandLB[i].hor + nOffset) >> nShift);
        affiAMVPInfo.mvCandLB[i].ver = affiAMVPInfo.mvCandLB[i].ver >= 0 ? (affiAMVPInfo.mvCandLB[i].ver + nOffset) >> nShift : -((-affiAMVPInfo.mvCandLB[i].ver + nOffset) >> nShift);
      }
    #endif
    
      if ( affiAMVPInfo.numCand < 2 )
      {
        AMVPInfo amvpInfo;
        PU::fillMvpCand( pu, eRefPicList, refIdx, amvpInfo );
    
        int iAdd = amvpInfo.numCand - affiAMVPInfo.numCand;
        for ( int i = 0; i < iAdd; i++ )
        {
    
    #if !REMOVE_MV_ADAPT_PREC
    
          amvpInfo.mvCand[i].setHighPrec();
    #endif
          affiAMVPInfo.mvCandLT[affiAMVPInfo.numCand] = amvpInfo.mvCand[i];
          affiAMVPInfo.mvCandRT[affiAMVPInfo.numCand] = amvpInfo.mvCand[i];
          affiAMVPInfo.mvCandLB[affiAMVPInfo.numCand] = amvpInfo.mvCand[i];
          affiAMVPInfo.numCand++;
        }
      }
    }
    
    bool PU::addMVPCandUnscaled( const PredictionUnit &pu, const RefPicList &eRefPicList, const int &iRefIdx, const Position &pos, const MvpDir &eDir, AMVPInfo &info, bool affine )
    {
            CodingStructure &cs    = *pu.cs;
      const PredictionUnit *neibPU = NULL;
            Position neibPos;
    
      switch (eDir)
      {
      case MD_LEFT:
        neibPos = pos.offset( -1,  0 );
        break;
      case MD_ABOVE:
        neibPos = pos.offset(  0, -1 );
        break;
      case MD_ABOVE_RIGHT:
        neibPos = pos.offset(  1, -1 );
        break;
      case MD_BELOW_LEFT:
        neibPos = pos.offset( -1,  1 );
        break;
      case MD_ABOVE_LEFT:
        neibPos = pos.offset( -1, -1 );
        break;
      default:
        break;
      }
    
      neibPU = cs.getPURestricted( neibPos, pu, pu.chType );
    
      if( neibPU == NULL || !CU::isInter( *neibPU->cu ) )
      {
        return false;
      }
    
      const MotionInfo& neibMi        = neibPU->getMotionInfo( neibPos );
    
      const int        currRefPOC     = cs.slice->getRefPic( eRefPicList, iRefIdx )->getPOC();
      const RefPicList eRefPicList2nd = ( eRefPicList == REF_PIC_LIST_0 ) ? REF_PIC_LIST_1 : REF_PIC_LIST_0;
    
      for( int predictorSource = 0; predictorSource < 2; predictorSource++ ) // examine the indicated reference picture list, then if not available, examine the other list.
      {
        const RefPicList eRefPicListIndex = ( predictorSource == 0 ) ? eRefPicList : eRefPicList2nd;
        const int        neibRefIdx       = neibMi.refIdx[eRefPicListIndex];
    
        if( neibRefIdx >= 0 && currRefPOC == cs.slice->getRefPOC( eRefPicListIndex, neibRefIdx ) )
        {
          if( affine )
          {
            int i = 0;
            for( i = 0; i < info.numCand; i++ )
            {
              if( info.mvCand[i] == neibMi.mv[eRefPicListIndex] )
              {
                break;
              }
            }
            if( i == info.numCand )
            {
              info.mvCand[info.numCand++] = neibMi.mv[eRefPicListIndex];
    
    #if !REMOVE_MV_ADAPT_PREC
    
              Mv cMvHigh = neibMi.mv[eRefPicListIndex];
              cMvHigh.setHighPrec();
    
    //          CHECK( !neibMi.mv[eRefPicListIndex].highPrec, "Unexpected low precision mv.");
              return true;
            }
          }
          else
          {
            info.mvCand[info.numCand++] = neibMi.mv[eRefPicListIndex];
            return true;
          }
        }
      }
    
    
      return false;
    }
    
    /**
    * \param pInfo
    * \param eRefPicList
    * \param iRefIdx
    * \param uiPartUnitIdx
    * \param eDir
    * \returns bool
    */
    bool PU::addMVPCandWithScaling( const PredictionUnit &pu, const RefPicList &eRefPicList, const int &iRefIdx, const Position &pos, const MvpDir &eDir, AMVPInfo &info, bool affine )
    {
            CodingStructure &cs    = *pu.cs;
      const Slice &slice           = *cs.slice;
      const PredictionUnit *neibPU = NULL;
            Position neibPos;
    
      switch( eDir )
      {
      case MD_LEFT:
        neibPos = pos.offset( -1,  0 );
        break;
      case MD_ABOVE:
        neibPos = pos.offset(  0, -1 );
        break;
      case MD_ABOVE_RIGHT:
        neibPos = pos.offset(  1, -1 );
        break;
      case MD_BELOW_LEFT:
        neibPos = pos.offset( -1,  1 );
        break;
      case MD_ABOVE_LEFT:
        neibPos = pos.offset( -1, -1 );
        break;
      default:
        break;
      }
    
      neibPU = cs.getPURestricted( neibPos, pu, pu.chType );
    
      if( neibPU == NULL || !CU::isInter( *neibPU->cu ) )
      {
        return false;
      }
    
      const MotionInfo& neibMi        = neibPU->getMotionInfo( neibPos );
    
      const RefPicList eRefPicList2nd = ( eRefPicList == REF_PIC_LIST_0 ) ? REF_PIC_LIST_1 : REF_PIC_LIST_0;
    
      const int  currPOC            = slice.getPOC();
      const int  currRefPOC         = slice.getRefPic( eRefPicList, iRefIdx )->poc;
      const bool bIsCurrRefLongTerm = slice.getRefPic( eRefPicList, iRefIdx )->longTerm;
      const int  neibPOC            = currPOC;
    
      for( int predictorSource = 0; predictorSource < 2; predictorSource++ ) // examine the indicated reference picture list, then if not available, examine the other list.
      {
        const RefPicList eRefPicListIndex = (predictorSource == 0) ? eRefPicList : eRefPicList2nd;
        const int        neibRefIdx       = neibMi.refIdx[eRefPicListIndex];
        if( neibRefIdx >= 0 )
        {
          const bool bIsNeibRefLongTerm = slice.getRefPic(eRefPicListIndex, neibRefIdx)->longTerm;
    
          if (bIsCurrRefLongTerm == bIsNeibRefLongTerm)
          {
            Mv cMv = neibMi.mv[eRefPicListIndex];
    
            if( !( bIsCurrRefLongTerm /* || bIsNeibRefLongTerm*/) )
            {
              const int neibRefPOC = slice.getRefPOC( eRefPicListIndex, neibRefIdx );
              const int scale      = xGetDistScaleFactor( currPOC, currRefPOC, neibPOC, neibRefPOC );
    
              if( scale != 4096 )
              {
    
    #if !REMOVE_MV_ADAPT_PREC
    
                if( slice.getSPS()->getSpsNext().getUseHighPrecMv() )
                {
                  cMv.setHighPrec();
                }
    #endif
                cMv = cMv.scaleMv( scale );
              }