From 8108d2eb0c6d13066096da0bb6cc05558e3c1c80 Mon Sep 17 00:00:00 2001
From: Donghyun Kim <kimddng@etri.re.kr>
Date: Mon, 11 Nov 2024 19:21:19 +0000
Subject: [PATCH] JVET-AJ0161: OBMC extension with intra prediction (Test 3.3b)

---
 source/Lib/CommonLib/CommonDef.h         |   8 +
 source/Lib/CommonLib/InterPrediction.cpp | 249 ++++++++-
 source/Lib/CommonLib/InterPrediction.h   |  30 +-
 source/Lib/CommonLib/IntraPrediction.cpp | 664 +++++++++++++++++++++++
 source/Lib/CommonLib/IntraPrediction.h   |  18 +-
 source/Lib/CommonLib/TypeDef.h           |   1 +
 source/Lib/CommonLib/UnitTools.cpp       | 176 ++++++
 source/Lib/CommonLib/UnitTools.h         |   4 +
 source/Lib/DecoderLib/DecCu.cpp          |  15 +
 source/Lib/EncoderLib/EncCu.cpp          |  79 +++
 10 files changed, 1241 insertions(+), 3 deletions(-)

diff --git a/source/Lib/CommonLib/CommonDef.h b/source/Lib/CommonLib/CommonDef.h
index 16f3f31c1..85128bf80 100644
--- a/source/Lib/CommonLib/CommonDef.h
+++ b/source/Lib/CommonLib/CommonDef.h
@@ -1255,6 +1255,14 @@ static const int ADAPTIVE_IBC_SUB_GROUP_SIZE =                     6;
 #if JVET_Z0061_TM_OBMC
 static const int TM_OBMC_TEMPLATE_SIZE =                           1;
 #endif
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+enum INTRA_OBMC_NEIGH_STATE
+{
+  INTRA_OBMC_BOTH_NEIGH_AVAIL = 0,
+  INTRA_OBMC_ONLY_ABOVE_AVAIL = 1,
+  INTRA_OBMC_ONLY_LEFT_AVAIL = 2
+};
+#endif
 
 #if JVET_Y0067_ENHANCED_MMVD_MVD_SIGN_PRED
 static const int ADAPTIVE_SUB_GROUP_SIZE_MMVD =   MMVD_MAX_REFINE_NUM;
diff --git a/source/Lib/CommonLib/InterPrediction.cpp b/source/Lib/CommonLib/InterPrediction.cpp
index 5b73a4764..5cc6cea10 100644
--- a/source/Lib/CommonLib/InterPrediction.cpp
+++ b/source/Lib/CommonLib/InterPrediction.cpp
@@ -466,6 +466,13 @@ void InterPrediction::destroy()
   xFree(m_pcRefTplLeft ); m_pcRefTplLeft  = nullptr;
   xFree(m_pcRefTplAbove); m_pcRefTplAbove = nullptr;
 #endif
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+  for (uint32_t ch = 0; ch < MAX_NUM_COMPONENT; ch++)
+  {
+    xFree(m_intraOBMCBuf[ch]); m_intraOBMCBuf[ch] = nullptr;
+    xFree(m_beforeOBMCBuf[ch]); m_beforeOBMCBuf[ch] = nullptr;
+  }
+#endif
 #if INTER_LIC || JVET_Y0067_ENHANCED_MMVD_MVD_SIGN_PRED
 #if JVET_AD0213_LIC_IMP
   for (int i = 0; i < MAX_NUM_COMPONENT; i++)
@@ -642,6 +649,13 @@ void InterPrediction::init( RdCost* pcRdCost, ChromaFormat chromaFormatIDC, cons
     {
       m_affineDmvrBlockTmp[i] = (Pel*)xMalloc(Pel, memBlockWidthExt*memBlockHeight*memBlockNum);
     }
+#endif
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+    for( uint32_t c = 0; c < MAX_NUM_COMPONENT; c++ )
+    {
+      m_intraOBMCBuf[c] = (Pel*)xMalloc(Pel, MAX_CU_SIZE * MAX_CU_SIZE);
+      m_beforeOBMCBuf[c] = (Pel*)xMalloc(Pel, MAX_CU_SIZE * MAX_CU_SIZE);
+    }
 #endif
     m_geoPartBuf[0].create(UnitArea(chromaFormatIDC, Area(0, 0, MAX_CU_SIZE, MAX_CU_SIZE)));
     m_geoPartBuf[1].create(UnitArea(chromaFormatIDC, Area(0, 0, MAX_CU_SIZE, MAX_CU_SIZE)));
@@ -8172,7 +8186,11 @@ void InterPrediction::motionCompensation( PredictionUnit &pu, const RefPicList &
 * 2. Before motion estimation, subtract (scaled) predictors generated by applying neighboring motions to current CU/PU from the original signal of current CU/PU,
 *    to make the motion estimation biased to OBMC.
 */
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+void InterPrediction::subBlockOBMC(PredictionUnit  &pu, PelUnitBuf* pDst, IntraPrediction *pcIntraPred)
+#else
 void InterPrediction::subBlockOBMC(PredictionUnit  &pu, PelUnitBuf* pDst)
+#endif
 {
   if (
     pu.cs->sps->getUseOBMC() == false
@@ -8196,6 +8214,20 @@ void InterPrediction::subBlockOBMC(PredictionUnit  &pu, PelUnitBuf* pDst)
   }
 #endif
 
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+  if (!m_dimdForOBMCFilled)
+  {
+    m_modeGetCheck[0] = pcIntraPred->getGradForOBMC(pu, pu.cu->cs->picture->getRecoBuf(pu.Y()), pu.Y(), *pu.cu, true, pu.cs->pcv->minCUWidth, m_modeBuf[0]);
+    m_modeGetCheck[1] = pcIntraPred->getGradForOBMC(pu, pu.cu->cs->picture->getRecoBuf(pu.Y()), pu.Y(), *pu.cu, false, pu.cs->pcv->minCUWidth, m_modeBuf[1]);
+
+    m_dimdForOBMCFilled = true;
+  }
+
+  PelUnitBuf predIntra = PelUnitBuf(pu.chromaFormat, PelBuf(m_intraOBMCBuf[COMPONENT_Y], pu.blocks[COMPONENT_Y].width, pu.blocks[COMPONENT_Y].height),
+    PelBuf(m_intraOBMCBuf[COMPONENT_Cb], pu.blocks[COMPONENT_Cb].width, pu.blocks[COMPONENT_Cb].height),
+    PelBuf(m_intraOBMCBuf[COMPONENT_Cr], pu.blocks[COMPONENT_Cr].width, pu.blocks[COMPONENT_Cr].height));
+#endif
+
   const UnitArea   orgPuArea = pu;
   PredictionUnit subPu = pu;
 
@@ -8245,6 +8277,10 @@ void InterPrediction::subBlockOBMC(PredictionUnit  &pu, PelUnitBuf* pDst)
   subPu.cu->licFlag = false;
 #endif
   subPu.ciipFlag = false;
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+  subPu.multiRefIdx = 0;
+  subPu.ciipPDPC = false;
+#endif
 #if TM_MRG || (JVET_Z0084_IBC_TM && IBC_TM_MRG)
   subPu.tmMergeFlag = false;
 #endif
@@ -8261,6 +8297,13 @@ void InterPrediction::subBlockOBMC(PredictionUnit  &pu, PelUnitBuf* pDst)
   PelUnitBuf pcYuvTmpPredL0 = m_tmpObmcBufL0.subBuf(UnitAreaRelative(*pu.cu, pu));
   PelUnitBuf pcYuvTmpPredT0 = m_tmpObmcBufT0.subBuf(UnitAreaRelative(*pu.cu, pu));
 
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+  PelUnitBuf beforeOBMC = PelUnitBuf(pu.chromaFormat, PelBuf(m_beforeOBMCBuf[COMPONENT_Y], pu.blocks[COMPONENT_Y].width, pu.blocks[COMPONENT_Y].height),
+    PelBuf(m_beforeOBMCBuf[COMPONENT_Cb], pu.blocks[COMPONENT_Cb].width, pu.blocks[COMPONENT_Cb].height),
+    PelBuf(m_beforeOBMCBuf[COMPONENT_Cr], pu.blocks[COMPONENT_Cr].width, pu.blocks[COMPONENT_Cr].height));
+  beforeOBMC.copyFrom(pcYuvPred);
+#endif
+
   for (int iBlkBoundary = 0; iBlkBoundary < 2; iBlkBoundary++)  // 0 - top; 1 - left
   {
     unsigned int uiLengthInBlock = ((iBlkBoundary == 0) ? uiWidthInBlock : uiHeightInBlock);
@@ -8355,13 +8398,20 @@ void InterPrediction::subBlockOBMC(PredictionUnit  &pu, PelUnitBuf* pDst)
         subPu.cu->altLMFlag = neighPu->cu->altLMFlag;
         subPu.cu->altLMParaUnit = neighPu->cu->secAltLMParaUnit;
 #endif
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+        PelUnitBuf cTmpBeforeOBMC = beforeOBMC.subBuf(predArea);
+#endif
 #if JVET_AA0132_CONFIGURABLE_TM_TOOLS
         if (iOBMCmode == -1)
         {
           xSubBlockMotionCompensation(subPu, cTmp1);
           for (int compID = 0; compID < MAX_NUM_COMPONENT; compID++)
           {
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+            xSubblockOBMC(ComponentID(compID), subPu, cPred, cTmp1, cTmpBeforeOBMC, iBlkBoundary, false, false);
+#else
             xSubblockOBMC(ComponentID(compID), subPu, cPred, cTmp1, iBlkBoundary);
+#endif
           }
           iSub += iLength;
         }
@@ -8377,7 +8427,11 @@ void InterPrediction::subBlockOBMC(PredictionUnit  &pu, PelUnitBuf* pDst)
 
           for (int compID = 0; compID < MAX_NUM_COMPONENT; compID++)
           {
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+            xSubblockTMOBMC(ComponentID(compID), subPu, cPred, cTmp1, cTmpBeforeOBMC, iBlkBoundary, iOBMCmode);
+#else
             xSubblockTMOBMC(ComponentID(compID), subPu, cPred, cTmp1, iBlkBoundary, iOBMCmode);
+#endif
           }
           iSub += iLength;
         }
@@ -8387,7 +8441,11 @@ void InterPrediction::subBlockOBMC(PredictionUnit  &pu, PelUnitBuf* pDst)
 
           for (int compID = 0; compID < MAX_NUM_COMPONENT; compID++)
           {
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+            xSubblockTMOBMC(ComponentID(compID), subPu, cPred, cTmp1, cTmpBeforeOBMC, iBlkBoundary, iOBMCmode);
+#else
             xSubblockTMOBMC(ComponentID(compID), subPu, cPred, cTmp1, iBlkBoundary, iOBMCmode);
+#endif
           }
           iSub += iLength;
         }
@@ -8430,6 +8488,54 @@ void InterPrediction::subBlockOBMC(PredictionUnit  &pu, PelUnitBuf* pDst)
         iSub += iLength;
 #endif
       }
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+      else if (iState == 1 && m_modeGetCheck[iBlkBoundary])
+      {
+        int cumLength = 0;
+        while (cumLength < iLength)
+        {
+          cumLength++;
+          int currLength = 1;
+          while ((m_modeBuf[iBlkBoundary][iSub] == m_modeBuf[iBlkBoundary][iSub + currLength]) 
+            && (cumLength < iLength) && (currLength < (MAX_INTRA_SIZE / uiMinCUW)))
+          {
+            currLength++;
+            cumLength++;
+          }
+
+          const bool intraOBMCCond = (((iBlkBoundary == 0) && ((m_modeBuf[iBlkBoundary][iSub] >= DIA_IDX) && (m_modeBuf[iBlkBoundary][iSub] <= VDIA_IDX)))
+            || ((iBlkBoundary == 1) && (m_modeBuf[iBlkBoundary][iSub] > DC_IDX) && (m_modeBuf[iBlkBoundary][iSub] <= DIA_IDX)));
+          if (!intraOBMCCond)
+          {
+            iSub += currLength;
+            continue;
+          }
+
+          if (iBlkBoundary == 0)
+          {
+            subPu.UnitArea::operator=(UnitArea(pu.chromaFormat, Area(orgPuArea.lumaPos().offset(iSub * uiMinCUW, 0), Size{ currLength*uiMinCUW, uiMinCUW })));
+          }
+          else
+          {
+            subPu.UnitArea::operator=(UnitArea(pu.chromaFormat, Area(orgPuArea.lumaPos().offset(0, iSub * uiMinCUW), Size{ uiMinCUW, currLength*uiMinCUW })));
+          }
+
+          const UnitArea predArea = UnitAreaRelative(orgPuArea, subPu);
+          PelUnitBuf     cPredForBlend = pcYuvPred.subBuf(predArea);
+          PelUnitBuf cTmp = predIntra.subBuf(predArea);
+         
+          subBlockIntraForOBMC(subPu, iSub, (iBlkBoundary == 0), cTmp, pcIntraPred);
+
+          for (int compID = 0; compID < MAX_NUM_COMPONENT; compID++)
+          {
+            PelUnitBuf cTmpBeforeOBMC = beforeOBMC.subBuf(predArea);
+            xSubblockOBMC(ComponentID(compID), subPu, cPredForBlend, cTmp, cTmpBeforeOBMC, iBlkBoundary, false, true);
+          }
+
+          iSub += currLength;
+        }
+      }
+#endif
       else if (iState == 1 || iState == 3)   // consecutive intra neighbors or   skip OBMC based on MV similarity
       {
         iSub += iLength;
@@ -8598,7 +8704,12 @@ void InterPrediction::subBlockOBMC(PredictionUnit  &pu, PelUnitBuf* pDst)
 }
 
 // Function for (weighted) averaging predictors of current block and predictors generated by applying neighboring motions to current block.
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+void InterPrediction::xSubblockOBMC(const ComponentID eComp, PredictionUnit &pu, PelUnitBuf &pcYuvPredDst, PelUnitBuf &pcYuvPredSrc, 
+   PelUnitBuf &pcYuvBefore, int iDir, bool bSubMotion, bool bIsIntra)
+#else
 void InterPrediction::xSubblockOBMC(const ComponentID eComp, PredictionUnit &pu, PelUnitBuf &pcYuvPredDst, PelUnitBuf &pcYuvPredSrc, int iDir, bool bSubMotion)
+#endif
 {
   int iWidth = pu.blocks[eComp].width;
   int iHeight = pu.blocks[eComp].height;
@@ -8612,9 +8723,17 @@ void InterPrediction::xSubblockOBMC(const ComponentID eComp, PredictionUnit &pu,
   Pel* pOrgSrc = pcYuvPredSrc.bufs[eComp].buf;
   const int strideDst = pcYuvPredDst.bufs[eComp].stride;
   const int strideSrc = pcYuvPredSrc.bufs[eComp].stride;
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+  Pel*     pBefore   = pcYuvBefore.bufs[eComp].buf;
+  const int strideBefore = pcYuvBefore.bufs[eComp].stride;
+#endif
 
 #if JVET_AD0193_ADAPTIVE_OBMC_CONTROL
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+  if (!bIsIntra && skipObmcConditionByPixel(pu, eComp, iWidth, iHeight, pOrgSrc, strideSrc, pBefore, strideBefore, pu.cs->sps->getBitDepth(toChannelType(eComp))))
+#else
   if (skipObmcConditionByPixel(pu, eComp, iWidth, iHeight, pOrgSrc, strideSrc, pOrgDst, strideDst, pu.cs->sps->getBitDepth(toChannelType(eComp))))
+#endif
   {
     return;
   }
@@ -9889,7 +10008,11 @@ void InterPrediction::motionCompensationGeo( CodingUnit &cu, MergeCtx &geoMrgCtx
         PU::spanMotionInfo(pu);
 #endif
       cu.isobmcMC = true;
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+      subBlockOBMC(pu, &tmpGeoBuf0, pcIntraPred);
+#else
       subBlockOBMC(pu, &tmpGeoBuf0);
+#endif
       cu.isobmcMC = false;
 #if JVET_AG0164_AFFINE_GPM
       if (pu.affineGPM[0])
@@ -10058,7 +10181,11 @@ void InterPrediction::motionCompensationGeo( CodingUnit &cu, MergeCtx &geoMrgCtx
         PU::spanMotionInfo(pu);
 #endif
       cu.isobmcMC = true;
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+      subBlockOBMC(pu, &tmpGeoBuf1, pcIntraPred);
+#else
       subBlockOBMC(pu, &tmpGeoBuf1);
+#endif
       cu.isobmcMC = false;
 #if JVET_AG0164_AFFINE_GPM
       if (pu.affineGPM[1])
@@ -20941,7 +21068,11 @@ void InterPrediction::xSubblockOBMCCopy(const ComponentID eComp, PredictionUnit
 }
 
 void InterPrediction::xSubblockTMOBMC(const ComponentID eComp, PredictionUnit &pu, PelUnitBuf &pcYuvPredDst,
-                                      PelUnitBuf &pcYuvPredSrc, int iDir, int iOBMCmode)
+                                      PelUnitBuf &pcYuvPredSrc, 
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+  PelUnitBuf &pcYuvBefore, 
+#endif
+  int iDir, int iOBMCmode)
 {
   int iWidth  = pu.blocks[eComp].width;
   int iHeight = pu.blocks[eComp].height;
@@ -20955,9 +21086,17 @@ void InterPrediction::xSubblockTMOBMC(const ComponentID eComp, PredictionUnit &p
   Pel *     pOrgSrc   = pcYuvPredSrc.bufs[eComp].buf;
   const int strideDst = pcYuvPredDst.bufs[eComp].stride;
   const int strideSrc = pcYuvPredSrc.bufs[eComp].stride;
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+  Pel *     pBefore   = pcYuvBefore.bufs[eComp].buf;
+  const int strideBefore = pcYuvBefore.bufs[eComp].stride;
+#endif
 
 #if JVET_AD0193_ADAPTIVE_OBMC_CONTROL
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+  if (skipObmcConditionByPixel(pu, eComp, iWidth, iHeight, pOrgSrc, strideSrc, pBefore, strideBefore, pu.cs->sps->getBitDepth(toChannelType(eComp))))
+#else
   if (skipObmcConditionByPixel(pu, eComp, iWidth, iHeight, pOrgSrc, strideSrc, pOrgDst, strideDst, pu.cs->sps->getBitDepth(toChannelType(eComp))))
+#endif
   {
     return;
   }
@@ -40379,3 +40518,111 @@ std::vector<Mv> InterPrediction::deriveMVDFromMVSDIdxAffineSI(PredictionUnit& pu
     return true;
   }
 #endif
+
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+  void InterPrediction::subBlockIntraForOBMC(PredictionUnit &subPu, const int iSub, const bool isAbove, PelUnitBuf &cTmp, IntraPrediction *pcIntraPred)
+  {
+    const uint32_t uiMinCUW = subPu.cs->pcv->minCUWidth;
+    const int scaleX = getComponentScaleX((ComponentID)COMPONENT_Cb, subPu.chromaFormat);
+    const int scaleY = getComponentScaleX((ComponentID)COMPONENT_Cb, subPu.chromaFormat);
+
+    const int origIntraDir[2] = {subPu.intraDir[0], subPu.intraDir[1]};
+    subPu.intraDir[0] = m_modeBuf[isAbove ? 0 : 1][iSub];
+    subPu.intraDir[1] = m_modeBuf[isAbove ? 0 : 1][iSub];
+
+    pcIntraPred->m_intraOBMCNeighState = (iSub == 0) ? INTRA_OBMC_BOTH_NEIGH_AVAIL : (isAbove ? INTRA_OBMC_ONLY_ABOVE_AVAIL : INTRA_OBMC_ONLY_LEFT_AVAIL);
+
+    bool prepareRef = false;
+    prepareRef |= (isAbove && subPu.Y().topLeft().x != 0 && subPu.intraDir[0] < VER_IDX);
+    prepareRef |= (!isAbove && subPu.Y().topLeft().y != 0 && subPu.intraDir[0] > HOR_IDX);
+    prepareRef &= pcIntraPred->m_intraOBMCNeighState != INTRA_OBMC_BOTH_NEIGH_AVAIL;
+    pcIntraPred->m_refSampleForOBMC = prepareRef;
+
+    CodingUnit *origCu = subPu.cu;
+    CodingUnit tempCu = *subPu.cu;
+    tempCu.firstPU = &subPu;
+    subPu.cu = &tempCu;
+    subPu.cu->UnitArea::operator=(UnitArea(subPu));
+    
+    for (uint32_t c = 0; c < MAX_NUM_COMPONENT; c++)
+    {
+      const uint32_t uiScaledMinCUW = c == 0 ? uiMinCUW : (isAbove ? (uiMinCUW >> scaleX) : (uiMinCUW >> scaleY));
+
+      if (iSub != 0)
+      {
+        pcIntraPred->initIntraPatternChTypeOBMC(tempCu, subPu.blocks[c]);
+      }
+      else
+      {
+        pcIntraPred->initIntraPatternChType(tempCu, subPu.blocks[c]);
+      }
+      const int sizeSide = (isAbove ? subPu.blocks[c].height : subPu.blocks[c].width) + 2;
+
+      if (prepareRef)
+      {
+        memset(pcIntraPred->m_refSampleForOBMCBuf, -1, sizeof( Pel ) * (MAX_INTRA_SIZE + 3));
+        memset(pcIntraPred->m_refSampleForOBMCBufFiltered, -1, sizeof( Pel ) * (MAX_INTRA_SIZE + 3));
+        const PelBuf pRecoBuf = subPu.cu->cs->picture->getRecoBuf().bufs[c];
+        for (int i = 0; i <= sizeSide; i++)
+        {
+          const int currPos_ = isAbove ? (subPu.blocks[c].topLeft().x - 1 - i) : (subPu.blocks[c].topLeft().y - 1 - i);
+          const Position currPos = isAbove ? Position(currPos_, subPu.blocks[c].topLeft().y - 1) : Position(subPu.blocks[c].topLeft().x - 1, currPos_);
+
+          if ((currPos_ + 1) % uiScaledMinCUW == 0)
+          {
+            bool valid = subPu.cs->isDecomp(currPos, (c == 0) ? CHANNEL_TYPE_LUMA : CHANNEL_TYPE_CHROMA);
+            if (valid)
+            {
+              valid = (subPu.cs->getCURestricted(currPos, *subPu.cu, (c == 0) ? CHANNEL_TYPE_LUMA : CHANNEL_TYPE_CHROMA) != NULL);
+            }
+            if (!valid)
+            {
+              for (int j = i; j <= sizeSide; j++)
+              {
+                pcIntraPred->m_refSampleForOBMCBuf[j] = pcIntraPred->m_refSampleForOBMCBuf[i-1];
+              }
+              break;
+            }
+          }
+
+          pcIntraPred->m_refSampleForOBMCBuf[i] = pRecoBuf.at(currPos);
+        }
+
+        if (pcIntraPred->getIpaParam()->refFilterFlag)
+        {
+          pcIntraPred->m_refSampleForOBMCBufFiltered[0] = pcIntraPred->m_refSampleForOBMCBuf[0];
+          pcIntraPred->m_refSampleForOBMCBufFiltered[sizeSide] = pcIntraPred->m_refSampleForOBMCBuf[sizeSide];
+          for (int i = 1; i < sizeSide; i++)
+          {
+            pcIntraPred->m_refSampleForOBMCBufFiltered[i] = (pcIntraPred->m_refSampleForOBMCBuf[i - 1] + 2 * pcIntraPred->m_refSampleForOBMCBuf[i] + pcIntraPred->m_refSampleForOBMCBuf[i + 1] + 2) >> 2;
+          }
+        }
+        else
+        {
+          for (int i = 0; i <= sizeSide; i++)
+          {
+            pcIntraPred->m_refSampleForOBMCBufFiltered[i] = pcIntraPred->m_refSampleForOBMCBuf[i];
+          }
+        }
+      }
+
+      pcIntraPred->predIntraAng(ComponentID(c), cTmp.bufs[c], subPu);
+
+      if (c == 0)
+      {
+        if (subPu.cs->picHeader->getLmcsEnabledFlag() && m_pcReshape->getCTUFlag())
+        {
+          cTmp.bufs[c].rspSignal(m_pcReshape->getInvLUT());
+        }
+      }
+    }
+
+    pcIntraPred->m_refSampleForOBMC = false;
+            
+    subPu.cu = origCu;
+    pcIntraPred->m_intraOBMCNeighState = INTRA_OBMC_BOTH_NEIGH_AVAIL;
+
+    subPu.intraDir[0] = origIntraDir[0];
+    subPu.intraDir[1] = origIntraDir[1];
+  }
+#endif
\ No newline at end of file
diff --git a/source/Lib/CommonLib/InterPrediction.h b/source/Lib/CommonLib/InterPrediction.h
index af488be51..bd92d7aef 100644
--- a/source/Lib/CommonLib/InterPrediction.h
+++ b/source/Lib/CommonLib/InterPrediction.h
@@ -200,6 +200,10 @@ private:
   static const int  m_LICShiftDiff = 12;
   int               m_LICMultApprox[64];
 #endif
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+  Pel* m_intraOBMCBuf[MAX_NUM_COMPONENT];
+  Pel* m_beforeOBMCBuf[MAX_NUM_COMPONENT];
+#endif
 
 #if JVET_AG0136_INTRA_TMP_LIC
   std::array<int, 7> m_arrayLicParams;
@@ -578,7 +582,11 @@ protected:
   bool xCheckIdenticalMotionInfo(MotionInfo orgMotionInfo, MotionInfo targetMotionInfo, MergeType puMergeType);
 #endif
 #if ENABLE_OBMC
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+  void xSubblockOBMC(const ComponentID eComp, PredictionUnit &pu, PelUnitBuf &pcYuvPredDst, PelUnitBuf &pcYuvPredSrc, PelUnitBuf &pcYuvBefore, int iDir, bool bSubMotion = false, bool bIsIntra = false);
+#else
   void xSubblockOBMC(const ComponentID eComp, PredictionUnit &pu, PelUnitBuf &pcYuvPredDst, PelUnitBuf &pcYuvPredSrc, int iDir, bool bSubMotion = false);
+#endif
   void xSubBlockMotionCompensation(PredictionUnit &pu, PelUnitBuf &pcYuvPred);
   void xSubblockOBMCBlending(const ComponentID eComp, PredictionUnit &pu, PelUnitBuf &pcYuvPredDst, PelUnitBuf &pcYuvPredSrc1, PelUnitBuf &pcYuvPredSrc2, PelUnitBuf &pcYuvPredSrc3, PelUnitBuf &pcYuvPredSrc4, bool isAboveAvail = false, bool isLeftAvail = false, bool isBelowAvail = false, bool isRightAvail = false, bool bSubMotion = false);
 #endif
@@ -747,7 +755,11 @@ public:
   }
 #endif
 #if ENABLE_OBMC
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+  void    subBlockOBMC(PredictionUnit &pu, PelUnitBuf *pDst = nullptr, IntraPrediction *pcIntraPred = nullptr);
+#else
   void    subBlockOBMC(PredictionUnit  &pu, PelUnitBuf *pDst = nullptr);
+#endif
 #if JVET_AD0193_ADAPTIVE_OBMC_CONTROL
   bool    isSCC(const PredictionUnit  &pu);
   bool    skipObmcConditionByPixel(PredictionUnit& pu, ComponentID comp, int width, int height, const Pel* src, int strideSrc, const Pel* dst, int strideDst, int bitDepth);
@@ -1050,7 +1062,10 @@ public:
   void xSubblockOBMCCopy(const ComponentID eComp, PredictionUnit &pu, PelUnitBuf &pcYuvPredDst,
                          PelUnitBuf &pcYuvPredSrc, int iDir);
   void xSubblockTMOBMC(const ComponentID eComp, PredictionUnit &pu, PelUnitBuf &pcYuvPredDst, PelUnitBuf &pcYuvPredSrc,
-                       int iDir, int iOBMCmode = 0);
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+    PelUnitBuf &pcYuvBefore,
+#endif
+    int iDir, int iOBMCmode = 0);
 #endif
 #if JVET_W0090_ARMC_TM || JVET_AA0070_RRIBC
   void    updateCandList(uint32_t uiCand, Distortion uiCost, uint32_t uiMrgCandNum, uint32_t* rdCandList, Distortion* candCostList);
@@ -1705,6 +1720,16 @@ public:
   void setFillCurTplAboveARMC(bool b) { m_fillCurTplAboveARMC = b; }
   void setFillCurTplLeftARMC(bool b) { m_fillCurTplLeftARMC = b; }
 #endif
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+private:
+  bool m_dimdForOBMCFilled;
+  int m_modeBuf[2][MAX_CU_SIZE >> MIN_CU_LOG2];
+  int m_modeGetCheck[2];
+public:
+  void setDIMDForOBMC(bool b) { m_dimdForOBMCFilled = b; }
+  void setModeGetCheck(int i, bool b) { m_modeGetCheck[i] = b; }
+  void setClearModeBuf(int i) { memset(m_modeBuf[i], -1, sizeof(int) * (MAX_CU_SIZE >> MIN_CU_LOG2)); }
+#endif
 
 #if JVET_AG0276_NLIC
 public:
@@ -1872,6 +1897,9 @@ public:
 #if JVET_AF0073_INTER_CCP_MERGE
   bool deriveInterCcpMergePrediction( TransformUnit* tu, const PelBuf& lumaReconstruction, PelBuf& inBufCb, PelBuf& inBufCr, PelBuf& outBufCb, PelBuf& outBufCr, CCPModelCandidate interCcpMergeList[], int validNum);
 #endif
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+  void subBlockIntraForOBMC(PredictionUnit &subPu, const int iSub, const bool isAbove, PelUnitBuf &cTmp, IntraPrediction *pcIntraPred);
+#endif
 };
 
 #if TM_AMVP || TM_MRG || JVET_Z0084_IBC_TM
diff --git a/source/Lib/CommonLib/IntraPrediction.cpp b/source/Lib/CommonLib/IntraPrediction.cpp
index 1bd8636ef..d4e4c4d13 100644
--- a/source/Lib/CommonLib/IntraPrediction.cpp
+++ b/source/Lib/CommonLib/IntraPrediction.cpp
@@ -152,6 +152,12 @@ IntraPrediction::IntraPrediction()
 #if MMLM
   m_encPreRDRun = false;
 #endif
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+  m_intraOBMCNeighState = INTRA_OBMC_BOTH_NEIGH_AVAIL;
+  m_refSampleForOBMC = false;
+  memset(m_refSampleForOBMCBuf, -1, sizeof( Pel ) * (MAX_INTRA_SIZE + 3));
+  memset(m_refSampleForOBMCBufFiltered, -1, sizeof( Pel ) * (MAX_INTRA_SIZE + 3));
+#endif
 #if JVET_V0130_INTRA_TMP
   m_pppTarPatch = NULL;
 #endif
@@ -3654,6 +3660,13 @@ void IntraPrediction::initPredIntraParams(const PredictionUnit & pu, const CompA
     }
   }
 
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+  if (pu.cu->isobmcMC)
+  {
+    m_ipaParam.applyPDPC &= (m_intraOBMCNeighState == INTRA_OBMC_BOTH_NEIGH_AVAIL);
+    m_ipaParam.useGradPDPC &= (m_intraOBMCNeighState == INTRA_OBMC_BOTH_NEIGH_AVAIL);
+  }
+#endif
   // high level conditions and DC intra prediction
   if(   sps.getSpsRangeExtension().getIntraSmoothingDisabledFlag()
     || !isLuma( chType )
@@ -3696,6 +3709,13 @@ void IntraPrediction::initPredIntraParams(const PredictionUnit & pu, const CompA
 #endif
     }
 
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+    if (pu.cu->isobmcMC)
+    {
+      filterFlag = false;
+    }
+#endif
+
     // Selelection of either ([1 2 1] / 4 ) refrence filter OR Gaussian 4-tap interpolation filter
     if (filterFlag)
     {
@@ -3857,9 +3877,30 @@ void IntraPrediction::xPredIntraAng( const CPelBuf &pSrc, PelBuf &pDst, const Ch
 
     // Extend the Main reference to the left.
     int sizeSide = bIsModeVer ? height : width;
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+    if (m_refSampleForOBMC && (m_intraOBMCNeighState == INTRA_OBMC_ONLY_LEFT_AVAIL && intraPredAngle == -32))
+    {
+      for (int k = 1; k <= width + 2; k++)
+      {
+        int val = m_refSampleForOBMCBufFiltered[k];
+        refMain[k] = (Pel)ClipPel(val, clpRng);
+      }
+    }
+#endif
     // left extend by 1
     for (int k = -(sizeSide + 1); k <= -1; k++)
     {
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+      if (m_refSampleForOBMC && !(m_intraOBMCNeighState == INTRA_OBMC_ONLY_LEFT_AVAIL && intraPredAngle == -32))
+      {
+        if ((m_intraOBMCNeighState == INTRA_OBMC_ONLY_ABOVE_AVAIL && bIsModeVer == true) || (m_intraOBMCNeighState == INTRA_OBMC_ONLY_LEFT_AVAIL && bIsModeVer == false))
+        {
+          int val = m_refSampleForOBMCBufFiltered[abs(k)];
+          refMain[k] = (Pel)ClipPel(val, clpRng);
+        }
+        continue;
+      }
+#endif
       int frac32precision = (-k * absInvAngle + 8) >> 4;
       int intpel = frac32precision >> 5;
       int fracpel = frac32precision & 31;
@@ -5337,6 +5378,12 @@ inline int  isAboveAvailable      ( const CodingUnit &cu, const ChannelType &chT
 inline int  isLeftAvailable       ( const CodingUnit &cu, const ChannelType &chType, const Position &posLT, const uint32_t uiNumUnitsInPU, const uint32_t unitWidth, bool *validFlags );
 inline int  isAboveRightAvailable ( const CodingUnit &cu, const ChannelType &chType, const Position &posRT, const uint32_t uiNumUnitsInPU, const uint32_t unitHeight, bool *validFlags );
 inline int  isBelowLeftAvailable  ( const CodingUnit &cu, const ChannelType &chType, const Position &posLB, const uint32_t uiNumUnitsInPU, const uint32_t unitHeight, bool *validFlags );
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+inline int  isAboveAvailableOBMC      ( const CodingUnit &cu, const ChannelType &chType, const Position &posLT, const uint32_t uiNumUnitsInPU, const uint32_t unitWidth, bool *validFlags, const int obmcNeighState );
+inline int  isLeftAvailableOBMC       ( const CodingUnit &cu, const ChannelType &chType, const Position &posLT, const uint32_t uiNumUnitsInPU, const uint32_t unitWidth, bool *validFlags, const int obmcNeighState );
+inline int  isAboveRightAvailableOBMC ( const CodingUnit &cu, const ChannelType &chType, const Position &posRT, const uint32_t uiNumUnitsInPU, const uint32_t unitHeight, bool *validFlags, const int obmcNeighState );
+inline int  isBelowLeftAvailableOBMC  ( const CodingUnit &cu, const ChannelType &chType, const Position &posLB, const uint32_t uiNumUnitsInPU, const uint32_t unitHeight, bool *validFlags, const int obmcNeighState );
+#endif
 
 #if JVET_AB0155_SGPM
 void IntraPrediction::initIntraPatternChType(const CodingUnit &cu, const CompArea &area, const bool forceRefFilterFlag,
@@ -5380,6 +5427,13 @@ void IntraPrediction::initIntraPatternChType(const CodingUnit &cu, const CompAre
 #if JVET_AC0112_IBC_GPM
   m_ipaParam.fetchRef2nd &= !cu.firstPU->ibcGpmFlag;
 #endif
+#endif
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+  if (cu.isobmcMC)
+  {
+    applyFusion = false;
+    m_ipaParam.fetchRef2nd = false;
+  }
 #endif
 
   if (!forceRefFilterFlag)
@@ -5435,6 +5489,38 @@ void IntraPrediction::initIntraPatternChType(const CodingUnit &cu, const CompAre
   }
 }
 
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+void IntraPrediction::initIntraPatternChTypeOBMC(const CodingUnit &cu, const CompArea &area)
+{
+#if !INTRA_RM_SMALL_BLOCK_SIZE_CONSTRAINTS
+  CHECK(area.width == 2, "Width of 2 is not supported");
+#endif
+  const CodingStructure& cs   = *cu.cs;
+
+  m_ipaParam.fetchRef2nd = false;
+
+  initPredIntraParams(*cu.firstPU, area, *cs.sps);
+
+  m_ipaParam.applyFusion = false;
+
+  Pel *refBufUnfiltered = m_refBuffer[area.compID][PRED_BUF_UNFILTERED];
+  Pel *refBufFiltered   = m_refBuffer[area.compID][PRED_BUF_FILTERED];
+
+  setReferenceArrayLengths( area );
+#if JVET_AH0209_PDP
+  m_refAvailable = false;
+#endif
+  // ----- Step 1: unfiltered reference samples -----
+  xFillReferenceSamplesOBMC(cs.picture->getRecoBuf(area), refBufUnfiltered, area, cu);
+
+  // ----- Step 2: filtered reference samples -----
+  if( m_ipaParam.refFilterFlag)
+  {
+    xFilterReferenceSamples( refBufUnfiltered, refBufFiltered, area, *cs.sps, cu.firstPU->multiRefIdx );
+  }
+}
+#endif
+
 void IntraPrediction::initIntraPatternChTypeISP(const CodingUnit& cu, const CompArea& area, PelBuf& recBuf, const bool forceRefFilterFlag)
 {
   const CodingStructure& cs = *cu.cs;
@@ -5800,7 +5886,11 @@ void IntraPrediction::xFillReferenceSamples2(const CPelBuf &recoBuf, const CompA
 void IntraPrediction::xFillReferenceSamples( const CPelBuf &recoBuf, Pel* refBufUnfiltered, const CompArea &area, const CodingUnit &cu )
 {
 #if JVET_AH0209_PDP
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+  if (m_intraOBMCNeighState == INTRA_OBMC_BOTH_NEIGH_AVAIL && !cu.firstPU->multiRefIdx && !cu.ispMode && !cu.plIdx)
+#else
   if (!cu.firstPU->multiRefIdx && !cu.ispMode && !cu.plIdx)
+#endif
   {
     xFillReferenceSamples2(recoBuf, area, cu);
   }
@@ -6171,6 +6261,372 @@ void IntraPrediction::xFillReferenceSamples( const CPelBuf &recoBuf, Pel* refBuf
   }
 }
 
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+void IntraPrediction::xFillReferenceSamplesOBMC( const CPelBuf &recoBuf, Pel* refBufUnfiltered, const CompArea &area, const CodingUnit &cu )
+{
+  m_refAvailable                = false;
+
+  const ChannelType      chType = toChannelType( area.compID );
+  const CodingStructure &cs     = *cu.cs;
+  const SPS             &sps    = *cs.sps;
+  const PreCalcValues   &pcv    = *cs.pcv;
+
+  const int multiRefIdx         = (area.compID == COMPONENT_Y) ? cu.firstPU->multiRefIdx : 0;
+
+  const int  tuWidth            = area.width;
+  const int  tuHeight           = area.height;
+  const int  predSize           = m_topRefLength;
+  const int  predHSize          = m_leftRefLength;
+  const int predStride = predSize + 1 + multiRefIdx;
+  m_refBufferStride[area.compID] = predStride;
+
+  const bool noShift            = pcv.noChroma2x2 && area.width == 4; // don't shift on the lowest level (chroma not-split)
+  const int  unitWidth          = tuWidth  <= 2 && cu.ispMode && isLuma(area.compID) ? tuWidth  : pcv.minCUWidth  >> (noShift ? 0 : getComponentScaleX(area.compID, sps.getChromaFormatIdc()));
+  const int  unitHeight         = tuHeight <= 2 && cu.ispMode && isLuma(area.compID) ? tuHeight : pcv.minCUHeight >> (noShift ? 0 : getComponentScaleY(area.compID, sps.getChromaFormatIdc()));
+
+ #if JVET_Y0116_EXTENDED_MRL_LIST
+  int leftMrlUnitNum  = multiRefIdx / unitHeight;
+  int aboveMrlUnitNum = multiRefIdx / unitWidth;
+#endif
+
+  const int  totalAboveUnits    = (predSize + (unitWidth - 1)) / unitWidth;
+  const int  totalLeftUnits     = (predHSize + (unitHeight - 1)) / unitHeight;
+#if JVET_Y0116_EXTENDED_MRL_LIST
+  const int  totalUnits         = totalAboveUnits + totalLeftUnits + 1 + leftMrlUnitNum + aboveMrlUnitNum; //+1 for top-left
+#else
+  const int  totalUnits         = totalAboveUnits + totalLeftUnits + 1; //+1 for top-left
+#endif
+  const int  numAboveUnits      = std::max<int>( tuWidth / unitWidth, 1 );
+  const int  numLeftUnits       = std::max<int>( tuHeight / unitHeight, 1 );
+  const int  numAboveRightUnits = totalAboveUnits - numAboveUnits;
+  const int  numLeftBelowUnits  = totalLeftUnits - numLeftUnits;
+
+  CHECK( numAboveUnits <= 0 || numLeftUnits <= 0 || numAboveRightUnits <= 0 || numLeftBelowUnits <= 0, "Size not supported" );
+
+  // ----- Step 1: analyze neighborhood -----
+  const Position posLT          = area;
+  const Position posRT          = area.topRight();
+  const Position posLB          = area.bottomLeft();
+
+#if JVET_Y0116_EXTENDED_MRL_LIST
+#if JVET_AC0094_REF_SAMPLES_OPT
+  bool neighborFlags[4 * (MAX_NUM_PART_IDXS_IN_CTU_WIDTH << 2) + MAX_REF_LINE_IDX + 1];
+#else
+  bool  neighborFlags[4 * MAX_NUM_PART_IDXS_IN_CTU_WIDTH + MAX_REF_LINE_IDX + 1];
+#endif
+#else
+  bool  neighborFlags[4 * MAX_NUM_PART_IDXS_IN_CTU_WIDTH + 1];
+#endif
+  int   numIntraNeighbor = 0;
+
+  memset( neighborFlags, 0, totalUnits );
+
+  neighborFlags[totalLeftUnits+leftMrlUnitNum] = isAboveLeftAvailable( cu, chType, posLT.offset(-multiRefIdx, -multiRefIdx) );
+  numIntraNeighbor += neighborFlags[totalLeftUnits+leftMrlUnitNum] ? 1 : 0;
+  numIntraNeighbor += isAboveAvailableOBMC     ( cu, chType, posLT.offset(-aboveMrlUnitNum*unitWidth, -multiRefIdx), aboveMrlUnitNum,      unitWidth,  (neighborFlags + totalLeftUnits + 1 + leftMrlUnitNum), m_intraOBMCNeighState );
+  numIntraNeighbor += isLeftAvailableOBMC      ( cu, chType, posLT.offset(-multiRefIdx, -leftMrlUnitNum*unitHeight), leftMrlUnitNum,       unitHeight, (neighborFlags + totalLeftUnits - 1 + leftMrlUnitNum), m_intraOBMCNeighState );
+  numIntraNeighbor += isAboveAvailableOBMC     ( cu, chType, posLT.offset(0, -multiRefIdx), numAboveUnits,      unitWidth,  (neighborFlags + totalLeftUnits + 1 + leftMrlUnitNum + aboveMrlUnitNum), m_intraOBMCNeighState );
+  numIntraNeighbor += isAboveRightAvailableOBMC( cu, chType, posRT.offset(0, -multiRefIdx), numAboveRightUnits, unitWidth,  (neighborFlags + totalLeftUnits + 1 + leftMrlUnitNum + aboveMrlUnitNum + numAboveUnits), m_intraOBMCNeighState );
+  numIntraNeighbor += isLeftAvailableOBMC      ( cu, chType, posLT.offset(-multiRefIdx, 0), numLeftUnits,       unitHeight, (neighborFlags + totalLeftUnits - 1), m_intraOBMCNeighState );
+  numIntraNeighbor += isBelowLeftAvailableOBMC ( cu, chType, posLB.offset(-multiRefIdx, 0), numLeftBelowUnits,  unitHeight, (neighborFlags + totalLeftUnits - 1 - numLeftUnits), m_intraOBMCNeighState );
+
+  // ----- Step 2: fill reference samples (depending on neighborhood) -----
+
+  const Pel*  srcBuf    = recoBuf.buf;
+  const int   srcStride = recoBuf.stride;
+        Pel*  ptrDst    = refBufUnfiltered;
+  const Pel*  ptrSrc;
+
+  // Fill top-left sample(s) if available
+  ptrSrc = srcBuf - (1 + multiRefIdx) * srcStride - (1 + multiRefIdx);
+  ptrDst = refBufUnfiltered;
+#if JVET_Y0116_EXTENDED_MRL_LIST
+  if (neighborFlags[totalLeftUnits+leftMrlUnitNum])
+#else
+  if (neighborFlags[totalLeftUnits])
+#endif
+  {
+    ptrDst[0] = ptrSrc[0];
+    ptrDst[predStride] = ptrSrc[0];
+#if JVET_Y0116_EXTENDED_MRL_LIST
+    for (int i = 1; i <= multiRefIdx-leftMrlUnitNum*unitHeight; i++)
+#else
+    for (int i = 1; i <= multiRefIdx; i++)
+#endif
+    {
+      ptrDst[i] = ptrSrc[i];
+      ptrDst[i + predStride] = ptrSrc[i * srcStride];
+    }
+  }
+    
+
+  if (m_intraOBMCNeighState == INTRA_OBMC_ONLY_LEFT_AVAIL)
+  {
+    // Fill left & below-left samples if available (downwards)
+#if JVET_Y0116_EXTENDED_MRL_LIST
+    ptrSrc += (1 + multiRefIdx - leftMrlUnitNum * unitHeight) * srcStride;
+    ptrDst += (1 + multiRefIdx - leftMrlUnitNum * unitHeight) + predStride;
+#else
+    ptrSrc += (1 + multiRefIdx) * srcStride;
+    ptrDst += (1 + multiRefIdx) + predStride;
+#endif
+#if JVET_Y0116_EXTENDED_MRL_LIST
+    for (int unitIdx = totalLeftUnits + leftMrlUnitNum - 1; unitIdx > 0; unitIdx--)
+#else
+    for (int unitIdx = totalLeftUnits - 1; unitIdx > 0; unitIdx--)
+#endif
+    {
+      if (neighborFlags[unitIdx])
+      {
+        for (int i = 0; i < unitHeight; i++)
+        {
+          ptrDst[i] = ptrSrc[i * srcStride];
+        }
+      }
+      ptrSrc += unitHeight * srcStride;
+      ptrDst += unitHeight;
+    }
+    // Fill last below-left sample(s)
+    if (neighborFlags[0])
+    {
+      int lastSample = (predHSize % unitHeight == 0) ? unitHeight : predHSize % unitHeight;
+      for (int i = 0; i < lastSample; i++)
+      {
+        ptrDst[i] = ptrSrc[i * srcStride];
+      }
+    }
+  }
+
+  if (m_intraOBMCNeighState == INTRA_OBMC_ONLY_ABOVE_AVAIL)
+  {
+    // Fill above & above-right samples if available (left-to-right)
+#if JVET_Y0116_EXTENDED_MRL_LIST
+    ptrSrc = srcBuf - srcStride * (1 + multiRefIdx) - aboveMrlUnitNum * unitWidth;
+    ptrDst = refBufUnfiltered + 1 + multiRefIdx - aboveMrlUnitNum * unitWidth;
+#else
+    ptrSrc = srcBuf - srcStride * (1 + multiRefIdx);
+    ptrDst = refBufUnfiltered + 1 + multiRefIdx;
+#endif
+#if JVET_Y0116_EXTENDED_MRL_LIST
+    for (int unitIdx = totalLeftUnits + leftMrlUnitNum + 1; unitIdx < totalUnits - 1; unitIdx++)
+#else
+    for (int unitIdx = totalLeftUnits + 1; unitIdx < totalUnits - 1; unitIdx++)
+#endif
+    {
+      if (neighborFlags[unitIdx])
+      {
+        for (int j = 0; j < unitWidth; j++)
+        {
+          ptrDst[j] = ptrSrc[j];
+        }
+      }
+      ptrSrc += unitWidth;
+      ptrDst += unitWidth;
+    }
+    // Fill last above-right sample(s)
+    if (neighborFlags[totalUnits - 1])
+    {
+      int lastSample = (predSize % unitWidth == 0) ? unitWidth : predSize % unitWidth;
+      for (int j = 0; j < lastSample; j++)
+      {
+        ptrDst[j] = ptrSrc[j];
+      }
+    }
+  }
+
+  // pad from first available down to the last below-left
+  ptrDst = refBufUnfiltered;
+  int lastAvailUnit = 0;
+  if (!neighborFlags[0])
+  {
+    int firstAvailUnit = 1;
+    while (firstAvailUnit < totalUnits && !neighborFlags[firstAvailUnit])
+    {
+      firstAvailUnit++;
+    }
+
+    // first available sample
+    int firstAvailRow = -1;
+    int firstAvailCol = 0;
+
+#if JVET_Y0116_EXTENDED_MRL_LIST
+    if (firstAvailUnit < totalLeftUnits + leftMrlUnitNum)
+    {
+      firstAvailRow = (totalLeftUnits + leftMrlUnitNum - firstAvailUnit) * unitHeight + multiRefIdx - leftMrlUnitNum * unitHeight;
+    }
+    else if (firstAvailUnit == totalLeftUnits + leftMrlUnitNum)
+    {
+      firstAvailRow = multiRefIdx - leftMrlUnitNum * unitHeight;
+    }
+    else
+    {
+      firstAvailCol = (firstAvailUnit - totalLeftUnits - leftMrlUnitNum - 1) * unitWidth + 1 + multiRefIdx - aboveMrlUnitNum * unitWidth;
+    }
+#else
+    if (firstAvailUnit < totalLeftUnits)
+    {
+      firstAvailRow = (totalLeftUnits - firstAvailUnit) * unitHeight + multiRefIdx;
+    }
+    else if (firstAvailUnit == totalLeftUnits)
+    {
+      firstAvailRow = multiRefIdx;
+    }
+    else
+    {
+      firstAvailCol = (firstAvailUnit - totalLeftUnits - 1) * unitWidth + 1 + multiRefIdx;
+    }
+#endif
+    const Pel firstAvailSample = ptrDst[firstAvailRow < 0 ? firstAvailCol : firstAvailRow + predStride];
+
+    // last sample below-left (n.a.)
+    int lastRow = predHSize + multiRefIdx;
+
+    if (m_intraOBMCNeighState == INTRA_OBMC_ONLY_ABOVE_AVAIL)
+    {
+      lastRow = std::min(lastRow, 1);
+    }
+    else
+    {
+      firstAvailCol = std::min(firstAvailCol, 1);
+    }
+
+    // fill left column
+    for (int i = lastRow; i > firstAvailRow; i--)
+    {
+      ptrDst[i + predStride] = firstAvailSample;
+    }
+    // fill top row
+    if (firstAvailCol > 0)
+    {
+      for (int j = 0; j < firstAvailCol; j++)
+      {
+        ptrDst[j] = firstAvailSample;
+      }
+    }
+    lastAvailUnit = firstAvailUnit;
+  }
+
+  // pad all other reference samples.
+  int currUnit = lastAvailUnit + 1;
+
+  if (m_intraOBMCNeighState == INTRA_OBMC_ONLY_ABOVE_AVAIL)
+  {
+    currUnit = std::max(totalLeftUnits+leftMrlUnitNum+1, currUnit);
+  }
+
+  while (currUnit < totalUnits)
+  {
+    if (m_intraOBMCNeighState == INTRA_OBMC_ONLY_LEFT_AVAIL && (totalLeftUnits+leftMrlUnitNum+1 < currUnit))
+    {
+      break;
+    }
+
+    if (!neighborFlags[currUnit]) // samples not available
+    {
+      // last available sample
+      int lastAvailRow = -1;
+      int lastAvailCol = 0;
+#if JVET_Y0116_EXTENDED_MRL_LIST
+      if (lastAvailUnit < totalLeftUnits + leftMrlUnitNum)
+      {
+        lastAvailRow = (totalLeftUnits + leftMrlUnitNum - lastAvailUnit - 1) * unitHeight + multiRefIdx - leftMrlUnitNum * unitHeight + 1;
+      }
+      else if (lastAvailUnit == totalLeftUnits + leftMrlUnitNum)
+      {
+        lastAvailCol = multiRefIdx- leftMrlUnitNum * unitHeight;
+      }
+      else
+      {
+        lastAvailCol = (lastAvailUnit - totalLeftUnits - leftMrlUnitNum) * unitWidth + multiRefIdx - aboveMrlUnitNum * unitWidth;
+      }
+#else
+      if (lastAvailUnit < totalLeftUnits)
+      {
+        lastAvailRow = (totalLeftUnits - lastAvailUnit - 1) * unitHeight + multiRefIdx + 1;
+      }
+      else if (lastAvailUnit == totalLeftUnits)
+      {
+        lastAvailCol = multiRefIdx;
+      }
+      else
+      {
+        lastAvailCol = (lastAvailUnit - totalLeftUnits) * unitWidth + multiRefIdx;
+      }
+#endif
+      const Pel lastAvailSample = ptrDst[lastAvailRow < 0 ? lastAvailCol : lastAvailRow + predStride];
+
+      // fill current unit with last available sample
+#if JVET_Y0116_EXTENDED_MRL_LIST
+      if (currUnit < totalLeftUnits + leftMrlUnitNum)
+      {
+        if (m_intraOBMCNeighState == INTRA_OBMC_ONLY_ABOVE_AVAIL)
+        {
+          for (int i = lastAvailRow - 1; i >= lastAvailRow - unitHeight; i--)
+          {
+            ptrDst[i + predStride] = lastAvailSample;
+          }
+        }
+      }
+      else if (currUnit == totalLeftUnits + leftMrlUnitNum)
+      {
+        if (m_intraOBMCNeighState == INTRA_OBMC_ONLY_ABOVE_AVAIL)
+        {
+          for (int i = 0; i < multiRefIdx - leftMrlUnitNum * unitHeight + 1; i++)
+          {
+            ptrDst[i + predStride] = lastAvailSample;
+          }
+        }
+        if (m_intraOBMCNeighState == INTRA_OBMC_ONLY_LEFT_AVAIL)
+        {
+          for (int j = 0; j < multiRefIdx - aboveMrlUnitNum * unitWidth + 1; j++)
+          {
+            ptrDst[j] = lastAvailSample;
+          }
+        }
+      }
+      else
+      {
+        int numSamplesInUnit = (currUnit == totalUnits - 1) ? ((predSize % unitWidth == 0) ? unitWidth : predSize % unitWidth) : unitWidth;
+        for (int j = lastAvailCol + 1; j <= lastAvailCol + numSamplesInUnit; j++)
+        {
+          ptrDst[j] = lastAvailSample;
+        }
+      }
+#else
+      if (currUnit < totalLeftUnits)
+      {
+        for (int i = lastAvailRow - 1; i >= lastAvailRow - unitHeight; i--)
+        {
+          ptrDst[i + predStride] = lastAvailSample;
+        }
+      }
+      else if (currUnit == totalLeftUnits)
+      {
+        for (int i = 0; i < multiRefIdx + 1; i++)
+        {
+          ptrDst[i + predStride] = lastAvailSample;
+        }
+        for (int j = 0; j < multiRefIdx + 1; j++)
+        {
+          ptrDst[j] = lastAvailSample;
+        }
+      }
+      else
+      {
+        int numSamplesInUnit = (currUnit == totalUnits - 1) ? ((predSize % unitWidth == 0) ? unitWidth : predSize % unitWidth) : unitWidth;
+        for (int j = lastAvailCol + 1; j <= lastAvailCol + numSamplesInUnit; j++)
+        {
+          ptrDst[j] = lastAvailSample;
+        }
+      }
+#endif
+    }
+    lastAvailUnit = currUnit;
+    currUnit++;
+  }
+}
+#endif
+
 #if JVET_AH0136_CHROMA_REORDERING
 void IntraPrediction::xFillReferenceSamplesForCoLuma(const CPelBuf &recoBuf, Pel* refBufUnfiltered, const CompArea &area, const CodingUnit &cu)
 {
@@ -6640,10 +7096,17 @@ void IntraPrediction::xFilterReferenceSamples(const Pel *refBufUnfiltered, Pel *
 
   refBufFiltered[0] = topLeft;
 
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+  if (!(m_intraOBMCNeighState == INTRA_OBMC_ONLY_LEFT_AVAIL && m_refSampleForOBMC))
+  {
+#endif
   for (int i = 1; i < predSize; i++)
   {
     refBufFiltered[i] = (refBufUnfiltered[i - 1] + 2 * refBufUnfiltered[i] + refBufUnfiltered[i + 1] + 2) >> 2;
   }
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+  }
+#endif
   refBufFiltered[predSize] = refBufUnfiltered[predSize];
 
   refBufFiltered += predStride;
@@ -6651,10 +7114,17 @@ void IntraPrediction::xFilterReferenceSamples(const Pel *refBufUnfiltered, Pel *
 
   refBufFiltered[0] = topLeft;
 
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+  if (!(m_intraOBMCNeighState == INTRA_OBMC_ONLY_ABOVE_AVAIL && m_refSampleForOBMC))
+  {
+#endif
   for (int i = 1; i < predHSize; i++)
   {
     refBufFiltered[i] = (refBufUnfiltered[i - 1] + 2 * refBufUnfiltered[i] + refBufUnfiltered[i + 1] + 2) >> 2;
   }
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+  }
+#endif
   refBufFiltered[predHSize] = refBufUnfiltered[predHSize];
 }
 
@@ -10237,6 +10707,70 @@ void IntraPrediction::ibcCiipBlending(Pel *pDst, int strideDst, const Pel *pSrc0
 }
 #endif
 
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+bool IntraPrediction::getGradForOBMC(const PredictionUnit pu, const CPelBuf &recoBuf, const CompArea &area, CodingUnit &cu, const bool isAbove, const int blkSize, int* modeBuf)
+{
+  const int templateSize = 1;
+  
+  const CodingStructure  &cs = *cu.cs;
+
+  const Pel *pReco = recoBuf.buf;
+  const uint32_t uiWidth = area.width;
+  const uint32_t uiHeight = area.height;
+  const int iStride = recoBuf.stride;
+  
+  int totalUnits;
+  if (isAbove)
+  {
+    const Position posNeighbor(pu.lumaPos().offset(1, -1));
+
+    PredictionUnit* tmpPu = pu.cs->getPU(posNeighbor, pu.chType);
+    if (tmpPu == nullptr)
+    {
+      return false;
+    }
+    
+    totalUnits = uiWidth / blkSize;
+  }
+  else
+  {
+    const Position posNeighbor(pu.lumaPos().offset(-1, 1));
+
+    PredictionUnit* tmpPu = pu.cs->getPU(posNeighbor, pu.chType);
+    if (tmpPu == nullptr)
+    {
+      return false;
+    }
+
+    totalUnits = uiHeight / blkSize;
+  }
+  memset(modeBuf, -1, sizeof(int) * totalUnits);
+
+  const Position posLT = cu.Y().topLeft().offset(-1, -1);
+  const Position posLB = cu.Y().bottomLeft().offset(-1, 1);
+  const Position posRT = cu.Y().topRight().offset(1, -1);
+
+  if (isAbove)
+  {
+    const bool isExistFirst = (cs.getCURestricted(posLT, cu, cu.chType) != nullptr);
+    const bool isExistLast = (cs.getCURestricted(posRT, cu, cu.chType) != nullptr);
+    const Pel *pRecoAbove = pReco - 2 * iStride;
+
+    calcGradForOBMC(pu, pRecoAbove, iStride, totalUnits, templateSize, blkSize, modeBuf, isAbove, isExistFirst, isExistLast);
+  }
+  else
+  {
+    const bool isExistFirst = (cs.getCURestricted(posLT, cu, cu.chType) != nullptr);
+    const bool isExistLast = (cs.getCURestricted(posLB, cu, cu.chType) != nullptr);
+    const Pel *pRecoLeft = pReco - 2;
+
+    calcGradForOBMC(pu, pRecoLeft, iStride, totalUnits, templateSize, blkSize, modeBuf, isAbove, isExistFirst, isExistLast);
+  }
+
+  return true;
+}
+#endif
+
 #if ENABLE_DIMD
 void IntraPrediction::deriveDimdMode(const CPelBuf &recoBuf, const CompArea &area, CodingUnit &cu)
 {
@@ -12703,6 +13237,136 @@ int isBelowLeftAvailable(const CodingUnit &cu, const ChannelType &chType, const
   return numIntra;
 }
 
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+int isAboveAvailableOBMC(const CodingUnit &cu, const ChannelType &chType, const Position &posLT, const uint32_t uiNumUnitsInPU, const uint32_t unitWidth, bool *bValidFlags, const int obmcNeighState)
+{
+  if (obmcNeighState == INTRA_OBMC_ONLY_LEFT_AVAIL)
+  { 
+    return 0;
+  }
+
+  const CodingStructure& cs = *cu.cs;
+
+  bool *    validFlags = bValidFlags;
+  int       numIntra   = 0;
+  const int maxDx      = uiNumUnitsInPU * unitWidth;
+
+  for (int dx = 0; dx < maxDx; dx += unitWidth)
+  {
+    const Position refPos = posLT.offset(dx, -1);
+
+    if (!cs.isDecomp(refPos, chType))
+    {
+      break;
+    }
+
+    const bool valid = (cs.getCURestricted(refPos, cu, chType) != NULL);
+    numIntra += valid ? 1 : 0;
+    *validFlags = valid;
+
+    validFlags++;
+  }
+
+  return numIntra;
+}
+
+int isLeftAvailableOBMC(const CodingUnit &cu, const ChannelType &chType, const Position &posLT, const uint32_t uiNumUnitsInPU, const uint32_t unitHeight, bool *bValidFlags, const int obmcNeighState)
+{
+  if (obmcNeighState == INTRA_OBMC_ONLY_ABOVE_AVAIL)
+  {
+    return 0;
+  }
+  
+  const CodingStructure& cs = *cu.cs;
+
+  bool *    validFlags = bValidFlags;
+  int       numIntra   = 0;
+  const int maxDy      = uiNumUnitsInPU * unitHeight;
+
+  for (int dy = 0; dy < maxDy; dy += unitHeight)
+  {
+    const Position refPos = posLT.offset(-1, dy);
+
+    if (!cs.isDecomp(refPos, chType))
+    {
+      break;
+    }
+
+    const bool valid = (cs.getCURestricted(refPos, cu, chType) != NULL);
+    numIntra += valid ? 1 : 0;
+    *validFlags = valid;
+
+    validFlags--;
+  }
+
+  return numIntra;
+}
+
+int isAboveRightAvailableOBMC(const CodingUnit &cu, const ChannelType &chType, const Position &posRT, const uint32_t uiNumUnitsInPU, const uint32_t unitWidth, bool *bValidFlags, const int obmcNeighState )
+{
+  if (obmcNeighState == INTRA_OBMC_ONLY_LEFT_AVAIL)
+  {
+    return 0;
+  }
+  
+  const CodingStructure& cs = *cu.cs;
+
+  bool *    validFlags = bValidFlags;
+  int       numIntra   = 0;
+  const int maxDx      = uiNumUnitsInPU * unitWidth;
+
+  for (int dx = 0; dx < maxDx; dx += unitWidth)
+  {
+    const Position refPos = posRT.offset(unitWidth + dx, -1);
+
+    if (!cs.isDecomp(refPos, chType))
+    {
+      break;
+    }
+
+    const bool valid = (cs.getCURestricted(refPos, cu, chType) != NULL);
+    numIntra += valid ? 1 : 0;
+    *validFlags = valid;
+
+    validFlags++;
+  }
+
+  return numIntra;
+}
+
+int isBelowLeftAvailableOBMC(const CodingUnit &cu, const ChannelType &chType, const Position &posLB, const uint32_t uiNumUnitsInPU, const uint32_t unitHeight, bool *bValidFlags, const int obmcNeighState )
+{
+  if (obmcNeighState == INTRA_OBMC_ONLY_ABOVE_AVAIL)
+  {
+    return 0;
+  }
+  
+  const CodingStructure& cs = *cu.cs;
+
+  bool *    validFlags = bValidFlags;
+  int       numIntra   = 0;
+  const int maxDy      = uiNumUnitsInPU * unitHeight;
+
+  for (int dy = 0; dy < maxDy; dy += unitHeight)
+  {
+    const Position refPos = posLB.offset(-1, unitHeight + dy);
+
+    if (!cs.isDecomp(refPos, chType))
+    {
+      break;
+    }
+
+    const bool valid = (cs.getCURestricted(refPos, cu, chType) != NULL);
+    numIntra += valid ? 1 : 0;
+    *validFlags = valid;
+
+    validFlags--;
+  }
+
+  return numIntra;
+}
+#endif
+
 #if JVET_AA0126_GLM
 Pel IntraPrediction::xGlmGetLumaVal(const int s[6], const int c[6], const int glmIdx, const Pel val) const
 {
diff --git a/source/Lib/CommonLib/IntraPrediction.h b/source/Lib/CommonLib/IntraPrediction.h
index b833e2b26..d702673e5 100644
--- a/source/Lib/CommonLib/IntraPrediction.h
+++ b/source/Lib/CommonLib/IntraPrediction.h
@@ -258,6 +258,12 @@ public:
 #if MMLM
   bool m_encPreRDRun;
 #endif
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+  int m_intraOBMCNeighState;
+  bool m_refSampleForOBMC;
+  Pel m_refSampleForOBMCBuf[MAX_INTRA_SIZE + 3];
+  Pel m_refSampleForOBMCBufFiltered[MAX_INTRA_SIZE + 3];
+#endif
 
 #if JVET_AC0147_CCCM_NO_SUBSAMPLING
 #if JVET_AD0202_CCCM_MDF
@@ -561,7 +567,9 @@ protected:
 #endif
 
   void xFillReferenceSamples      ( const CPelBuf &recoBuf,      Pel* refBufUnfiltered, const CompArea &area, const CodingUnit &cu );
-
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+  void xFillReferenceSamplesOBMC( const CPelBuf &recoBuf, Pel* refBufUnfiltered, const CompArea &area, const CodingUnit &cu );
+#endif
 #if JVET_AH0136_CHROMA_REORDERING
   void xFillReferenceSamplesForCoLuma(const CPelBuf &recoBuf, Pel* refBufUnfiltered, const CompArea &area, const CodingUnit &cu);
 #endif
@@ -812,6 +820,11 @@ public:
   );
 #endif
 
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+  const IntraPredParam* getIpaParam () { return &m_ipaParam; };
+  bool getGradForOBMC(const PredictionUnit pu, const CPelBuf &recoBuf, const CompArea &area, CodingUnit &cu, const bool isAbove, const int blkSize, int* modeBuf);
+#endif
+
 #if ENABLE_DIMD
   static void deriveDimdMode      (const CPelBuf &recoBuf, const CompArea &area, CodingUnit &cu);
 #if JVET_Z0050_DIMD_CHROMA_FUSION && ENABLE_DIMD
@@ -1046,6 +1059,9 @@ public:
 #else
   void initIntraPatternChType     (const CodingUnit &cu, const CompArea &area, const bool forceRefFilterFlag = false); // use forceRefFilterFlag to get both filtered and unfiltered buffers
 #endif
+#endif
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+  void initIntraPatternChTypeOBMC(const CodingUnit &cu, const CompArea &area);
 #endif
   void initIntraPatternChTypeISP  (const CodingUnit& cu, const CompArea& area, PelBuf& piReco, const bool forceRefFilterFlag = false); // use forceRefFilterFlag to get both filtered and unfiltered buffers
 
diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index 9c70282d9..dd2bbe4da 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -292,6 +292,7 @@
 #if ENABLE_OBMC
 #define JVET_AC0335_CONTENT_ADAPTIVE_OBMC_ENABLING        1 // JVET-AC0335: Content adaptive OBMC enabling
 #define JVET_AD0193_ADAPTIVE_OBMC_CONTROL                 1 // JVET-AD0193: Adaptive OBMC control
+#define JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED              1 // JVET-AJ0161: OBMC extension with intra prediction
 #endif
 
 #if JVET_X0049_BDMVR_SW_OPT
diff --git a/source/Lib/CommonLib/UnitTools.cpp b/source/Lib/CommonLib/UnitTools.cpp
index 7d0faa132..5c98aea75 100644
--- a/source/Lib/CommonLib/UnitTools.cpp
+++ b/source/Lib/CommonLib/UnitTools.cpp
@@ -33076,6 +33076,182 @@ int buildHistogram(const Pel *pReco, int iStride, uint32_t uiHeight, uint32_t ui
 }
 #endif
 
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+void calcGradForOBMC(const PredictionUnit pu, const Pel *pReco, const int iStride, const int totalUnits, const int templateSize, const int blkSize, int *modeBuf, const int isAbove, const bool isExistFirst, const bool isExistLast)
+{
+  int       angTable[17]   = { 0,     2048,  4096,  6144,  8192,  12288, 16384, 20480, 24576,
+                       28672, 32768, 36864, 40960, 47104, 53248, 59392, 65536 };
+  int       offsets[4]     = { HOR_IDX, HOR_IDX, VER_IDX, VER_IDX };
+  int       dirs[4]        = { -1, 1, -1, 1 };
+  int       mapXgrY1[2][2] = { { 1, 0 }, { 0, 1 } };
+  int       mapXgrY0[2][2] = { { 2, 3 }, { 3, 2 } };
+  
+  int toSaveAmp[NUM_LUMA_MODE];
+  const Position startPos = pu.Y().topLeft().offset(isAbove ? 0 : -1, isAbove ? -1 : 0);
+  PredictionUnit* tmpPu = nullptr;
+  MotionInfo mi;
+
+  const int extraPosNum = 2;
+  bool prevSaved = false;
+  int savedPrevAmp[extraPosNum << 1];
+  int savedPrevAng[extraPosNum << 1];
+
+  for (int i = 0; i < (extraPosNum << 1); i++)
+  {
+    savedPrevAmp[i] = -1;
+    savedPrevAng[i] = -1;
+  }
+
+  int i = 0;
+
+  while (i < totalUnits)
+  {
+    const int off = i * blkSize;
+    const Position  posSubBlock(startPos.offset(isAbove ? off : 0, isAbove ? 0 : off));
+    tmpPu = pu.cs->getPU(posSubBlock, pu.chType);
+    mi = tmpPu->getMotionInfo(posSubBlock);
+
+    if (mi.isInter && !mi.isIBCmot)
+    {
+      if (tmpPu->gpmIntraFlag || tmpPu->gpmInterIbcFlag)
+      {
+        i++;
+      }
+      else
+      {
+        int forNext;
+        if (isAbove)
+        {
+          forNext = tmpPu->Y().bottomRight().x - posSubBlock.x;
+          forNext /= blkSize;
+        }
+        else
+        {
+          forNext = tmpPu->Y().bottomRight().y - posSubBlock.y;
+          forNext /= blkSize;
+        }
+        forNext++;
+
+        for (int k = 0; k < forNext; k++)
+        {
+          mi = tmpPu->getMotionInfo(posSubBlock.offset(isAbove ? blkSize * k : 0, isAbove ? 0 : blkSize * k));
+        }
+        i += forNext;
+      }
+      
+      prevSaved = false;
+      continue;
+    }
+
+    memset(toSaveAmp, 0, sizeof(int) * NUM_LUMA_MODE);
+    int bestMode = PLANAR_IDX;
+    
+    for (int j = -extraPosNum; j < blkSize + extraPosNum; j++)
+    {
+      if ((!isExistFirst && (i == 0) && (j < 0)) || (!isExistLast && ((i + 1) >= totalUnits) && (j >= blkSize - 1)))
+      {
+        continue;
+      }
+
+      int iAmp = 0, iAngUneven = -1;
+      if (j < extraPosNum && prevSaved)
+      {
+        iAmp = savedPrevAmp[j + extraPosNum];
+        iAngUneven = savedPrevAng[j + extraPosNum];        
+        toSaveAmp[iAngUneven] += iAmp;
+      }
+      else
+      {
+        const Pel *pRec = &(pReco[isAbove ? (i * blkSize + j) : ((i * blkSize + j) * iStride)]);
+
+        int iDy = pRec[0] + pRec[iStride] - pRec[+1] - pRec[iStride + 1];
+        int iDx = - pRec[0] + pRec[iStride] - pRec[+1] + pRec[iStride + 1];
+
+        if (iDy == 0 && iDx == 0)
+        {
+          if (j >= blkSize - extraPosNum)
+          {
+            savedPrevAmp[j - (blkSize - extraPosNum)] = 0;
+            savedPrevAng[j - (blkSize - extraPosNum)] = 0;
+          }
+          continue;
+        }
+
+        iAmp       = (int) (abs(iDx) + abs(iDy));
+        if (iDx != 0 && iDy != 0)
+        {
+          int signx  = iDx < 0 ? 1 : 0;
+          int signy  = iDy < 0 ? 1 : 0;
+          int absx   = iDx < 0 ? -iDx : iDx;
+          int absy   = iDy < 0 ? -iDy : iDy;
+          int gtY    = absx > absy ? 1 : 0;
+          int region = gtY ? mapXgrY1[signy][signx] : mapXgrY0[signy][signx];
+#if JVET_X0149_TIMD_DIMD_LUT
+          int s0   = gtY ? absy : absx;
+          int s1   = gtY ? absx : absy;
+          int x    = floorLog2(s1);
+          int norm = (s1 << 4 >> x) & 15;
+          int v    = g_gradDivTable[norm] | 8;
+          x += (norm != 0);
+          int shift = 13 - x;
+          int ratio;
+          if (shift < 0)
+          {
+            shift   = -shift;
+            int add = (1 << (shift - 1));
+            ratio   = (s0 * v + add) >> shift;
+          }
+          else
+          {
+            ratio = (s0 * v) << shift;
+          }
+#else
+          float fRatio = gtY ? static_cast<float>(absy) / static_cast<float>(absx)
+                              : static_cast<float>(absx) / static_cast<float>(absy);
+          float fRatioScaled = fRatio * (1 << 16);
+          int ratio = static_cast<int>(fRatioScaled);
+#endif
+          int idx = 16;
+          for (int i = 1; i < 17; i++)
+          {
+            if (ratio <= angTable[i])
+            {
+              idx = ratio - angTable[i - 1] < angTable[i] - ratio ? i - 1 : i;
+              break;
+            }
+          }
+
+          iAngUneven = offsets[region] + dirs[region] * idx;
+        }
+        else
+        {
+          iAngUneven = iDx == 0 ? VER_IDX : HOR_IDX;
+        }
+
+        CHECK(iAngUneven < 0, "Wrong mode in DIMD histogram");
+        CHECK(iAngUneven >= NUM_LUMA_MODE, "Wrong mode in DIMD histogram");
+
+        toSaveAmp[iAngUneven] += iAmp;
+      
+        if (j >= blkSize - extraPosNum)
+        {
+          savedPrevAmp[j - (blkSize - extraPosNum)] = iAmp;
+          savedPrevAng[j - (blkSize - extraPosNum)] = iAngUneven;
+        }
+      }
+      if (toSaveAmp[iAngUneven] > toSaveAmp[bestMode])
+      {
+        bestMode = iAngUneven;
+      }
+    }
+
+    prevSaved = true;
+    modeBuf[i] = bestMode;
+    i++;
+  }
+}
+#endif
+
 #if JVET_AG0164_AFFINE_GPM
 int  PU::getAffGPMCtxOffset(const PredictionUnit& pu)
 {
diff --git a/source/Lib/CommonLib/UnitTools.h b/source/Lib/CommonLib/UnitTools.h
index 49b8239e1..287548ad3 100644
--- a/source/Lib/CommonLib/UnitTools.h
+++ b/source/Lib/CommonLib/UnitTools.h
@@ -1471,3 +1471,7 @@ void getTemporalBv(const PredictionUnit &pu, std::vector<MotionInfo>& temporalMi
 #if JVET_AG0061_INTER_LFNST_NSPT
 int buildHistogram(const Pel *pReco, int iStride, uint32_t uiHeight, uint32_t uiWidth, int *piHistogram, int direction, int bw, int bh);
 #endif
+
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+void calcGradForOBMC(const PredictionUnit pu, const Pel *pReco, const int iStride, const int totalUnits, const int templateSize, const int blkSize, int *modeBuf, const int isAbove, const bool isExistFirst, const bool isExistLast);
+#endif
\ No newline at end of file
diff --git a/source/Lib/DecoderLib/DecCu.cpp b/source/Lib/DecoderLib/DecCu.cpp
index 4ce223339..ab9ed528c 100644
--- a/source/Lib/DecoderLib/DecCu.cpp
+++ b/source/Lib/DecoderLib/DecCu.cpp
@@ -160,6 +160,13 @@ void DecCu::decompressCtu( CodingStructure& cs, const UnitArea& ctuArea )
 
     for( auto &currCU : cs.traverseCUs( CS::getArea( cs, ctuArea, chType ), chType ) )
     {
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+      m_pcInterPred->setDIMDForOBMC(false);
+      m_pcInterPred->setModeGetCheck(0, false);
+      m_pcInterPred->setModeGetCheck(1, false);
+      m_pcInterPred->setClearModeBuf(0);
+      m_pcInterPred->setClearModeBuf(1);
+#endif
 #if JVET_Z0054_BLK_REF_PIC_REORDER
       m_pcInterPred->setFillCurTplAboveARMC(false);
       m_pcInterPred->setFillCurTplLeftARMC(false);
@@ -2306,7 +2313,11 @@ void DecCu::xReconInter(CodingUnit &cu)
     const UnitArea localUnitArea( cu.cs->area.chromaFormat, Area( 0, 0, cu.Y().width, cu.Y().height ) );
 #if ENABLE_OBMC && JVET_X0090_CIIP_FIX
     cu.isobmcMC = true;
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+    m_pcInterPred->subBlockOBMC(*cu.firstPU, nullptr, m_pcIntraPred);
+#else
     m_pcInterPred->subBlockOBMC(*cu.firstPU);
+#endif
     cu.isobmcMC = false;
 #endif
 #if JVET_AG0135_AFFINE_CIIP
@@ -2369,7 +2380,11 @@ void DecCu::xReconInter(CodingUnit &cu)
   if (!cu.firstPU->ciipFlag)
 #endif
   {
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+    m_pcInterPred->subBlockOBMC(*cu.firstPU, nullptr, m_pcIntraPred);
+#else
     m_pcInterPred->subBlockOBMC(*cu.firstPU);
+#endif
   }
 #else
   m_pcInterPred->subBlockOBMC(*cu.firstPU);
diff --git a/source/Lib/EncoderLib/EncCu.cpp b/source/Lib/EncoderLib/EncCu.cpp
index 81a2b847f..01bdfccc3 100644
--- a/source/Lib/EncoderLib/EncCu.cpp
+++ b/source/Lib/EncoderLib/EncCu.cpp
@@ -1138,6 +1138,13 @@ void EncCu::xCompressCU( CodingStructure*& tempCS, CodingStructure*& bestCS, Par
   tempCS->splitRdCostBest = NULL;
 #endif
   m_modeCtrl->initCULevel( partitioner, *tempCS );
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+  m_pcInterSearch->setDIMDForOBMC(false);
+  m_pcInterSearch->setModeGetCheck(0, false);
+  m_pcInterSearch->setModeGetCheck(1, false);
+  m_pcInterSearch->setClearModeBuf(0);
+  m_pcInterSearch->setClearModeBuf(1);
+#endif
 #if JVET_Z0054_BLK_REF_PIC_REORDER
   m_pcInterSearch->setFillCurTplAboveARMC(false);
   m_pcInterSearch->setFillCurTplLeftARMC(false);
@@ -4750,7 +4757,11 @@ void EncCu::xCheckRDCostHashInter( CodingStructure *&tempCS, CodingStructure *&b
 #else
   cu.obmcFlag = true;
 #endif
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+  m_pcInterSearch->subBlockOBMC(*cu.firstPU, nullptr, m_pcIntraSearch);
+#else
   m_pcInterSearch->subBlockOBMC(*cu.firstPU);
+#endif
   cu.isobmcMC = false;
 #endif
     xEncodeInterResidual(tempCS, bestCS, partitioner, encTestMode, 0
@@ -9214,7 +9225,11 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
 #if JVET_AG0135_AFFINE_CIIP
           }
 #endif
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+          m_pcInterSearch->subBlockOBMC(pu, nullptr, m_pcIntraSearch);
+#else
           m_pcInterSearch->subBlockOBMC(pu);
+#endif
           cu.isobmcMC = false;
 #endif
 #if JVET_AG0135_AFFINE_CIIP
@@ -9758,7 +9773,11 @@ void EncCu::xCheckRDCostMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&
 #if JVET_X0090_CIIP_FIX
       if (!pu.ciipFlag)
       {
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+        m_pcInterSearch->subBlockOBMC(pu, nullptr, m_pcIntraSearch);
+#else
         m_pcInterSearch->subBlockOBMC(pu);
+#endif
       }
 #else
       m_pcInterSearch->subBlockOBMC( pu );
@@ -10261,7 +10280,11 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
     obmcBuf.copyFrom(predSrc);
     pu.cu->isobmcMC = true;
     pu.cu->obmcFlag = true;
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+    m_pcInterSearch->subBlockOBMC(pu, &obmcBuf, m_pcIntraSearch);
+#else
     m_pcInterSearch->subBlockOBMC(pu, &obmcBuf);
+#endif
     pu.cu->affine = false;
     pu.cu->isobmcMC = false;
   };
@@ -11695,7 +11718,11 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
 #endif
       cu.isobmcMC = true;
       cu.obmcFlag = true;
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+      m_pcInterSearch->subBlockOBMC(pu, !isIntra0 ? &predSrc0 : &predSrc1, m_pcIntraSearch);
+#else
       m_pcInterSearch->subBlockOBMC(pu, !isIntra0 ? &predSrc0 : &predSrc1);
+#endif
       cu.isobmcMC = false;
 #endif
 #if JVET_AG0164_AFFINE_GPM
@@ -11712,7 +11739,11 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
           PU::spanMotionInfo2(pu, MergeCtx());
           cu.isobmcMC = true;
           cu.obmcFlag = true;
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+          m_pcInterSearch->subBlockOBMC(pu, &predSrc0, m_pcIntraSearch);
+#else
           m_pcInterSearch->subBlockOBMC(pu, &predSrc0);
+#endif
           pu.cu->affine = false;
           pu.cu->isobmcMC = false;
 
@@ -11726,7 +11757,11 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
           PU::spanMotionInfo2(pu, MergeCtx());
           cu.isobmcMC = true;
           cu.obmcFlag = true;
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+          m_pcInterSearch->subBlockOBMC(pu, &predSrc1, m_pcIntraSearch);
+#else
           m_pcInterSearch->subBlockOBMC(pu, &predSrc1);
+#endif
           pu.cu->affine = false;
           pu.cu->isobmcMC = false;
 
@@ -12045,7 +12080,11 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
 #endif
       cu.isobmcMC = true;
       cu.obmcFlag = true;
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+      m_pcInterSearch->subBlockOBMC(pu, nullptr, m_pcIntraSearch);
+#else
       m_pcInterSearch->subBlockOBMC(pu);
+#endif
       cu.isobmcMC = false;
 #if JVET_Y0065_GPM_INTRA
       }
@@ -13937,7 +13976,11 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
 #endif
         cu.isobmcMC = true;
         cu.obmcFlag = true;
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+        m_pcInterSearch->subBlockOBMC(pu, !isIntra0 ? &predSrcTemp0 : &predSrcTemp1, m_pcIntraSearch);
+#else
         m_pcInterSearch->subBlockOBMC(pu, !isIntra0 ? &predSrcTemp0 : &predSrcTemp1);
+#endif
         cu.isobmcMC = false;
 #endif
 #if JVET_AG0164_AFFINE_GPM
@@ -13954,7 +13997,11 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
             PU::spanMotionInfo2(pu, MergeCtx());
             cu.isobmcMC = true;
             cu.obmcFlag = true;
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+            m_pcInterSearch->subBlockOBMC(pu, &predSrcTemp0, m_pcIntraSearch);
+#else
             m_pcInterSearch->subBlockOBMC(pu, &predSrcTemp0);
+#endif
             pu.cu->affine = false;
             cu.isobmcMC = false;
 
@@ -13968,7 +14015,11 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
             PU::spanMotionInfo2(pu, MergeCtx());
             cu.isobmcMC = true;
             cu.obmcFlag = true;
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+            m_pcInterSearch->subBlockOBMC(pu, &predSrcTemp1, m_pcIntraSearch);
+#else
             m_pcInterSearch->subBlockOBMC(pu, &predSrcTemp1);
+#endif
             pu.cu->affine = false;
             cu.isobmcMC = false;
           }
@@ -14537,7 +14588,11 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
 #endif
         cu.isobmcMC = true;
         cu.obmcFlag = true;
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+        m_pcInterSearch->subBlockOBMC(pu, nullptr, m_pcIntraSearch);
+#else
         m_pcInterSearch->subBlockOBMC(pu);
+#endif
         cu.isobmcMC = false;
 #if JVET_Y0065_GPM_INTRA
         }
@@ -14747,7 +14802,11 @@ void EncCu::xCheckRDCostMergeGeoComb2Nx2N(CodingStructure *&tempCS, CodingStruct
       {
         cu.isobmcMC = true;
         cu.obmcFlag = true;
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+        m_pcInterSearch->subBlockOBMC(pu, nullptr, m_pcIntraSearch);
+#else
         m_pcInterSearch->subBlockOBMC(pu);
+#endif
         cu.isobmcMC = false;
       }
 #endif
@@ -22074,7 +22133,11 @@ void EncCu::xCheckRDCostInter( CodingStructure *&tempCS, CodingStructure *&bestC
       CHECK(cu.obmcFlag == false, "this is not possible");
 #endif
       cu.isobmcMC = true;
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+      m_pcInterSearch->subBlockOBMC(*cu.firstPU, nullptr, m_pcIntraSearch);
+#else
       m_pcInterSearch->subBlockOBMC(*cu.firstPU);
+#endif
       cu.isobmcMC = false;
       xEncodeInterResidual( tempCS, bestCS, partitioner, encTestMode, 0
                             , 0
@@ -22191,7 +22254,11 @@ void EncCu::xCheckRDCostInter( CodingStructure *&tempCS, CodingStructure *&bestC
 #else
   cu.obmcFlag = true;
 #endif
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+  m_pcInterSearch->subBlockOBMC(*cu.firstPU, nullptr, m_pcIntraSearch);
+#else
   m_pcInterSearch->subBlockOBMC(*cu.firstPU);
+#endif
   cu.isobmcMC = false;
 #endif
   xEncodeInterResidual( tempCS, bestCS, partitioner, encTestMode, 0
@@ -22608,7 +22675,11 @@ bool EncCu::xCheckRDCostInterIMV(CodingStructure *&tempCS, CodingStructure *&bes
 #endif
       CodingStructure *prevCS = tempCS;
       cu.isobmcMC = true;
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+      m_pcInterSearch->subBlockOBMC(*cu.firstPU, nullptr, m_pcIntraSearch);
+#else
       m_pcInterSearch->subBlockOBMC(*cu.firstPU);
+#endif
       cu.isobmcMC = false;
       xEncodeInterResidual( tempCS, bestCS, partitioner, encTestMode, 0
                             , 0
@@ -22724,7 +22795,11 @@ bool EncCu::xCheckRDCostInterIMV(CodingStructure *&tempCS, CodingStructure *&bes
 #else
   cu.obmcFlag = true;
 #endif
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+  m_pcInterSearch->subBlockOBMC(*cu.firstPU, nullptr, m_pcIntraSearch);
+#else
   m_pcInterSearch->subBlockOBMC(*cu.firstPU);
+#endif
   cu.isobmcMC = false;
 #endif
   xEncodeInterResidual( tempCS, bestCS, partitioner, encTestModeBase, 0
@@ -24730,7 +24805,11 @@ void EncCu::xCheckRDCostInterMultiHyp2Nx2N(CodingStructure *&tempCS, CodingStruc
 #if JVET_AD0193_ADAPTIVE_OBMC_CONTROL
     }
 #endif
+#if JVET_AJ0161_OBMC_EXT_WITH_INTRA_PRED
+    m_pcInterSearch->subBlockOBMC(*cu.firstPU, nullptr, m_pcIntraSearch);
+#else
     m_pcInterSearch->subBlockOBMC(*cu.firstPU);
+#endif
     cu.isobmcMC = false;
 #endif
     xEncodeInterResidual(tempCS, bestCS, partitioner, encTestMode);
-- 
GitLab