Skip to content
Snippets Groups Projects
UnitTools.cpp 99.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • /* The copyright in this software is being made available under the BSD
     * License, included below. This software may be subject to other third party
     * and contributor rights, including patent rights, and no such rights are
     * granted under this license.
     *
     * Copyright (c) 2010-2018, ITU/ISO/IEC
     * All rights reserved.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions are met:
     *
     *  * Redistributions of source code must retain the above copyright notice,
     *    this list of conditions and the following disclaimer.
     *  * Redistributions in binary form must reproduce the above copyright notice,
     *    this list of conditions and the following disclaimer in the documentation
     *    and/or other materials provided with the distribution.
     *  * Neither the name of the ITU/ISO/IEC nor the names of its contributors may
     *    be used to endorse or promote products derived from this software without
     *    specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
     * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
     * THE POSSIBILITY OF SUCH DAMAGE.
     */
    
    /** \file     UnitTool.cpp
     *  \brief    defines operations for basic units
     */
    
    #include "UnitTools.h"
    
    #include "dtrace_next.h"
    
    #include "Unit.h"
    #include "Slice.h"
    #include "Picture.h"
    
    #include <utility>
    #include <algorithm>
    
    // CS tools
    
    
    uint64_t CS::getEstBits(const CodingStructure &cs)
    {
      return cs.fracBits >> SCALE_BITS;
    }
    
    
    
    bool CS::isDualITree( const CodingStructure &cs )
    {
    
      return cs.slice->isIRAP() && !cs.pcv->ISingleTree;
    
    }
    
    UnitArea CS::getArea( const CodingStructure &cs, const UnitArea &area, const ChannelType chType )
    {
      return isDualITree( cs ) ? area.singleChan( chType ) : area;
    }
    
    
    #if DMVR_JVET_LOW_LATENCY_K0217
    void CS::setRefinedMotionField(CodingStructure &cs)
    {
      for (CodingUnit *cu : cs.cus)
      {
        for (auto &pu : CU::traversePUs(*cu))
        {
          if (pu.cs->sps->getSpsNext().getUseDMVR()
            && pu.mergeFlag
            && pu.mergeType == MRG_TYPE_DEFAULT_N
            && !pu.frucMrgMode
            && !pu.cu->LICFlag
            && !pu.cu->affine
            && PU::isBiPredFromDifferentDir(pu))
          {
            pu.mv[REF_PIC_LIST_0] += pu.mvd[REF_PIC_LIST_0];
            pu.mv[REF_PIC_LIST_1] -= pu.mvd[REF_PIC_LIST_0];
            pu.mvd[REF_PIC_LIST_0].setZero();
            PU::spanMotionInfo(pu);
          }
        }
      }
    }
    #endif
    
    // CU tools
    
    bool CU::isIntra(const CodingUnit &cu)
    {
      return cu.predMode == MODE_INTRA;
    }
    
    bool CU::isInter(const CodingUnit &cu)
    {
      return cu.predMode == MODE_INTER;
    }
    
    bool CU::isRDPCMEnabled(const CodingUnit& cu)
    {
      return cu.cs->sps->getSpsRangeExtension().getRdpcmEnabledFlag(cu.predMode == MODE_INTRA ? RDPCM_SIGNAL_IMPLICIT : RDPCM_SIGNAL_EXPLICIT);
    }
    
    bool CU::isLosslessCoded(const CodingUnit &cu)
    {
      return cu.cs->pps->getTransquantBypassEnabledFlag() && cu.transQuantBypass;
    }
    
    bool CU::isSameSlice(const CodingUnit& cu, const CodingUnit& cu2)
    {
      return cu.slice->getIndependentSliceIdx() == cu2.slice->getIndependentSliceIdx();
    }
    
    #if HEVC_TILES_WPP
    bool CU::isSameTile(const CodingUnit& cu, const CodingUnit& cu2)
    {
      return cu.tileIdx == cu2.tileIdx;
    }
    
    bool CU::isSameSliceAndTile(const CodingUnit& cu, const CodingUnit& cu2)
    {
      return ( cu.slice->getIndependentSliceIdx() == cu2.slice->getIndependentSliceIdx() ) && ( cu.tileIdx == cu2.tileIdx );
    }
    #endif
    
    bool CU::isSameCtu(const CodingUnit& cu, const CodingUnit& cu2)
    {
      uint32_t ctuSizeBit = g_aucLog2[cu.cs->sps->getMaxCUWidth()];
    
      Position pos1Ctu(cu.lumaPos().x  >> ctuSizeBit, cu.lumaPos().y  >> ctuSizeBit);
      Position pos2Ctu(cu2.lumaPos().x >> ctuSizeBit, cu2.lumaPos().y >> ctuSizeBit);
    
      return pos1Ctu.x == pos2Ctu.x && pos1Ctu.y == pos2Ctu.y;
    }
    
    uint32_t CU::getIntraSizeIdx(const CodingUnit &cu)
    {
      uint8_t uiWidth = cu.lumaSize().width;
    
      uint32_t  uiCnt   = 0;
    
      while (uiWidth)
      {
        uiCnt++;
        uiWidth >>= 1;
      }
    
      uiCnt -= 2;
    
      return uiCnt > 6 ? 6 : uiCnt;
    }
    
    bool CU::isLastSubCUOfCtu( const CodingUnit &cu )
    {
      const SPS &sps      = *cu.cs->sps;
      const Area cuAreaY = CS::isDualITree( *cu.cs ) ? Area( recalcPosition( cu.chromaFormat, cu.chType, CHANNEL_TYPE_LUMA, cu.blocks[cu.chType].pos() ), recalcSize( cu.chromaFormat, cu.chType, CHANNEL_TYPE_LUMA, cu.blocks[cu.chType].size() ) ) : ( const Area& ) cu.Y();
    
      return ( ( ( ( cuAreaY.x + cuAreaY.width  ) & cu.cs->pcv->maxCUWidthMask  ) == 0 || cuAreaY.x + cuAreaY.width  == sps.getPicWidthInLumaSamples()  ) &&
               ( ( ( cuAreaY.y + cuAreaY.height ) & cu.cs->pcv->maxCUHeightMask ) == 0 || cuAreaY.y + cuAreaY.height == sps.getPicHeightInLumaSamples() ) );
    }
    
    uint32_t CU::getCtuAddr( const CodingUnit &cu )
    {
      return getCtuAddr( cu.blocks[cu.chType].lumaPos(), *cu.cs->pcv );
    }
    
    int CU::predictQP( const CodingUnit& cu, const int prevQP )
    {
      const CodingStructure &cs = *cu.cs;
    
    #if ENABLE_WPP_PARALLELISM
      if( cs.sps->getSpsNext().getUseNextDQP() )
      {
        // Inter-CTU 2D "planar"   c(orner)  a(bove)
        // predictor arrangement:  b(efore)  p(rediction)
    
        // restrict the lookup, as it might cross CTU/slice/tile boundaries
        const CodingUnit *cuA = cs.getCURestricted( cu.blocks[cu.chType].pos().offset(  0, -1 ), cu, cu.chType );
        const CodingUnit *cuB = cs.getCURestricted( cu.blocks[cu.chType].pos().offset( -1,  0 ), cu, cu.chType );
        const CodingUnit *cuC = cs.getCURestricted( cu.blocks[cu.chType].pos().offset( -1, -1 ), cu, cu.chType );
    
        const int a = cuA ? cuA->qp : cs.slice->getSliceQpBase();
        const int b = cuB ? cuB->qp : cs.slice->getSliceQpBase();
        const int c = cuC ? cuC->qp : cs.slice->getSliceQpBase();
    
        return Clip3( ( a < b ? a : b ), ( a > b ? a : b ), a + b - c ); // derived from Martucci's Median Adaptive Prediction, 1990
      }
    
    #endif
      // only predict within the same CTU, use HEVC's above+left prediction
      const int a = ( cu.blocks[cu.chType].y & ( cs.pcv->maxCUHeightMask >> getChannelTypeScaleY( cu.chType, cu.chromaFormat ) ) ) ? ( cs.getCU( cu.blocks[cu.chType].pos().offset( 0, -1 ), cu.chType ) )->qp : prevQP;
      const int b = ( cu.blocks[cu.chType].x & ( cs.pcv->maxCUWidthMask  >> getChannelTypeScaleX( cu.chType, cu.chromaFormat ) ) ) ? ( cs.getCU( cu.blocks[cu.chType].pos().offset( -1, 0 ), cu.chType ) )->qp : prevQP;
    
      return ( a + b + 1 ) >> 1;
    }
    
    bool CU::isQGStart( const CodingUnit& cu )
    {
      const SPS &sps = *cu.cs->sps;
      const PPS &pps = *cu.cs->pps;
    
      return ( cu.blocks[cu.chType].x % ( ( 1 << ( g_aucLog2[sps.getMaxCUWidth()]  - pps.getMaxCuDQPDepth() ) ) >> getChannelTypeScaleX( cu.chType, cu.chromaFormat ) ) ) == 0 &&
             ( cu.blocks[cu.chType].y % ( ( 1 << ( g_aucLog2[sps.getMaxCUHeight()] - pps.getMaxCuDQPDepth() ) ) >> getChannelTypeScaleY( cu.chType, cu.chromaFormat ) ) ) == 0;
    }
    
    uint32_t CU::getNumPUs( const CodingUnit& cu )
    {
      uint32_t cnt = 0;
      PredictionUnit *pu = cu.firstPU;
    
      do
      {
        cnt++;
      } while( ( pu != cu.lastPU ) && ( pu = pu->next ) );
    
      return cnt;
    }
    
    void CU::addPUs( CodingUnit& cu )
    {
      cu.cs->addPU( CS::getArea( *cu.cs, cu, cu.chType ), cu.chType );
    }
    
    
    PartSplit CU::getSplitAtDepth( const CodingUnit& cu, const unsigned depth )
    {
      if( depth >= cu.depth ) return CU_DONT_SPLIT;
    
      const PartSplit cuSplitType = PartSplit( ( cu.splitSeries >> ( depth * SPLIT_DMULT ) ) & SPLIT_MASK );
    
      if     ( cuSplitType == CU_QUAD_SPLIT    ) return CU_QUAD_SPLIT;
    
      else if( cuSplitType == CU_HORZ_SPLIT    ) return CU_HORZ_SPLIT;
    
      else if( cuSplitType == CU_VERT_SPLIT    ) return CU_VERT_SPLIT;
    
      else if( cuSplitType == CU_TRIH_SPLIT    ) return CU_TRIH_SPLIT;
      else if( cuSplitType == CU_TRIV_SPLIT    ) return CU_TRIV_SPLIT;
      else   { THROW( "Unknown split mode"    ); return CU_QUAD_SPLIT; }
    }
    
    bool CU::hasNonTsCodedBlock( const CodingUnit& cu )
    {
      bool hasAnyNonTSCoded = false;
    
      for( auto &currTU : traverseTUs( cu ) )
      {
        for( uint32_t i = 0; i < ::getNumberValidTBlocks( *cu.cs->pcv ); i++ )
        {
          hasAnyNonTSCoded |= ( currTU.blocks[i].valid() && !currTU.transformSkip[i] && TU::getCbf( currTU, ComponentID( i ) ) );
        }
      }
    
      return hasAnyNonTSCoded;
    }
    
    uint32_t CU::getNumNonZeroCoeffNonTs( const CodingUnit& cu )
    {
      uint32_t count = 0;
      for( auto &currTU : traverseTUs( cu ) )
      {
        count += TU::getNumNonZeroCoeffsNonTS( currTU );
      }
    
      return count;
    }
    
    
    
    
    PUTraverser CU::traversePUs( CodingUnit& cu )
    {
      return PUTraverser( cu.firstPU, cu.lastPU->next );
    }
    
    TUTraverser CU::traverseTUs( CodingUnit& cu )
    {
      return TUTraverser( cu.firstTU, cu.lastTU->next );
    }
    
    cPUTraverser CU::traversePUs( const CodingUnit& cu )
    {
      return cPUTraverser( cu.firstPU, cu.lastPU->next );
    }
    
    cTUTraverser CU::traverseTUs( const CodingUnit& cu )
    {
      return cTUTraverser( cu.firstTU, cu.lastTU->next );
    }
    
    // PU tools
    
    int PU::getIntraMPMs( const PredictionUnit &pu, unsigned* mpm, const ChannelType &channelType /*= CHANNEL_TYPE_LUMA*/ )
    {
      const unsigned numMPMs = pu.cs->pcv->numMPMs;
      {
        int numCand      = -1;
        int leftIntraDir = DC_IDX, aboveIntraDir = DC_IDX;
    
        const CompArea &area = pu.block(getFirstComponentOfChannel(channelType));
        const Position &pos  = area.pos();
    
        // Get intra direction of left PU
        const PredictionUnit *puLeft = pu.cs->getPURestricted(pos.offset(-1, 0), pu, channelType);
    
        if (puLeft && CU::isIntra(*puLeft->cu))
        {
          leftIntraDir = puLeft->intraDir[channelType];
    
          if (isChroma(channelType) && leftIntraDir == DM_CHROMA_IDX)
          {
            leftIntraDir = puLeft->intraDir[0];
          }
        }
    
        // Get intra direction of above PU
        const PredictionUnit *puAbove = pu.cs->getPURestricted(pos.offset(0, -1), pu, channelType);
    
        if (puAbove && CU::isIntra(*puAbove->cu) && CU::isSameCtu(*pu.cu, *puAbove->cu))
        {
          aboveIntraDir = puAbove->intraDir[channelType];
    
          if (isChroma(channelType) && aboveIntraDir == DM_CHROMA_IDX)
          {
            aboveIntraDir = puAbove->intraDir[0];
          }
        }
    
        CHECK(2 >= numMPMs, "Invalid number of most probable modes");
    
    
        const int offset = 61;
        const int mod    = 64;
    
    
        if (leftIntraDir == aboveIntraDir)
        {
          numCand = 1;
    
          if (leftIntraDir > DC_IDX)   // angular modes
          {
            mpm[0] = leftIntraDir;
            mpm[1] = ((leftIntraDir + offset) % mod) + 2;
            mpm[2] = ((leftIntraDir - 1) % mod) + 2;
          }
          else   // non-angular
          {
            mpm[0] = PLANAR_IDX;
            mpm[1] = DC_IDX;
            mpm[2] = VER_IDX;
          }
        }
        else
        {
          numCand = 2;
    
          mpm[0] = leftIntraDir;
          mpm[1] = aboveIntraDir;
    
          if (leftIntraDir && aboveIntraDir)   // both modes are non-planar
          {
            mpm[2] = PLANAR_IDX;
          }
          else
          {
            mpm[2] = (leftIntraDir + aboveIntraDir) < 2 ? VER_IDX : DC_IDX;
          }
        }
        for (int i = 0; i < numMPMs; i++)
        {
          CHECK(mpm[i] >= NUM_LUMA_MODE, "Invalid MPM");
        }
        CHECK(numCand == 0, "No candidates found");
        return numCand;
      }
    }
    
    
    void PU::getIntraChromaCandModes( const PredictionUnit &pu, unsigned modeList[NUM_CHROMA_MODE] )
    {
      {
        modeList[  0 ] = PLANAR_IDX;
        modeList[  1 ] = VER_IDX;
        modeList[  2 ] = HOR_IDX;
        modeList[  3 ] = DC_IDX;
        modeList[4] = LM_CHROMA_IDX;
    
    #if JVET_L0338_MDLM
        modeList[5] = MDLM_L_IDX;
        modeList[6] = MDLM_T_IDX;
        modeList[7] = DM_CHROMA_IDX;
    #else
    
    #endif
    
    
        const PredictionUnit *lumaPU = CS::isDualITree( *pu.cs ) ? pu.cs->picture->cs->getPU( pu.blocks[pu.chType].lumaPos(), CHANNEL_TYPE_LUMA ) : &pu;
        const uint32_t lumaMode = lumaPU->intraDir[CHANNEL_TYPE_LUMA];
        for( int i = 0; i < 4; i++ )
        {
          if( lumaMode == modeList[i] )
          {
            modeList[i] = VDIA_IDX;
            break;
          }
        }
      }
    }
    
    
    bool PU::isLMCMode(unsigned mode)
    {
    
    #if JVET_L0338_MDLM
      return (mode >= LM_CHROMA_IDX && mode <= MDLM_T_IDX);
    #else
    
    #endif
    
    }
    bool PU::isLMCModeEnabled(const PredictionUnit &pu, unsigned mode)
    {
      if ( pu.cs->sps->getSpsNext().getUseLMChroma() )
      {
        return true;
      }
      return false;
    }
    
    int PU::getLMSymbolList(const PredictionUnit &pu, int *pModeList)
    {
    
      const int iNeighbors = 5;
      const PredictionUnit* neighboringPUs[ iNeighbors ];
    
      const CompArea& area = pu.Cb();
      const Position posLT = area.topLeft();
      const Position posRT = area.topRight();
      const Position posLB = area.bottomLeft();
    
      neighboringPUs[ 0 ] = pu.cs->getPURestricted( posLB.offset(-1,  0), pu, CHANNEL_TYPE_CHROMA ); //left
      neighboringPUs[ 1 ] = pu.cs->getPURestricted( posRT.offset( 0, -1), pu, CHANNEL_TYPE_CHROMA ); //above
      neighboringPUs[ 2 ] = pu.cs->getPURestricted( posRT.offset( 1, -1), pu, CHANNEL_TYPE_CHROMA ); //aboveRight
      neighboringPUs[ 3 ] = pu.cs->getPURestricted( posLB.offset(-1,  1), pu, CHANNEL_TYPE_CHROMA ); //BelowLeft
      neighboringPUs[ 4 ] = pu.cs->getPURestricted( posLT.offset(-1, -1), pu, CHANNEL_TYPE_CHROMA ); //AboveLeft
    
      int iCount = 0;
      for ( int i = 0; i < iNeighbors; i++ )
      {
        if ( neighboringPUs[i] && CU::isIntra( *(neighboringPUs[i]->cu) ) )
        {
          int iMode = neighboringPUs[i]->intraDir[CHANNEL_TYPE_CHROMA];
          if ( ! PU::isLMCMode( iMode ) )
          {
            iCount++;
          }
        }
      }
    
      bool bNonLMInsert = false;
    
      if ( iCount >= g_aiNonLMPosThrs[0] && ! bNonLMInsert )
      {
    
    #if JVET_L0338_MDLM
      pModeList[iIdx++] = MDLM_L_IDX;
      pModeList[iIdx++] = MDLM_T_IDX;
    #endif
    
      if ( iCount >= g_aiNonLMPosThrs[1] && ! bNonLMInsert )
      {
        pModeList[ iIdx++ ] = -1;
        bNonLMInsert = true;
      }
      if ( ! bNonLMInsert )
      {
        pModeList[ iIdx++ ] = -1;
        bNonLMInsert = true;
      }
    
      return iIdx;
    }
    
    
    
    bool PU::isChromaIntraModeCrossCheckMode( const PredictionUnit &pu )
    {
      return pu.intraDir[CHANNEL_TYPE_CHROMA] == DM_CHROMA_IDX;
    }
    
    uint32_t PU::getFinalIntraMode( const PredictionUnit &pu, const ChannelType &chType )
    {
      uint32_t uiIntraMode = pu.intraDir[chType];
    
      if( uiIntraMode == DM_CHROMA_IDX && !isLuma( chType ) )
      {
        const PredictionUnit &lumaPU = CS::isDualITree( *pu.cs ) ? *pu.cs->picture->cs->getPU( pu.blocks[chType].lumaPos(), CHANNEL_TYPE_LUMA ) : *pu.cs->getPU( pu.blocks[chType].lumaPos(), CHANNEL_TYPE_LUMA );
        uiIntraMode = lumaPU.intraDir[0];
      }
      if( pu.chromaFormat == CHROMA_422 && !isLuma( chType ) )
      {
        uiIntraMode = g_chroma422IntraAngleMappingTable[uiIntraMode];
      }
      return uiIntraMode;
    }
    
    
    #if JVET_L0266_HMVP
    bool PU::xCheckSimilarMotion(const int mergeCandIndex, const int prevCnt, const MergeCtx mergeCandList, bool hasPruned[MRG_MAX_NUM_CANDS])
    {
      for (uint32_t ui = 0; ui < prevCnt; ui++)
      {
        if (hasPruned[ui])
        {
          continue;
        }
    
        if (mergeCandList.interDirNeighbours[ui] == mergeCandList.interDirNeighbours[mergeCandIndex])
    
        {
          if (mergeCandList.interDirNeighbours[ui] == 3)
          {
    
    Li's avatar
    Li committed
            int offset0 = (ui * 2);
            int offset1 = (mergeCandIndex * 2);
    
            if (mergeCandList.mvFieldNeighbours[offset0].refIdx == mergeCandList.mvFieldNeighbours[offset1].refIdx &&
                mergeCandList.mvFieldNeighbours[offset0 + 1].refIdx == mergeCandList.mvFieldNeighbours[offset1 + 1].refIdx &&
                mergeCandList.mvFieldNeighbours[offset0].mv == mergeCandList.mvFieldNeighbours[offset1].mv &&
                mergeCandList.mvFieldNeighbours[offset0 + 1].mv == mergeCandList.mvFieldNeighbours[offset1 + 1].mv
              )
            {
              hasPruned[ui] = true;
              return true;
            }
          }
          else
          {
    
    Li's avatar
    Li committed
            int offset0 = (ui * 2) + mergeCandList.interDirNeighbours[ui] - 1;
            int offset1 = (mergeCandIndex * 2) + mergeCandList.interDirNeighbours[ui] - 1;
    
            if (mergeCandList.mvFieldNeighbours[offset0].refIdx == mergeCandList.mvFieldNeighbours[offset1].refIdx &&
    
                mergeCandList.mvFieldNeighbours[offset0].mv == mergeCandList.mvFieldNeighbours[offset1].mv
    
      return false;
    }
    #if JVET_L0090_PAIR_AVG
    bool PU::addMergeHMVPCand(const Slice &slice, MergeCtx& mrgCtx, bool canFastExit, const int& mrgCandIdx, const uint32_t maxNumMergeCandMin1, int &cnt, const int prevCnt, bool isAvailableSubPu, unsigned subPuMvpPos)
    #else
    bool PU::addMergeHMVPCand(const Slice &slice, MergeCtx& mrgCtx, bool isCandInter[MRG_MAX_NUM_CANDS], bool canFastExit, const int& mrgCandIdx, const uint32_t maxNumMergeCandMin1, int &cnt, const int prevCnt, bool isAvailableSubPu, unsigned subPuMvpPos)
    #endif
    {
      MotionInfo miNeighbor;
      bool hasPruned[MRG_MAX_NUM_CANDS];
    
    Li's avatar
    Li committed
      memset(hasPruned, 0, MRG_MAX_NUM_CANDS * sizeof(bool));
    
      if (isAvailableSubPu)
      {
        hasPruned[subPuMvpPos] = true;
      }
      int num_avai_candInLUT = slice.getAvailableLUTMrgNum();
      for (int mrgIdx = 1; mrgIdx <= num_avai_candInLUT; mrgIdx++)
      {
        miNeighbor = slice.getMotionInfoFromLUTs(num_avai_candInLUT - mrgIdx);
        mrgCtx.interDirNeighbours[cnt] = miNeighbor.interDir;
        mrgCtx.mvFieldNeighbours[cnt << 1].setMvField(miNeighbor.mv[0], miNeighbor.refIdx[0]);
        if (slice.isInterB())
        {
          mrgCtx.mvFieldNeighbours[(cnt << 1) + 1].setMvField(miNeighbor.mv[1], miNeighbor.refIdx[1]);
        }
        if (!xCheckSimilarMotion(cnt, prevCnt, mrgCtx, hasPruned))
        {
    #if !JVET_L0090_PAIR_AVG
          isCandInter[cnt] = true;
    #endif
          if (mrgCandIdx == cnt && canFastExit)
          {
            return true;
          }
          cnt ++;
          if (cnt  == maxNumMergeCandMin1)
          {
            break;
          }
        }
      }
      return false;
    }
    #endif
    
    void PU::getInterMergeCandidates( const PredictionUnit &pu, MergeCtx& mrgCtx,
    #if JVET_L0054_MMVD
    
    {
      const CodingStructure &cs  = *pu.cs;
      const Slice &slice         = *pu.cs->slice;
      const uint32_t maxNumMergeCand = slice.getMaxNumMergeCand();
      const bool canFastExit     = pu.cs->pps->getLog2ParallelMergeLevelMinus2() == 0;
    
    
    #if !JVET_L0090_PAIR_AVG
      // this variable is unused if remove HEVC combined candidates
    
    Yu-Chi Su's avatar
    Yu-Chi Su committed
    #endif
    
    #if JVET_L0646_GBI
        mrgCtx.GBiIdx[ui] = GBI_DEFAULT;
    #endif
    
        mrgCtx.interDirNeighbours[ui] = 0;
        mrgCtx.mrgTypeNeighbours [ui] = MRG_TYPE_DEFAULT_N;
        mrgCtx.mvFieldNeighbours[(ui << 1)    ].refIdx = NOT_VALID;
        mrgCtx.mvFieldNeighbours[(ui << 1) + 1].refIdx = NOT_VALID;
      }
    
      mrgCtx.numValidMergeCand = maxNumMergeCand;
      // compute the location of the current PU
    
      int cnt = 0;
      const Position posLT = pu.Y().topLeft();
      const Position posRT = pu.Y().topRight();
      const Position posLB = pu.Y().bottomLeft();
    
      MotionInfo miAbove, miLeft, miAboveLeft, miAboveRight, miBelowLeft;
    
      //left
      const PredictionUnit* puLeft = cs.getPURestricted( posLB.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( posLB.offset(-1, 0) );
    
    
    
        // get Inter Dir
        mrgCtx.interDirNeighbours[cnt] = miLeft.interDir;
    
    #if JVET_L0646_GBI
        mrgCtx.GBiIdx[cnt] = (mrgCtx.interDirNeighbours[cnt] == 3) ? puLeft->cu->GBiIdx : GBI_DEFAULT;
    #endif
    
        // get Mv from Left
        mrgCtx.mvFieldNeighbours[cnt << 1].setMvField(miLeft.mv[0], miLeft.refIdx[0]);
    
        if (slice.isInterB())
        {
          mrgCtx.mvFieldNeighbours[(cnt << 1) + 1].setMvField(miLeft.mv[1], miLeft.refIdx[1]);
        }
    
        if( mrgCandIdx == cnt && canFastExit )
        {
          return;
        }
    
        cnt++;
      }
    
      // early termination
      if (cnt == maxNumMergeCand)
      {
        return;
      }
    
    
      // above
      const PredictionUnit *puAbove = cs.getPURestricted( posRT.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( posRT.offset( 0, -1 ) );
    
        if( !isAvailableA1 || ( miAbove != miLeft ) )
        {
    
    
          // get Inter Dir
          mrgCtx.interDirNeighbours[cnt] = miAbove.interDir;
    
    #if JVET_L0646_GBI
          mrgCtx.GBiIdx[cnt] = (mrgCtx.interDirNeighbours[cnt] == 3) ? puAbove->cu->GBiIdx : GBI_DEFAULT;
    #endif
    
          mrgCtx.mvFieldNeighbours[cnt << 1].setMvField( miAbove.mv[0], miAbove.refIdx[0] );
    
          if( slice.isInterB() )
          {
            mrgCtx.mvFieldNeighbours[( cnt << 1 ) + 1].setMvField( miAbove.mv[1], miAbove.refIdx[1] );
          }
    
          if( mrgCandIdx == cnt && canFastExit )
          {
            return;
          }
    
          cnt++;
        }
      }
    
      // early termination
      if( cnt == maxNumMergeCand )
      {
        return;
      }
    
      // above right
      const PredictionUnit *puAboveRight = cs.getPURestricted( posRT.offset( 1, -1 ), pu, pu.chType );
    
      bool isAvailableB0 = puAboveRight && isDiffMER( pu, *puAboveRight ) && CU::isInter( *puAboveRight->cu );
    
      if( isAvailableB0 )
      {
        miAboveRight = puAboveRight->getMotionInfo( posRT.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[cnt] = miAboveRight.interDir;
    
    #if JVET_L0646_GBI
          mrgCtx.GBiIdx[cnt] = (mrgCtx.interDirNeighbours[cnt] == 3) ? puAboveRight->cu->GBiIdx : GBI_DEFAULT;
    #endif
    
          mrgCtx.mvFieldNeighbours[cnt << 1].setMvField( miAboveRight.mv[0], miAboveRight.refIdx[0] );
    
          if( slice.isInterB() )
          {
            mrgCtx.mvFieldNeighbours[( cnt << 1 ) + 1].setMvField( miAboveRight.mv[1], miAboveRight.refIdx[1] );
          }
    
          if( mrgCandIdx == cnt && canFastExit )
          {
            return;
          }
    
          cnt++;
        }
      }
      // early termination
      if( cnt == maxNumMergeCand )
      {
        return;
      }
    
      //left bottom
      const PredictionUnit *puLeftBottom = cs.getPURestricted( posLB.offset( -1, 1 ), pu, pu.chType );
    
      bool isAvailableA0 = puLeftBottom && isDiffMER( pu, *puLeftBottom ) && CU::isInter( *puLeftBottom->cu );
    
      if( isAvailableA0 )
      {
        miBelowLeft = puLeftBottom->getMotionInfo( posLB.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[cnt] = miBelowLeft.interDir;
    
    #if JVET_L0646_GBI
          mrgCtx.GBiIdx[cnt] = (mrgCtx.interDirNeighbours[cnt] == 3) ? puLeftBottom->cu->GBiIdx : GBI_DEFAULT;
    #endif
    
          // get Mv from Bottom-Left
          mrgCtx.mvFieldNeighbours[cnt << 1].setMvField( miBelowLeft.mv[0], miBelowLeft.refIdx[0] );
    
          if( slice.isInterB() )
          {
            mrgCtx.mvFieldNeighbours[( cnt << 1 ) + 1].setMvField( miBelowLeft.mv[1], miBelowLeft.refIdx[1] );
          }
    
          if( mrgCandIdx == cnt && canFastExit )
          {
            return;
          }
    
          cnt++;
        }
      }
      // early termination
      if( cnt == maxNumMergeCand )
      {
        return;
      }
    
      bool enableSubPuMvp = slice.getSPS()->getSpsNext().getUseSubPuMvp();
      bool isAvailableSubPu = false;
      unsigned subPuMvpPos = 0;
    
      if( enableSubPuMvp )
      {
        CHECK( mrgCtx.subPuMvpMiBuf   .area() == 0 || !mrgCtx.subPuMvpMiBuf   .buf, "Buffer not initialized" );
    
        mrgCtx.subPuMvpMiBuf   .fill( MotionInfo() );
      }
    
      if( enableSubPuMvp && slice.getEnableTMVPFlag() )
      {
        bool bMrgIdxMatchATMVPCan = ( mrgCandIdx == cnt );
        bool tmpLICFlag           = false;
    
    
        isAvailableSubPu = cs.sps->getSpsNext().getUseATMVP() &&     
          getInterMergeSubPuMvpCand( pu, mrgCtx, tmpLICFlag, cnt 
    #if JVET_L0054_MMVD
            , mmvdList
    #endif
    
    
          mrgCtx.mrgTypeNeighbours[cnt] = MRG_TYPE_SUBPU_ATMVP;
    
          if( bMrgIdxMatchATMVPCan )
          {
            return;
          }
          subPuMvpPos = cnt;
          cnt++;
    
          if( cnt == maxNumMergeCand )
          {
            return;
          }
        }
    
      }
    
      // above left
      if( cnt < ( enableSubPuMvp ? 6 : 4 ) )
      {
        const PredictionUnit *puAboveLeft = cs.getPURestricted( posLT.offset( -1, -1 ), pu, pu.chType );
    
        bool isAvailableB2 = puAboveLeft && isDiffMER( pu, *puAboveLeft ) && CU::isInter( *puAboveLeft->cu );
    
        if( isAvailableB2 )
        {
          miAboveLeft = puAboveLeft->getMotionInfo( posLT.offset( -1, -1 ) );
    
    #if HM_JEM_MERGE_CANDS
          if( ( !isAvailableA1 || ( miLeft != miAboveLeft ) ) && ( !isAvailableB1 || ( miAbove != miAboveLeft ) ) && ( !isAvailableA0 || ( miBelowLeft != miAboveLeft ) ) && ( !isAvailableB0 || ( miAboveRight != miAboveLeft ) ) )
    #else
          if( ( !isAvailableA1 || ( miLeft != miAboveLeft ) ) && ( !isAvailableB1 || ( miAbove != miAboveLeft ) ) )
    #endif
          {
    
    
            // get Inter Dir
            mrgCtx.interDirNeighbours[cnt] = miAboveLeft.interDir;
    
    #if JVET_L0646_GBI
            mrgCtx.GBiIdx[cnt] = (mrgCtx.interDirNeighbours[cnt] == 3) ? puAboveLeft->cu->GBiIdx : GBI_DEFAULT;
    #endif
    
            // get Mv from Above-Left
            mrgCtx.mvFieldNeighbours[cnt << 1].setMvField( miAboveLeft.mv[0], miAboveLeft.refIdx[0] );
    
            if( slice.isInterB() )
            {
              mrgCtx.mvFieldNeighbours[( cnt << 1 ) + 1].setMvField( miAboveLeft.mv[1], miAboveLeft.refIdx[1] );
            }
    
            if( mrgCandIdx == cnt && canFastExit )
            {
              return;
            }
    
            cnt++;
          }
        }
      }
      // early termination
      if (cnt == maxNumMergeCand)
      {
        return;
      }
    
      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;
        Position posC1 = pu.Y().center();
        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       iRefIdx     = 0;
        int       dir         = 0;
        unsigned  uiArrayAddr = cnt;
        bool      bExistMV    = ( C0Avail && getColocatedMVP(pu, REF_PIC_LIST_0, posC0, cColMv, iRefIdx ) )
                                          || getColocatedMVP(pu, REF_PIC_LIST_0, posC1, cColMv, iRefIdx );
    
        if (bExistMV)
        {
          dir     |= 1;
          mrgCtx.mvFieldNeighbours[2 * uiArrayAddr].setMvField(cColMv, iRefIdx);
        }
    
        if (slice.isInterB())
        {
          bExistMV = ( C0Avail && getColocatedMVP(pu, REF_PIC_LIST_1, posC0, cColMv, iRefIdx ) )
                               || getColocatedMVP(pu, REF_PIC_LIST_1, posC1, cColMv, iRefIdx );
          if (bExistMV)
          {
            dir     |= 2;
            mrgCtx.mvFieldNeighbours[2 * uiArrayAddr + 1].setMvField(cColMv, iRefIdx);
          }
        }
    
        if( dir != 0 )
        {
          bool addTMvp = !( cs.sps->getSpsNext().getUseSubPuMvp() && isAvailableSubPu );
          if( !addTMvp )
          {
            if ( dir != mrgCtx.interDirNeighbours[subPuMvpPos] )
            {
              addTMvp = true;
            }
            else
            {
              for( unsigned refList = 0; refList < NUM_REF_PIC_LIST_01; refList++ )
              {
                if( dir & ( 1 << refList ) )
                {
                  if( mrgCtx.mvFieldNeighbours[( cnt << 1 ) + refList] != mrgCtx.mvFieldNeighbours[(subPuMvpPos << 1) + refList] )
                  {
                    addTMvp = true;
                    break;
                  }
                }
              }
            }
          }
    #if HM_JEM_MERGE_CANDS