diff --git a/cfg/per-class/classF.cfg b/cfg/per-class/classF.cfg
index b9fbf3fb3dbcbeb349be11d2651488fdb0af669e..7a61bc17ba0823f34f033cf139accdb2ae89bd1b 100644
--- a/cfg/per-class/classF.cfg
+++ b/cfg/per-class/classF.cfg
@@ -16,3 +16,4 @@ IBCGPM:          1
 BvpCluster:      1
 IBCBiPred:       0
 IBCNonAdjCand:      1
+IBCFilter:       1
\ No newline at end of file
diff --git a/source/App/EncoderApp/EncApp.cpp b/source/App/EncoderApp/EncApp.cpp
index 734a22232eaf15b721e99c034231b5f42596d088..421aa470dfbc5b66364e2dd2c459cb5450211e09 100644
--- a/source/App/EncoderApp/EncApp.cpp
+++ b/source/App/EncoderApp/EncApp.cpp
@@ -964,6 +964,9 @@ void EncApp::xInitLibCfg()
 #if JVET_AC0112_IBC_LIC
   m_cEncLib.setIbcLic                                            ( m_ibcLic );
 #endif
+#if JVET_AE0159_FIBC
+  m_cEncLib.setIbcFilter                                         ( m_ibcFilter );
+#endif
 #if JVET_AE0169_BIPREDICTIVE_IBC
   m_cEncLib.setIbcBiPred                                         ( m_ibcBiPred );
 #endif
diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp
index 9a4def6bd16b2df244f6144b5835c7807117bf5f..c3b680eaf03daf5124e980ca3c77562c2bfc2732 100644
--- a/source/App/EncoderApp/EncAppCfg.cpp
+++ b/source/App/EncoderApp/EncAppCfg.cpp
@@ -1229,6 +1229,9 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
 #if JVET_AC0112_IBC_LIC
   ("IBCLIC",                                          m_ibcLic,                                          true, "IBC LIC mode (0:off, 1:on)  [default: on]" )
 #endif
+#if JVET_AE0159_FIBC
+  ("IBCFilter",                                       m_ibcFilter,                                      false, "Filtered IBC mode (0:off, 1:on)  [default: off]" )
+#endif
 #if JVET_AE0169_BIPREDICTIVE_IBC
   ("IBCBiPred",                                       m_ibcBiPred,                                       true, "IBC BiPred mode in I-Slice (0:off, 1:on)  [default: on]" )
 #endif
@@ -5464,6 +5467,9 @@ void EncAppCfg::xPrintParameter()
 #if JVET_AC0112_IBC_LIC
   msg( VERBOSE, "IBCLIC:%d ", m_ibcLic );
 #endif
+#if JVET_AE0159_FIBC
+  msg( VERBOSE, "IBCFilter:%d ", m_ibcFilter );
+#endif
 #if JVET_AE0169_BIPREDICTIVE_IBC
   msg( VERBOSE, "IBCBiPred:%d ", m_ibcBiPred );
 #endif
diff --git a/source/App/EncoderApp/EncAppCfg.h b/source/App/EncoderApp/EncAppCfg.h
index d8dc49c69c9abf5f19156e5d1f57230cbfaf9503..5ca908e396fe778768e911622cabe822abce5bbb 100644
--- a/source/App/EncoderApp/EncAppCfg.h
+++ b/source/App/EncoderApp/EncAppCfg.h
@@ -542,6 +542,9 @@ protected:
 #if JVET_AC0112_IBC_LIC
   bool      m_ibcLic;
 #endif
+#if JVET_AE0159_FIBC
+  bool      m_ibcFilter;
+#endif
 #if JVET_AE0169_BIPREDICTIVE_IBC
   bool      m_ibcBiPred;
 #endif
diff --git a/source/Lib/CommonLib/CommonDef.h b/source/Lib/CommonLib/CommonDef.h
index e59bf940e969e3219e3c4527383054c72cee2cab..bdae9f2928e1124ebf6c7052048b08a5c3d13d80 100644
--- a/source/Lib/CommonLib/CommonDef.h
+++ b/source/Lib/CommonLib/CommonDef.h
@@ -1154,7 +1154,11 @@ static const int INTER_CCCM_MAX_REF_SAMPLES = 256;
 static const int NUM_DBV_POSITION = 5;
 static const int DBV_TEMPLATE_SIZE = 1;
 #endif
-
+#if JVET_AE0159_FIBC
+static const int FIBC_TEMPLATE_SIZE = 4; 
+static const int FIBC_PADDING       = 1;
+static const int FIBC_PARAMS        = 7;
+#endif
 #if JVET_Y0152_TT_ENC_SPEEDUP
 static constexpr int FAST_METHOD_TT_ENC_SPEEDUP =              0x0001;  ///< Embedding flag, which, if false, de-activates all the following ABT_ENC_SPEEDUP_* modes
 static constexpr int FAST_METHOD_HOR_XOR_VER =                 0x0002;
diff --git a/source/Lib/CommonLib/ContextModelling.cpp b/source/Lib/CommonLib/ContextModelling.cpp
index cf3e26ad1922c3912c26fb237f0e37fb4bdd01e2..888508c23280f273ab6f7a0f8c83a7f88ecc9697 100644
--- a/source/Lib/CommonLib/ContextModelling.cpp
+++ b/source/Lib/CommonLib/ContextModelling.cpp
@@ -620,6 +620,9 @@ void MergeCtx::setMergeInfo( PredictionUnit& pu, int candIdx )
 #if JVET_AC0112_IBC_LIC
     pu.cu->ibcLicFlag = ibcLicFlags[candIdx];
 #endif
+#if JVET_AE0159_FIBC
+    pu.cu->ibcFilterFlag = ibcFilterFlags[candIdx];
+#endif
 #if JVET_AA0070_RRIBC
     pu.cu->rribcFlipType = rribcFlipTypes[candIdx];
   }
@@ -684,6 +687,9 @@ void MergeCtx::setIbcL1Info( PredictionUnit& pu, int candIdx )
   pu.refIdx[REF_PIC_LIST_1] = MAX_NUM_REF;
   pu.cu->ibcLicFlag = 0;
   pu.cu->rribcFlipType = 0;
+#if JVET_AE0159_FIBC
+  pu.cu->ibcFilterFlag = false;
+#endif
 }
 #endif
 
@@ -831,6 +837,9 @@ void MergeCtx::initMrgCand(int cnt)
 #if JVET_AC0112_IBC_LIC
   ibcLicFlags[cnt] = false;
 #endif
+#if JVET_AE0159_FIBC
+  ibcFilterFlags[cnt] = false;
+#endif
 #if JVET_AA0070_RRIBC
   rribcFlipTypes[cnt] = 0;
 #endif
@@ -1774,6 +1783,9 @@ bool MergeCtx::setIbcMbvdMergeCandiInfo(PredictionUnit& pu, int candIdx, int can
 #if JVET_AC0112_IBC_LIC
   pu.cu->ibcLicFlag = (pu.interDir == 3) ? false : ibcLicFlags[fPosBaseIdx];
 #endif
+#if JVET_AE0159_FIBC
+  pu.cu->ibcFilterFlag = (pu.interDir == 3) ? false : ibcFilterFlags[fPosBaseIdx];
+#endif
 #if JVET_AA0070_RRIBC
   pu.cu->rribcFlipType = (pu.interDir == 3) ? 0 : rribcFlipTypes[fPosBaseIdx];
 #endif
@@ -1781,6 +1793,9 @@ bool MergeCtx::setIbcMbvdMergeCandiInfo(PredictionUnit& pu, int candIdx, int can
 #if JVET_AC0112_IBC_LIC
   pu.cu->ibcLicFlag = ibcLicFlags[fPosBaseIdx];
 #endif
+#if JVET_AE0159_FIBC
+  pu.cu->ibcFilterFlag = ibcFilterFlags[fPosBaseIdx];
+#endif
 #if JVET_AA0070_RRIBC
   pu.cu->rribcFlipType = rribcFlipTypes[fPosBaseIdx];
 #endif
@@ -1855,3 +1870,18 @@ unsigned DeriveCtx::CtxPltCopyFlag( const unsigned prevRunType, const unsigned d
     return ucCtxLut[RUN_IDX_THRE];
   }
 }
+
+#if JVET_AE0159_FIBC 
+unsigned DeriveCtx::ctxIbcFilterFlag(const CodingUnit& cu)
+{
+  const CodingStructure *cs = cu.cs;
+  unsigned ctxId = 0;
+  const Position pos = cu.chType == CHANNEL_TYPE_CHROMA ? cu.chromaPos() : cu.lumaPos();
+  const CodingUnit *cuLeft = cs->getCURestricted(pos.offset(-1, 0), cu, cu.chType);
+  ctxId += (cuLeft && !cuLeft->firstPU->mergeFlag && (cuLeft->ibcLicFlag || cuLeft->ibcFilterFlag)) ? 1 : 0;
+
+  const CodingUnit *cuAbove = cs->getCURestricted(pos.offset(0, -1), cu, cu.chType);
+  ctxId += (cuAbove && !cuAbove->firstPU->mergeFlag && (cuAbove->ibcLicFlag || cuAbove->ibcFilterFlag)) ? 1 : 0;
+  return ctxId;
+}
+#endif
\ No newline at end of file
diff --git a/source/Lib/CommonLib/ContextModelling.h b/source/Lib/CommonLib/ContextModelling.h
index 049fb897cb9bde099953de31030f2de463175c3e..b47833471ee7a43eb60ba57acab6e13d686b7685 100644
--- a/source/Lib/CommonLib/ContextModelling.h
+++ b/source/Lib/CommonLib/ContextModelling.h
@@ -564,6 +564,9 @@ public:
 #if JVET_AC0112_IBC_LIC
   bool          ibcLicFlags[NUM_MERGE_CANDS];
 #endif
+#if JVET_AE0159_FIBC
+  bool          ibcFilterFlags[NUM_MERGE_CANDS];
+#endif
 #if JVET_AA0070_RRIBC
   int           rribcFlipTypes[NUM_MERGE_CANDS];
 #endif
@@ -584,6 +587,9 @@ public:
 #if JVET_AC0112_IBC_LIC
   bool          ibcLicFlags       [ MRG_MAX_NUM_CANDS      ];
 #endif
+#if JVET_AE0159_FIBC
+  bool          ibcFilterFlags    [ MRG_MAX_NUM_CANDS      ];
+#endif
 #if JVET_AA0070_RRIBC
   int rribcFlipTypes[MRG_MAX_NUM_CANDS];
 #endif
@@ -770,7 +776,9 @@ int ctxSmMvdBin(const int iPreviousBinIsCorrect2, const int iPreviousBinIsCorrec
 #if JVET_AC0104_IBC_BVD_PREDICTION
 int CtxSmBvdBin(const int iPreviousBinIsCorrect2, const int iPreviousBinIsCorrect, const int isHor, const int significance);
 #endif
-
+#if JVET_AE0159_FIBC
+unsigned ctxIbcFilterFlag(const CodingUnit& cu);
+#endif
 }
 
 #endif // __CONTEXTMODELLING__
diff --git a/source/Lib/CommonLib/Contexts.cpp b/source/Lib/CommonLib/Contexts.cpp
index c2dbf87d9444a63c2defb73ddda001bdd29883c2..3a2c4b845f7d91fe268d627f5e47b38170464e63 100644
--- a/source/Lib/CommonLib/Contexts.cpp
+++ b/source/Lib/CommonLib/Contexts.cpp
@@ -3218,6 +3218,19 @@ const CtxSet ContextSetCfg::IbcGpmBldIdx = ContextSetCfg::addCtxSet
 #if JVET_AC0112_IBC_LIC
 const CtxSet ContextSetCfg::IbcLicFlag = ContextSetCfg::addCtxSet
 ({
+#if JVET_AE0159_FIBC
+  { CNU, CNU, CNU, CNU, },
+  { CNU, CNU, CNU, CNU, },
+  {  27,  25,  26,  12, },
+  { DWS, DWS, DWS, DWS, },
+  { DWS, DWS, DWS, DWS, },
+  {   1,   6,   2,   0, },
+  { DWE, DWE, DWE, DWE, },
+  { DWE, DWE, DWE, DWE, },
+  {  32,  32,  32,  32, },
+  { DWO, DWO, DWO, DWO, },
+  { DWO, DWO, DWO, DWO, },
+#else
   { CNU, },
   { CNU, },
   { CNU, },
@@ -3229,6 +3242,7 @@ const CtxSet ContextSetCfg::IbcLicFlag = ContextSetCfg::addCtxSet
   { DWE, },
   { DWO, },
   { DWO, },
+#endif
   });
 #endif
 
diff --git a/source/Lib/CommonLib/InterPrediction.cpp b/source/Lib/CommonLib/InterPrediction.cpp
index 5339df7a691be9db61cbbb2527f466641174145a..2026f527c1ee785f22defeed7d594e13e7b4ee76 100644
--- a/source/Lib/CommonLib/InterPrediction.cpp
+++ b/source/Lib/CommonLib/InterPrediction.cpp
@@ -319,6 +319,13 @@ InterPrediction::InterPrediction()
     }
   }
 #endif
+#if JVET_AE0159_FIBC
+  m_ibcRefBuf = nullptr;
+  m_a = nullptr;
+  m_y = nullptr;
+  m_samples = nullptr;
+  m_pcIntraPred     = nullptr;
+#endif
 }
 
 InterPrediction::~InterPrediction()
@@ -504,6 +511,13 @@ void InterPrediction::destroy()
     }
   }
 #endif
+#if JVET_AE0159_FIBC
+  m_a = nullptr;
+  m_y = nullptr;
+  m_samples = nullptr;
+  m_pcIntraPred     = nullptr;
+  m_ibcRefBuf   = nullptr;
+#endif
 #if JVET_AE0059_INTER_CCCM
   if (m_interCccm)
   {
@@ -13192,6 +13206,9 @@ void  InterPrediction::updateIBCCandInfo(PredictionUnit &pu, MergeCtx& mrgCtx, u
 #if JVET_AC0112_IBC_LIC
     mrgCtxTmp.ibcLicFlags[ui] = false;
 #endif
+#if JVET_AE0159_FIBC
+    mrgCtxTmp.ibcFilterFlags[ui] = false;
+#endif
 #if JVET_AA0070_RRIBC
     mrgCtxTmp.rribcFlipTypes[ui] = 0;
 #endif
@@ -13220,6 +13237,9 @@ void  InterPrediction::updateIBCCandInfo(PredictionUnit &pu, MergeCtx& mrgCtx, u
 #if JVET_AC0112_IBC_LIC
     mrgCtxTmp.ibcLicFlags[uiMergeCand] = mrgCtx.ibcLicFlags[uiMergeCand];
 #endif
+#if JVET_AE0159_FIBC
+    mrgCtxTmp.ibcFilterFlags[uiMergeCand] = mrgCtx.ibcFilterFlags[uiMergeCand];
+#endif
 #if JVET_AA0070_RRIBC
     mrgCtxTmp.rribcFlipTypes[uiMergeCand] = mrgCtx.rribcFlipTypes[uiMergeCand];
 #endif
@@ -13249,6 +13269,9 @@ void  InterPrediction::updateIBCCandInfo(PredictionUnit &pu, MergeCtx& mrgCtx, u
 #if JVET_AC0112_IBC_LIC
     mrgCtx.ibcLicFlags[uiMergeCand] = mrgCtxTmp.ibcLicFlags[RdCandList[uiMergeCand / ADAPTIVE_IBC_SUB_GROUP_SIZE][uiMergeCand%ADAPTIVE_IBC_SUB_GROUP_SIZE]];
 #endif
+#if JVET_AE0159_FIBC
+    mrgCtx.ibcFilterFlags[uiMergeCand] = mrgCtxTmp.ibcFilterFlags[RdCandList[uiMergeCand / ADAPTIVE_IBC_SUB_GROUP_SIZE][uiMergeCand%ADAPTIVE_IBC_SUB_GROUP_SIZE]];
+#endif
 #if JVET_AA0070_RRIBC
     mrgCtx.rribcFlipTypes[uiMergeCand] = mrgCtxTmp.rribcFlipTypes[RdCandList[uiMergeCand / ADAPTIVE_IBC_SUB_GROUP_SIZE][uiMergeCand % ADAPTIVE_IBC_SUB_GROUP_SIZE]];
 #endif
@@ -13529,6 +13552,9 @@ void  InterPrediction::updateIBCCandInfo( PredictionUnit &pu, MergeCtx& mrgCtx,
 #if JVET_AC0112_IBC_LIC
     mrgCtxTmp.ibcLicFlags[ui] = false;
 #endif
+#if JVET_AE0159_FIBC
+    mrgCtxTmp.ibcFilterFlags[ui] = false;
+#endif
 #if JVET_AA0070_RRIBC
     mrgCtxTmp.rribcFlipTypes[ui] = 0;
 #endif
@@ -13551,6 +13577,9 @@ void  InterPrediction::updateIBCCandInfo( PredictionUnit &pu, MergeCtx& mrgCtx,
 #if JVET_AC0112_IBC_LIC
     mrgCtxTmp.ibcLicFlags[uiMergeCand] = mrgCtx.ibcLicFlags[uiMergeCand];
 #endif
+#if JVET_AE0159_FIBC
+    mrgCtxTmp.ibcFilterFlags[uiMergeCand] = mrgCtx.ibcFilterFlags[uiMergeCand];
+#endif
 #if JVET_AA0070_RRIBC
     mrgCtxTmp.rribcFlipTypes[uiMergeCand] = mrgCtx.rribcFlipTypes[uiMergeCand];
 #endif
@@ -13574,6 +13603,9 @@ void  InterPrediction::updateIBCCandInfo( PredictionUnit &pu, MergeCtx& mrgCtx,
 #if JVET_AC0112_IBC_LIC
     mrgCtx.ibcLicFlags[uiMergeCand] = mrgCtxTmp.ibcLicFlags[RdCandList[uiMergeCand - startPos]];
 #endif
+#if JVET_AE0159_FIBC
+    mrgCtx.ibcFilterFlags[uiMergeCand] = mrgCtxTmp.ibcFilterFlags[RdCandList[uiMergeCand - startPos]];
+#endif
 #if JVET_AA0070_RRIBC
     mrgCtx.rribcFlipTypes[uiMergeCand] = mrgCtxTmp.rribcFlipTypes[RdCandList[uiMergeCand - startPos]];
 #endif
@@ -15553,6 +15585,391 @@ void InterPrediction::xGetLICParamGeneral(const CodingUnit& cu,
 }
 #endif
 
+#if JVET_AE0159_FIBC
+void InterPrediction::setIbcFilterBuffers(Pel (*a)[CCCM_REF_SAMPLES_MAX], Pel* y, Pel* samples, IntraPrediction* pcIntra)
+{
+  m_pcIntraPred     = pcIntra;
+  m_ibcRefBuf       = pcIntra->getCccmBufferLuma();
+  m_a = a;
+  m_y = y;
+  m_samples = samples;
+}
+void InterPrediction::xCalIbcFilterParam(PelBuf& piPred, CodingUnit* cu, const ComponentID compID, const Mv& mv, unsigned int uiBlkWidth, unsigned int uiBlkHeight) 
+{
+  CompArea area = cu->blocks[compID];
+  xGetIbcFilterRefBuf(piPred, cu, compID, mv, uiBlkWidth, uiBlkHeight); 
+  int areaWidth, areaHeight, refSizeX, refSizeY;
+  refSizeX = m_ibcRefArea.x;
+  refSizeY = m_ibcRefArea.y;
+  areaWidth = m_ibcRefArea.width;
+  areaHeight = m_ibcRefArea.height;
+  int refStride = areaWidth + 2 * FIBC_PADDING; // Including paddings required for the 2D filter
+  int refOrigin = refStride * FIBC_PADDING + FIBC_PADDING;
+  PelBuf tmpRefBuf = PelBuf(m_ibcRefBuf + refOrigin, refStride, areaWidth, areaHeight);
+
+  CccmModel ibcflmModel(FIBC_PARAMS , cu->cs->sps->getBitDepth(toChannelType(compID)));
+
+  if ((refSizeX == 0) && (refSizeY == 0)) // Number of samples can go to zero in the multimode case
+  {
+    ibcflmModel.clearModel();
+    for (int i = 0; i < FIBC_PARAMS; i++)
+    {
+      cu->ibcFilterParams[i] = ibcflmModel.params[i];
+    }
+    return;
+  }
+
+  int sampleNum = 0;
+
+  Pel* ref = cu->cs->picture->getRecoBuf(area).buf; // cur Template
+  int picStride = cu->cs->picture->getRecoBuf(area).stride;
+
+  ref = ref - refSizeY * picStride - refSizeX;
+
+#if JVET_AB0174_CCCM_DIV_FREE
+  int offset = 1 << (cu->slice->getSPS()->getBitDepth(toChannelType(compID)) - 1);
+  if (refSizeX || refSizeY)
+  {
+    int refPosX = refSizeX > 0 ? refSizeX - 1 : 0;
+    int refPosY = refSizeY > 0 ? refSizeY - 1 : 0;
+    Pel* refOffset = ref + refPosY * picStride + refPosX;
+    offset = refOffset[0];
+  }
+#endif
+  // Collect reference data to input matrix m_a and target vector m_y
+  for (int y = 0; y < areaHeight; y++)
+  {
+    for (int x = 0; x < areaWidth; x++)
+    {
+      if (x >= refSizeX && y >= refSizeY)
+      {
+        continue;
+      }
+      // 7-tap cross
+      m_a[0][sampleNum] = tmpRefBuf.at(x, y); // C
+      m_a[1][sampleNum] = tmpRefBuf.at(x, y - 1); // N
+      m_a[2][sampleNum] = tmpRefBuf.at(x, y + 1); // S
+      m_a[3][sampleNum] = tmpRefBuf.at(x - 1, y); // W
+      m_a[4][sampleNum] = tmpRefBuf.at(x + 1, y); // E
+      m_a[5][sampleNum] = ibcflmModel.nonlinear(tmpRefBuf.at(x, y));
+      m_a[6][sampleNum] = ibcflmModel.bias();
+
+      m_y[sampleNum++] = ref[x];
+    }
+    ref += picStride;
+  }
+
+  if (!sampleNum) // Number of samples can go to zero in the multimode case
+  {
+    ibcflmModel.clearModel();
+  }
+  else
+  {
+    m_pcIntraPred->getCccmBufferSolver()->solve1(m_a, m_y, sampleNum, offset, ibcflmModel);
+  }
+
+  for (int i = 0; i < FIBC_PARAMS; i++)
+  {
+    cu->ibcFilterParams[i] = ibcflmModel.params[i];
+  }
+}
+
+
+void InterPrediction::xGetIbcFilterRefBuf(PelBuf& piPred, CodingUnit* cu, const ComponentID compID, const Mv& bv, unsigned int uiBlkWidth, unsigned int uiBlkHeight) 
+{
+  const int shiftSampleHor = ::getComponentScaleX(compID, cu->chromaFormat);
+  const int shiftSampleVer = ::getComponentScaleY(compID, cu->chromaFormat);
+  int pX = bv.getHor() >> (MV_FRACTIONAL_BITS_INTERNAL + shiftSampleHor);
+  int pY = bv.getVer() >> (MV_FRACTIONAL_BITS_INTERNAL + shiftSampleVer);
+
+  int iOffsetY, iOffsetX;
+  Pel* refTarget;
+
+  int uiHeight = uiBlkHeight;
+  int uiWidth = uiBlkWidth;
+
+  Pel* ref = cu->cs->picture->getRecoBuf(cu->cs->picture->blocks[compID]).buf;
+  int picStride = cu->cs->picture->getRecoBuf(cu->cs->picture->blocks[compID]).stride;
+
+  iOffsetY = pY;
+  iOffsetX = pX;
+  refTarget = ref + (cu->blocks[compID].pos().y +iOffsetY) * picStride + (cu->blocks[compID].pos().x + iOffsetX); 
+
+  int areaWidth = uiWidth + FIBC_TEMPLATE_SIZE;
+  int areaHeight = uiHeight + FIBC_TEMPLATE_SIZE;
+  int refSizeX = 0;
+  int refSizeY = 0;
+
+  Pel* refTemp = nullptr;
+  bool paddingTop = true, paddingLeft = true, paddingRight = true, paddingBottom = true;
+
+  const CodingUnit* const cuAbove = cu->cs->getCU(cu->blocks[compID].pos().offset(0, -FIBC_TEMPLATE_SIZE), toChannelType(compID));
+  const CodingUnit* const cuLeft = cu->cs->getCU(cu->blocks[compID].pos().offset(-FIBC_TEMPLATE_SIZE, 0), toChannelType(compID));
+  bool refBvTopValid = false;
+  bool refBvLeftValid = false;
+  bool refBvTopRightValid = false;
+  bool refBvLeftBottomValid = false;
+  int  filterIdx = 1;
+  // above
+  if (cuAbove)
+  {
+    Mv mvTop(0, -(FIBC_TEMPLATE_SIZE << (MV_FRACTIONAL_BITS_INTERNAL + shiftSampleVer)));
+    mvTop += bv;
+    MotionInfo miTop;
+    miTop.mv[0] = mvTop;
+    miTop.refIdx[0] = MAX_NUM_REF;
+    
+    if (PU::checkIsIBCFilterCandidateValid(*cu->firstPU, miTop, filterIdx, true, true))
+    {
+      refBvTopValid = true;
+    }
+    if (refBvTopValid)
+    {
+      Mv mvTop1(0, -((1 + shiftSampleVer) << MV_FRACTIONAL_BITS_INTERNAL));
+      mvTop1 += mvTop;
+      MotionInfo miTop1;
+      miTop1.mv[0] = mvTop1;
+      miTop1.refIdx[0] = MAX_NUM_REF;
+
+      if (PU::checkIsIBCFilterCandidateValid(*cu->firstPU, miTop1, filterIdx, true, true))
+      {
+        paddingTop = false;
+      }
+      //TopRight
+      Mv mvTopRight(((1 + shiftSampleVer) << MV_FRACTIONAL_BITS_INTERNAL), 0);
+      mvTopRight += mvTop;
+      MotionInfo miTopRight;
+      miTopRight.mv[0] = mvTopRight;
+      miTopRight.refIdx[0] = MAX_NUM_REF;
+      if (PU::checkIsIBCFilterCandidateValid(*cu->firstPU, miTopRight, filterIdx, true, true))
+      {
+        refBvTopRightValid = true;
+      }
+    }
+  }
+  
+  //Left
+  if (cuLeft)
+  {
+    Mv mvLeft(-(FIBC_TEMPLATE_SIZE  << (MV_FRACTIONAL_BITS_INTERNAL + shiftSampleHor)), 0);
+    mvLeft += bv;
+    MotionInfo miLeft;
+    miLeft.mv[0] = mvLeft;
+    miLeft.refIdx[0] = MAX_NUM_REF;
+    if (PU::checkIsIBCFilterCandidateValid(*cu->firstPU, miLeft, filterIdx, true, false))
+    {
+      refBvLeftValid = true;
+    }
+    if (refBvLeftValid)
+    {
+      Mv mvLeft1(-((1 + shiftSampleHor) << MV_FRACTIONAL_BITS_INTERNAL), 0);
+      mvLeft1 += mvLeft;
+      MotionInfo miLeft1;
+      miLeft1.mv[0] = mvLeft1;
+      miLeft1.refIdx[0] = MAX_NUM_REF;
+      if (PU::checkIsIBCFilterCandidateValid(*cu->firstPU, miLeft1, filterIdx, true, false))
+      {
+        paddingLeft = false;
+      }
+      //Left-Bottom
+      Mv mvLeftBot(0, ((1 + shiftSampleHor) << MV_FRACTIONAL_BITS_INTERNAL));
+      mvLeftBot += mvLeft;
+      MotionInfo miLeftBot;
+      miLeftBot.mv[0] = mvLeftBot;
+      miLeftBot.refIdx[0] = MAX_NUM_REF;
+
+      if (PU::checkIsIBCFilterCandidateValid(*cu->firstPU, miLeftBot, filterIdx, true, true))
+      {
+        refBvLeftBottomValid = true;
+      }
+    }
+  }
+  //Right
+  Mv mvRight(((1 + shiftSampleHor) << MV_FRACTIONAL_BITS_INTERNAL), 0);
+  mvRight += bv;
+  MotionInfo miRight;
+  miRight.mv[0] = mvRight;
+  miRight.refIdx[0] = MAX_NUM_REF;
+  if (PU::checkIsIBCFilterCandidateValid(*cu->firstPU, miRight, filterIdx, false, false))
+  {
+    if ((!refBvTopValid) || refBvTopRightValid)
+    {
+      paddingRight = false;
+    }
+  }
+  //Bottom
+  Mv mvBot(0, ((1 + shiftSampleHor) << MV_FRACTIONAL_BITS_INTERNAL));
+  mvBot += bv;
+  MotionInfo miBot;
+  miBot.mv[0] = mvBot;
+  miBot.refIdx[0] = MAX_NUM_REF;
+
+  if (PU::checkIsIBCFilterCandidateValid(*cu->firstPU, miBot, filterIdx, false, true))
+  {
+    if ((!refBvLeftValid) || refBvLeftBottomValid)
+    {
+      paddingBottom = false;
+    }
+  }
+  
+  if ((bool)cuAbove && refBvTopValid && (bool)cuLeft && refBvLeftValid )
+  {
+    refSizeX = FIBC_TEMPLATE_SIZE;
+    refSizeY = FIBC_TEMPLATE_SIZE;
+    areaWidth = uiWidth + FIBC_TEMPLATE_SIZE;
+    areaHeight = uiHeight + FIBC_TEMPLATE_SIZE;
+    refTemp = refTarget - FIBC_TEMPLATE_SIZE * picStride - FIBC_TEMPLATE_SIZE;
+  }
+  else if ((bool)cuAbove && refBvTopValid)
+  {
+    refSizeX = 0;
+    refSizeY = FIBC_TEMPLATE_SIZE;
+    areaWidth = uiWidth;
+    areaHeight = uiHeight + FIBC_TEMPLATE_SIZE;
+    refTemp = refTarget - FIBC_TEMPLATE_SIZE * picStride;
+  }
+  else if ((bool)cuLeft && refBvLeftValid)
+  {
+    refSizeX = FIBC_TEMPLATE_SIZE;
+    refSizeY = 0;
+    areaWidth = uiWidth + FIBC_TEMPLATE_SIZE;
+    areaHeight = uiHeight;
+    refTemp = refTarget - FIBC_TEMPLATE_SIZE;
+  }
+  else
+  {
+    refSizeX = 0;
+    refSizeY = 0;
+    areaWidth = uiWidth;
+    areaHeight = uiHeight;
+    refTemp = refTarget;
+    m_ibcRefArea = Area(refSizeX, refSizeY, areaWidth, areaHeight);
+    return;
+  }
+  m_ibcRefArea = Area(refSizeX, refSizeY, areaWidth, areaHeight);
+
+  int refStride = areaWidth + 2 * FIBC_PADDING; // Including paddings required for the 2D filter
+  int refOrigin = refStride * FIBC_PADDING + FIBC_PADDING;
+
+  PelBuf tmpRefBuf = PelBuf(m_ibcRefBuf + refOrigin, refStride, areaWidth, areaHeight);
+  PelBuf srcRefBuf = PelBuf(refTemp, picStride, areaWidth, areaHeight);
+
+  bool isFracMv = cu->cs->sps->getIBCFracFlag() && (compID == COMPONENT_Y ? bv.isFracMv() : bv.isFracMv<false>(cu->chromaFormat));
+  if (isFracMv)
+  {
+    int filterIdx = 1;
+    Mv curMv;
+    curMv.set(bv.hor + (-refSizeX << MV_FRACTIONAL_BITS_INTERNAL), bv.ver + ((-refSizeY) << MV_FRACTIONAL_BITS_INTERNAL));
+    PelUnitBuf pcUnitBuf(cu->chromaFormat, tmpRefBuf, tmpRefBuf, tmpRefBuf);
+    getPredIBCBlk(*cu->firstPU, compID, cu->slice->getPic(), curMv, pcUnitBuf, filterIdx == 1, true);
+  }
+
+#if JVET_AB0174_CCCM_DIV_FREE
+  int offset = 1 << (cu->slice->getSPS()->getBitDepth(toChannelType(compID)) - 1);
+  if (refSizeX || refSizeY)
+  {
+    int refPosX = refSizeX > 0 ? refSizeX - 1 : 0;
+    int refPosY = refSizeY > 0 ? refSizeY - 1 : 0;
+    offset = srcRefBuf.at(refPosX, refPosY);
+  }
+#endif
+  for (int y = (paddingTop ? 0 : -1); y < (paddingBottom ? areaHeight : areaHeight + 1); y++)
+  {
+    for (int x = (paddingLeft ? 0 : -1); x < (paddingRight ? areaWidth : areaWidth + 1); x++)
+    {
+#if JVET_AB0174_CCCM_DIV_FREE
+      if ((x >= refSizeX) && (y >= refSizeY) && (x < (refSizeX + uiWidth)) && (y < (refSizeY + uiHeight)))
+      {
+        tmpRefBuf.at(x, y) = piPred.at(x - refSizeX, y - refSizeY) - offset;
+      }
+      else
+      {
+        if (isFracMv && (x >= 0) && (y >= 0) && (x < areaWidth) && (y < areaHeight))
+        {
+          tmpRefBuf.at(x, y) = tmpRefBuf.at(x, y) - offset;
+        }
+        else
+        {
+          tmpRefBuf.at(x, y) = srcRefBuf.at(x, y) - offset;
+        }
+      }
+#else
+      tmpRefBuf.at(x, y) = srcRefBuf.at(x, y);
+#endif
+    }
+
+  }
+
+
+  // Pad top area
+  if (paddingTop)
+  {
+    for (int x = (paddingLeft ? 0 : -1); x < (paddingRight ? areaWidth : areaWidth + 1); x++)
+    {
+      tmpRefBuf.at(x, -1) = tmpRefBuf.at(x, 0);
+    }
+  }
+  // Pad bottom area
+  if (paddingBottom)
+  {
+    for (int x = (paddingLeft ? 0 : -1); x < (paddingRight ? areaWidth : areaWidth + 1); x++)
+    {
+      tmpRefBuf.at(x, areaHeight) = tmpRefBuf.at(x, areaHeight - 1);
+    }
+  }
+
+  // Pad right area
+  if (paddingRight)
+  {
+    for (int y = -1; y <= areaHeight; y++)
+    {
+      tmpRefBuf.at(areaWidth, y) = tmpRefBuf.at(areaWidth - 1, y);
+    }
+  }
+  // Pad left area
+  if (paddingLeft)
+  {
+    for (int y = -1; y <= areaHeight; y++)
+    {
+      tmpRefBuf.at(-1, y) = tmpRefBuf.at(0, y);
+    }
+  }
+}
+void InterPrediction::xGenerateIbcFilterPred(PelBuf& piPred, unsigned int uiBlkWidth, unsigned int uiBlkHeight, const ComponentID compID, CodingUnit* cu)
+{
+  if ((m_ibcRefArea.x == 0) && (m_ibcRefArea.y == 0)) // Number of samples can go to zero in the multimode case
+  {
+    return;
+  }
+  const  ClpRng& clpRng(cu->cs->slice->clpRng(compID));
+  CccmModel ibcflmModel( FIBC_PARAMS, cu->cs->sps->getBitDepth(toChannelType(compID)));
+  memcpy(&ibcflmModel.params[0], &cu->ibcFilterParams[0], FIBC_PARAMS * sizeof(int64_t));
+
+  // Get Luma Buffer
+  int refStride = m_ibcRefArea.width + 2 * FIBC_PADDING; // Including paddings required for the 2D filter
+  int refOrigin = refStride * (m_ibcRefArea.y + FIBC_PADDING) + m_ibcRefArea.x + FIBC_PADDING;
+  PelBuf tmpRefBuf = PelBuf(m_ibcRefBuf + refOrigin, refStride, uiBlkWidth, uiBlkHeight);
+
+  for (int y = 0; y < tmpRefBuf.height; y++)
+  {
+    for (int x = 0; x < tmpRefBuf.width; x++)
+    {
+      // 7-tap cross
+      m_samples[0] = tmpRefBuf.at(x, y); // C
+      m_samples[1] = tmpRefBuf.at(x, y - 1); // N
+      m_samples[2] = tmpRefBuf.at(x, y + 1); // S
+      m_samples[3] = tmpRefBuf.at(x - 1, y); // W
+      m_samples[4] = tmpRefBuf.at(x + 1, y); // E
+      m_samples[5] = ibcflmModel.nonlinear(tmpRefBuf.at(x, y));
+      m_samples[6] = ibcflmModel.bias();
+
+      piPred.at(x, y) = ClipPel<Pel>(ibcflmModel.convolve(m_samples), clpRng);
+    }
+  }
+  return;
+}
+#endif
+
 #if INTER_LIC
 template <bool trueAfalseL>
 void InterPrediction::xGetPredBlkTpl(const CodingUnit& cu, const ComponentID compID, const CPelBuf& refBuf, const Mv& mv, const int posW, const int posH, const int tplSize, Pel* predBlkTpl
@@ -15643,6 +16060,35 @@ void InterPrediction::xLocalIlluComp(const PredictionUnit& pu,
                                      PelBuf&               dstBuf
 )
 {
+#if JVET_AE0159_FIBC
+  if ((pu.cu->ibcLicFlag) && (!pu.cu->ibcFilterFlag))
+  {
+#if JVET_AD0213_LIC_IMP
+    Pel* refLeftTemplate = m_pcLICRefLeftTemplate[0][compID];
+    Pel* refAboveTemplate = m_pcLICRefAboveTemplate[0][compID];
+    Pel* recLeftTemplate = m_pcIBCLICRecLeftTemplate[compID];
+    Pel* recAboveTemplate = m_pcIBCLICRecAboveTemplate[compID];
+#else
+    Pel* refLeftTemplate  = m_pcLICRefLeftTemplate;
+    Pel* refAboveTemplate = m_pcLICRefAboveTemplate;
+    Pel* recLeftTemplate  = m_pcLICRecLeftTemplate;
+    Pel* recAboveTemplate = m_pcLICRecAboveTemplate;
+#endif
+    int numTemplate[2] = { 0 , 0 }; // 0:Above, 1:Left
+    xGetSublkTemplate(*pu.cu, compID, bv, pu.blocks[compID].width, pu.blocks[compID].height, 0, 0, numTemplate, refLeftTemplate, refAboveTemplate, recLeftTemplate, recAboveTemplate);
+
+    int shift = 0, scale = 0, offset = 0;
+    xGetLICParamGeneral(*pu.cu, compID, numTemplate, refLeftTemplate, refAboveTemplate, recLeftTemplate, recAboveTemplate, shift, scale, offset);
+
+    const ClpRng& clpRng = pu.cu->cs->slice->clpRng(compID);
+    dstBuf.linearTransform(scale, shift, offset, true, clpRng);
+  }
+  else if ((pu.cu->ibcLicFlag) && (pu.cu->ibcFilterFlag ))
+  {
+    xCalIbcFilterParam(dstBuf, pu.cu, compID, bv, pu.blocks[compID].width, pu.blocks[compID].height ); 
+    xGenerateIbcFilterPred(dstBuf, pu.blocks[compID].width, pu.blocks[compID].height, compID, pu.cu);
+  }
+#else
 #if JVET_AD0213_LIC_IMP
   Pel* refLeftTemplate = m_pcLICRefLeftTemplate[0][compID];
   Pel* refAboveTemplate = m_pcLICRefAboveTemplate[0][compID];
@@ -15662,6 +16108,7 @@ void InterPrediction::xLocalIlluComp(const PredictionUnit& pu,
 
   const ClpRng& clpRng = pu.cu->cs->slice->clpRng(compID);
   dstBuf.linearTransform(scale, shift, offset, true, clpRng);
+#endif
 }
 
 void InterPrediction::xGetSublkTemplate(const CodingUnit& cu,
@@ -30122,4 +30569,4 @@ inline int InterCccm::computeOffset(const PelBuf &buf)
   sum += buf.at(0,buf.height-1);
   return ((sum + 2) >> 2);
 }
-#endif
\ No newline at end of file
+#endif
diff --git a/source/Lib/CommonLib/InterPrediction.h b/source/Lib/CommonLib/InterPrediction.h
index 791595fe122421b81807554aa1a8ec2187c2a225..982266d33afc9e9c903624ccfbc82024a824f9a0 100644
--- a/source/Lib/CommonLib/InterPrediction.h
+++ b/source/Lib/CommonLib/InterPrediction.h
@@ -319,6 +319,15 @@ protected:
   Pel m_acYuvRefAMLBiPredTemplateIdMotionCache[MAX_NUM_REFIDX][NUM_REF_PIC_LIST_01][MAX_NUM_CANDS][2][MAX_CU_SIZE];
 #endif
 
+#if JVET_AE0159_FIBC
+  IntraPrediction*  m_pcIntraPred;
+  Area m_ibcRefArea;
+  Pel *m_ibcRefBuf;
+  Pel *m_y;
+  Pel *m_samples;
+  Pel (*m_a)[CCCM_REF_SAMPLES_MAX];
+#endif
+
   void xIntraBlockCopy          (PredictionUnit &pu, PelUnitBuf &predBuf, const ComponentID compID);
   int             rightShiftMSB(int numer, int    denom);
 #if MULTI_PASS_DMVR
@@ -1044,6 +1053,12 @@ public:
 #if INTER_LIC || JVET_AC0112_IBC_LIC
   void xGetLICParamGeneral (const CodingUnit& cu, const ComponentID compID, int* numTemplate, Pel* refLeftTemplate, Pel* refAboveTemplate, Pel* recLeftTemplate, Pel* recAboveTemplate, int& shift, int& scale, int& offset);
 #endif
+#if JVET_AE0159_FIBC
+  void xGetIbcFilterRefBuf(PelBuf& piPred, CodingUnit* pcCU, const ComponentID compID, const Mv& mv, unsigned int uiBlkWidth, unsigned int uiBlkHeight );
+  void xCalIbcFilterParam(PelBuf& piPred, CodingUnit* pcCU, const ComponentID compID, const Mv& mv, unsigned int uiBlkWidth, unsigned int uiBlkHeight ); 
+  void xGenerateIbcFilterPred(PelBuf& piPred, unsigned int uiBlkWidth, unsigned int uiBlkHeight, const ComponentID compID, CodingUnit* pcCU);
+  void setIbcFilterBuffers(Pel (*m_a_intra)[CCCM_REF_SAMPLES_MAX], Pel *m_cb_intra,Pel *m_samples_intra, IntraPrediction* pcIntra);
+#endif
 #if INTER_LIC
 #if JVET_AA0146_WRAP_AROUND_FIX
   void xGetSublkTemplate   (const CodingUnit& cu, const ComponentID compID, const Picture& refPic, const Mv& mv, const int sublkWidth, const int sublkHeight, const int posW, const int posH, int* numTemplate, Pel* refLeftTemplate, Pel* refAboveTemplate, Pel* recLeftTemplate, Pel* recAboveTemplate, bool wrapRef = false);
diff --git a/source/Lib/CommonLib/IntraPrediction.h b/source/Lib/CommonLib/IntraPrediction.h
index ee89f914b0a5646852be9c588d52199a4b5970fb..359e4f59a17676fa1346a97cc4ddf04351573909 100644
--- a/source/Lib/CommonLib/IntraPrediction.h
+++ b/source/Lib/CommonLib/IntraPrediction.h
@@ -382,12 +382,16 @@ protected:
   Pel m_a[CCCM_NUM_PARAMS_MAX][CCCM_REF_SAMPLES_MAX];
   Pel m_cb[CCCM_REF_SAMPLES_MAX];
   Pel m_cr[CCCM_REF_SAMPLES_MAX];
-#if JVET_AE0059_INTER_CCCM
+#if JVET_AE0059_INTER_CCCM || JVET_AE0159_FIBC
   public:
     Pel (*getCccmBufferA())[CCCM_REF_SAMPLES_MAX]  { return m_a; };
     Pel* getCccmBufferCb() { return m_cb; };
     Pel* getCccmBufferCr() { return m_cr; };
     Pel* getCccmBufferSamples() { return m_samples; };
+#if JVET_AE0159_FIBC
+    CccmCovariance* getCccmBufferSolver() { return &m_cccmSolver; };
+    Pel* getCccmBufferLuma() { return *m_cccmLumaBuf; };   
+#endif
   protected:
 #endif
 #endif
diff --git a/source/Lib/CommonLib/MotionInfo.h b/source/Lib/CommonLib/MotionInfo.h
index 42b3c10a3b60b2f49be8ac5e4ffd5abfac42e94c..01e3a7298c45a138e607401d534fac94334f3293 100644
--- a/source/Lib/CommonLib/MotionInfo.h
+++ b/source/Lib/CommonLib/MotionInfo.h
@@ -187,6 +187,9 @@ struct MotionInfo
 #if JVET_AC0112_IBC_LIC
   bool     useIbcLic;
 #endif
+#if JVET_AE0159_FIBC
+  bool      useIbcFilter;
+#endif
 #if JVET_AA0070_RRIBC
   int  rribcFlipType;
   Position centerPos;
diff --git a/source/Lib/CommonLib/Slice.cpp b/source/Lib/CommonLib/Slice.cpp
index 12fc9f92f2e55baad462b0a9502b432566be37b3..a92ef3022d9203f2d58f8c8ef3549463d876c91c 100644
--- a/source/Lib/CommonLib/Slice.cpp
+++ b/source/Lib/CommonLib/Slice.cpp
@@ -3643,6 +3643,9 @@ SPS::SPS()
 #if JVET_AC0112_IBC_LIC
   , m_ibcLic                  ( false )
 #endif
+#if JVET_AE0159_FIBC
+  , m_ibcFilter               ( false )
+#endif
 #if JVET_AE0094_IBC_NONADJACENT_SPATIAL_CANDIDATES
   , m_ibcNonAdjCand           ( false )
 #endif
diff --git a/source/Lib/CommonLib/Slice.h b/source/Lib/CommonLib/Slice.h
index 0748223487ebe64845c708c862efee4b711377e3..65039ea8836d20d2677d876541e1b7a9d48b1ba4 100644
--- a/source/Lib/CommonLib/Slice.h
+++ b/source/Lib/CommonLib/Slice.h
@@ -1554,6 +1554,9 @@ private:
 #if JVET_AC0112_IBC_LIC
   bool              m_ibcLic;
 #endif
+#if JVET_AE0159_FIBC
+  bool              m_ibcFilter;
+#endif
 #if JVET_AE0094_IBC_NONADJACENT_SPATIAL_CANDIDATES
   bool              m_ibcNonAdjCand;
 #endif
@@ -2156,6 +2159,10 @@ void                    setCCALFEnabledFlag( bool b )
   void                    setUseIbcLic(bool b)                                                            { m_ibcLic = b; }
   bool                    getUseIbcLic() const                                                            { return m_ibcLic; }
 #endif
+#if JVET_AE0159_FIBC
+  void                    setUseIbcFilter(bool b)                                                         { m_ibcFilter = b; }
+  bool                    getUseIbcFilter() const                                                         { return m_ibcFilter; }
+#endif
 #if JVET_AE0094_IBC_NONADJACENT_SPATIAL_CANDIDATES
   void                    setUseIbcNonAdjCand(bool b)                                                     { m_ibcNonAdjCand = b; }
   bool                    getUseIbcNonAdjCand() const                                                     { return m_ibcNonAdjCand; }
diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index 536625f3d17e44a6f84c4242d97454ed137bb5b7..7719e5081aa2c52255efe0653cbec92a1bb5694f 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -213,6 +213,7 @@
 #define JVET_AC0071_DBV                                   1 // JVET-AC0071: Direct block vector mode for chroma prediction
 #define JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS   1 // JVET-AD0208: IBC adaptation for camera-captured contents and IBC extension to fractional-pel BV
 #define JVET_AE0084_IBC_LIC_INHERITANCE                   1 // JVET-AE0084: Harmonization of IBC HMVP and IBC-LIC
+#define JVET_AE0159_FIBC                                  1 // JVET-AE0159: Filtered Intra Block Copy (FIBC)
 #define JVET_AE0169_GPM_IBC_IBC                           1 // JVET-AE0169: Bi-predictive IBC GPM
 #define JVET_AE0169_BIPREDICTIVE_IBC                      1 // JVET-AE0169: IBC BVP-merge and bi-predictive IBC merge
 
diff --git a/source/Lib/CommonLib/Unit.cpp b/source/Lib/CommonLib/Unit.cpp
index 59c195d5e09bc30f6b52c2ff58835a916fb0813e..49508044105e08bbf9d59d1b38e4d5110f237331 100644
--- a/source/Lib/CommonLib/Unit.cpp
+++ b/source/Lib/CommonLib/Unit.cpp
@@ -410,6 +410,13 @@ CodingUnit& CodingUnit::operator=( const CodingUnit& other )
 #if JVET_AC0112_IBC_LIC
   ibcLicFlag = other.ibcLicFlag;
 #endif
+#if JVET_AE0159_FIBC
+  ibcFilterFlag  = other.ibcFilterFlag;
+  if (slice->getSPS()->getUseIbcFilter())
+  {
+    memcpy(ibcFilterParams, other.ibcFilterParams, FIBC_PARAMS * sizeof(int64_t));
+  }
+#endif
 #if JVET_AA0070_RRIBC
   rribcFlipType = other.rribcFlipType;
 #endif
@@ -617,6 +624,13 @@ void CodingUnit::initData()
 #if JVET_AC0112_IBC_LIC
   ibcLicFlag = false;
 #endif
+#if JVET_AE0159_FIBC
+  for (int i = 0; i < FIBC_PARAMS; i++)
+  {
+    ibcFilterParams[i] = -1;
+  }
+  ibcFilterFlag = false;
+#endif
 #if JVET_AA0070_RRIBC
   rribcFlipType = 0;
 #endif
diff --git a/source/Lib/CommonLib/Unit.h b/source/Lib/CommonLib/Unit.h
index e8c27172190364c6400f1d96dbde35312b143641..412f1442c8f71cb12b744a67d783a0e1746ac7fa 100644
--- a/source/Lib/CommonLib/Unit.h
+++ b/source/Lib/CommonLib/Unit.h
@@ -422,6 +422,10 @@ struct CodingUnit : public UnitArea
 #if JVET_AC0112_IBC_LIC
   bool           ibcLicFlag;
 #endif
+#if JVET_AE0159_FIBC
+  int64_t ibcFilterParams[FIBC_PARAMS];
+  bool           ibcFilterFlag;
+#endif
 #if JVET_AA0070_RRIBC
   int            rribcFlipType;
 #endif
diff --git a/source/Lib/CommonLib/UnitTools.cpp b/source/Lib/CommonLib/UnitTools.cpp
index 56d0fcfdb527ad64f17d6209fdc044fdc4cc8683..294f0c81cb44d3233e74ae4cdd0802f487e604b0 100644
--- a/source/Lib/CommonLib/UnitTools.cpp
+++ b/source/Lib/CommonLib/UnitTools.cpp
@@ -4128,6 +4128,13 @@ bool PU::addMergeHMVPCand(const CodingStructure &cs, MergeCtx &mrgCtx, const int
 #else
       mrgCtx.ibcLicFlags       [cnt] = miNeighbor.useIbcLic;
 #endif
+#endif
+#if JVET_AE0159_FIBC
+#if JVET_AA0070_RRIBC
+      mrgCtx.ibcFilterFlags    [cnt] = mrgCtx.rribcFlipTypes[cnt] ? false : miNeighbor.useIbcFilter;
+#else
+      mrgCtx.ibcFilterFlags    [cnt] = miNeighbor.useIbcFilter;
+#endif
 #endif
 
       mrgCtx.mvFieldNeighbours[cnt << 1].setMvField(miNeighbor.mv[0], miNeighbor.refIdx[0]);
@@ -4441,6 +4448,13 @@ bool PU::addIBCMergeHMVPCand(const CodingStructure &cs, MergeCtx &mrgCtx, const
 #endif
 #endif
 #endif
+#if JVET_AE0159_FIBC
+#if JVET_AA0070_RRIBC
+      mrgCtx.ibcFilterFlags[cnt] = mrgCtx.rribcFlipTypes[cnt] == 0 ? miNeighbor.useIbcFilter : false;
+#else
+      mrgCtx.ibcFilterFlags[cnt] = miNeighbor.useIbcFilter;
+#endif
+#endif
 #if !JVET_Z0084_IBC_TM
       if (slice.isInterB())
       {
@@ -4601,6 +4615,9 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
 #if JVET_AC0112_IBC_LIC
     mrgCtx.ibcLicFlags[ui] =false;
 #endif
+#if JVET_AE0159_FIBC
+    mrgCtx.ibcFilterFlags[ui] = false;
+#endif
 #if JVET_AA0070_RRIBC
     mrgCtx.rribcFlipTypes[ui] = 0;
 #endif
@@ -4688,6 +4705,13 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
 #else
     mrgCtx.ibcLicFlags[cnt] = miLeft.isIBCmot? miLeft.useIbcLic : false;
 #endif
+#endif
+#if JVET_AE0159_FIBC
+#if JVET_AA0070_RRIBC
+    mrgCtx.ibcFilterFlags[cnt] = miLeft.isIBCmot && mrgCtx.rribcFlipTypes[cnt] == 0 ? miLeft.useIbcFilter : false;
+#else
+    mrgCtx.ibcFilterFlags[cnt] = miLeft.isIBCmot? miLeft.useIbcFilter : false;
+#endif
 #endif
     if (mrgCandIdx == cnt)
     {
@@ -4748,6 +4772,13 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
 #else
       mrgCtx.ibcLicFlags[cnt] = miLeftL1.isIBCmot? miLeftL1.useIbcLic : false;
 #endif
+#endif
+#if JVET_AE0159_FIBC
+#if JVET_AA0070_RRIBC
+      mrgCtx.ibcFilterFlags[cnt] = miLeftL1.isIBCmot && mrgCtx.rribcFlipTypes[cnt] == 0 ? miLeftL1.useIbcFilter : false;
+#else
+      mrgCtx.ibcFilterFlags[cnt] = miLeftL1.isIBCmot? miLeftL1.useIbcFilter : false;
+#endif
 #endif
       if (mrgCandIdx == cnt)
       {
@@ -4830,6 +4861,13 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
       mrgCtx.ibcLicFlags[cnt] = miAbove.isIBCmot ? miAbove.useIbcLic : false;
 #endif
 #endif
+#if JVET_AE0159_FIBC
+#if JVET_AA0070_RRIBC
+      mrgCtx.ibcFilterFlags[cnt] = miAbove.isIBCmot && mrgCtx.rribcFlipTypes[cnt] == 0 ? miAbove.useIbcFilter : false;
+#else
+      mrgCtx.ibcFilterFlags[cnt] = miAbove.isIBCmot ? miAbove.useIbcFilter : false;
+#endif
+#endif
 
 #if JVET_Z0084_IBC_TM
       if( !mrgCtx.xCheckSimilarIBCMotion(cnt, mvdSimilarityThresh) )
@@ -4897,6 +4935,13 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
         mrgCtx.ibcLicFlags[cnt] = miAboveL1.isIBCmot ? miAboveL1.useIbcLic : false;
 #endif
 #endif
+#if JVET_AE0159_FIBC
+#if JVET_AA0070_RRIBC
+        mrgCtx.ibcFilterFlags[cnt] = miAboveL1.isIBCmot && mrgCtx.rribcFlipTypes[cnt] == 0 ? miAboveL1.useIbcFilter : false;
+#else
+        mrgCtx.ibcFilterFlags[cnt] = miAboveL1.isIBCmot ? miAboveL1.useIbcFilter : false;
+#endif
+#endif
 
 #if JVET_Z0084_IBC_TM
         if( !mrgCtx.xCheckSimilarIBCMotion(cnt, mvdSimilarityThresh) )
@@ -4983,6 +5028,13 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
         mrgCtx.ibcLicFlags[cnt] = miAboveRight.isIBCmot? miAboveRight.useIbcLic : false;
 #endif
 #endif
+#if JVET_AE0159_FIBC
+#if JVET_AA0070_RRIBC
+        mrgCtx.ibcFilterFlags[cnt] = miAboveRight.isIBCmot && mrgCtx.rribcFlipTypes[cnt] == 0 ? miAboveRight.useIbcFilter : false;
+#else
+        mrgCtx.ibcFilterFlags[cnt] = miAboveRight.isIBCmot? miAboveRight.useIbcFilter : false;
+#endif
+#endif
 
 #if JVET_Z0084_IBC_TM
         if( !mrgCtx.xCheckSimilarIBCMotion(cnt, mvdSimilarityThresh) )
@@ -5046,6 +5098,13 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
         mrgCtx.ibcLicFlags[cnt] = miAboveRightL1.isIBCmot? miAboveRightL1.useIbcLic : false;
 #endif
 #endif
+#if JVET_AE0159_FIBC
+#if JVET_AA0070_RRIBC
+        mrgCtx.ibcFilterFlags[cnt] = miAboveRightL1.isIBCmot && mrgCtx.rribcFlipTypes[cnt] == 0 ? miAboveRightL1.useIbcFilter : false;
+#else
+        mrgCtx.ibcFilterFlags[cnt] = miAboveRightL1.isIBCmot? miAboveRightL1.useIbcFilter : false;
+#endif
+#endif
 
 #if JVET_Z0084_IBC_TM
         if( !mrgCtx.xCheckSimilarIBCMotion(cnt, mvdSimilarityThresh) )
@@ -5128,6 +5187,13 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
         mrgCtx.ibcLicFlags[cnt] = miBelowLeft.isIBCmot ? miBelowLeft.useIbcLic : false;
 #endif
 #endif
+#if JVET_AE0159_FIBC
+#if JVET_AA0070_RRIBC
+        mrgCtx.ibcFilterFlags[cnt] = miBelowLeft.isIBCmot && mrgCtx.rribcFlipTypes[cnt] == 0 ? miBelowLeft.useIbcFilter : false;
+#else
+        mrgCtx.ibcFilterFlags[cnt] = miBelowLeft.isIBCmot ? miBelowLeft.useIbcFilter : false;
+#endif
+#endif
 
 #if JVET_Z0084_IBC_TM
         if( !mrgCtx.xCheckSimilarIBCMotion(cnt, mvdSimilarityThresh) )
@@ -5190,6 +5256,13 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
         mrgCtx.ibcLicFlags[cnt] = miBelowLeftL1.isIBCmot ? miBelowLeftL1.useIbcLic : false;
 #endif
 #endif
+#if JVET_AE0159_FIBC
+#if JVET_AA0070_RRIBC
+        mrgCtx.ibcFilterFlags[cnt] = miBelowLeftL1.isIBCmot && mrgCtx.rribcFlipTypes[cnt] == 0 ? miBelowLeftL1.useIbcFilter : false;
+#else
+        mrgCtx.ibcFilterFlags[cnt] = miBelowLeftL1.isIBCmot ? miBelowLeftL1.useIbcFilter : false;
+#endif
+#endif
 
 #if JVET_Z0084_IBC_TM
         if( !mrgCtx.xCheckSimilarIBCMotion(cnt, mvdSimilarityThresh) )
@@ -5284,6 +5357,13 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
           mrgCtx.ibcLicFlags[cnt] = miAboveLeft.isIBCmot ? miAboveLeft.useIbcLic : false;
 #endif
 #endif
+#if JVET_AE0159_FIBC
+#if JVET_AA0070_RRIBC
+          mrgCtx.ibcFilterFlags[cnt] = miAboveLeft.isIBCmot && mrgCtx.rribcFlipTypes[cnt] == 0 ? miAboveLeft.useIbcFilter : false;
+#else
+          mrgCtx.ibcFilterFlags[cnt] = miAboveLeft.isIBCmot ? miAboveLeft.useIbcFilter : false;
+#endif
+#endif
 
 #if JVET_Z0084_IBC_TM
           if( !mrgCtx.xCheckSimilarIBCMotion(cnt, mvdSimilarityThresh) )
@@ -5347,6 +5427,13 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
           mrgCtx.ibcLicFlags[cnt] = miAboveLeftL1.isIBCmot ? miAboveLeftL1.useIbcLic : false;
 #endif
 #endif
+#if JVET_AE0159_FIBC
+#if JVET_AA0070_RRIBC
+          mrgCtx.ibcFilterFlags[cnt] = miAboveLeftL1.isIBCmot && mrgCtx.rribcFlipTypes[cnt] == 0 ? miAboveLeftL1.useIbcFilter : false;
+#else
+          mrgCtx.ibcFilterFlags[cnt] = miAboveLeftL1.isIBCmot ? miAboveLeftL1.useIbcFilter : false;
+#endif
+#endif
 
 #if JVET_Z0084_IBC_TM
           if( !mrgCtx.xCheckSimilarIBCMotion(cnt, mvdSimilarityThresh) )
@@ -5504,6 +5591,13 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
             mrgCtx.ibcLicFlags[cnt]    = miNonAdjacent.isIBCmot ? miNonAdjacent.useIbcLic : false;
 #endif
 #endif
+#if JVET_AE0159_FIBC
+#if JVET_AA0070_RRIBC
+            mrgCtx.ibcFilterFlags[cnt] = miNonAdjacent.isIBCmot && mrgCtx.rribcFlipTypes[cnt] == 0 ? miNonAdjacent.useIbcFilter : false;
+#else
+            mrgCtx.ibcFilterFlags[cnt] = miNonAdjacent.isIBCmot ? miNonAdjacent.useIbcFilter : false;
+#endif
+#endif
 #if JVET_Z0084_IBC_TM
             if (!mrgCtx.xCheckSimilarIBCMotion(cnt, mvdSimilarityThresh))
 #endif
@@ -5570,6 +5664,13 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
               mrgCtx.ibcLicFlags[cnt] = miNonAdjacentL1.isIBCmot ? miNonAdjacentL1.useIbcLic : false;
 #endif
 #endif
+#if JVET_AE0159_FIBC
+#if JVET_AA0070_RRIBC
+              mrgCtx.ibcFilterFlags[cnt] = miNonAdjacentL1.isIBCmot && mrgCtx.rribcFlipTypes[cnt] == 0 ? miNonAdjacentL1.useIbcFilter : false;
+#else
+              mrgCtx.ibcFilterFlags[cnt] = miNonAdjacentL1.isIBCmot ? miNonAdjacentL1.useIbcFilter : false;
+#endif
+#endif
 
 #if JVET_Z0084_IBC_TM
               if (!mrgCtx.xCheckSimilarIBCMotion(cnt, mvdSimilarityThresh))
@@ -5699,6 +5800,13 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
             mrgCtx.ibcLicFlags[cnt]    = miNonAdjacent.isIBCmot ? miNonAdjacent.useIbcLic : false;
 #endif
 #endif
+#if JVET_AE0159_FIBC
+#if JVET_AA0070_RRIBC
+            mrgCtx.ibcFilterFlags[cnt] = miNonAdjacent.isIBCmot && mrgCtx.rribcFlipTypes[cnt] == 0 ? miNonAdjacent.useIbcFilter : false;
+#else
+            mrgCtx.ibcFilterFlags[cnt] = miNonAdjacent.isIBCmot ? miNonAdjacent.useIbcFilter : false;
+#endif
+#endif
 #if JVET_Z0084_IBC_TM
             if (!mrgCtx.xCheckSimilarIBCMotion(cnt, mvdSimilarityThresh))
 #endif
@@ -5766,7 +5874,13 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
               mrgCtx.ibcLicFlags[cnt] = miNonAdjacentL1.isIBCmot ? miNonAdjacentL1.useIbcLic : false;
 #endif
 #endif
-
+#if JVET_AE0159_FIBC
+#if JVET_AA0070_RRIBC
+              mrgCtx.ibcFilterFlags[cnt] = miNonAdjacentL1.isIBCmot && mrgCtx.rribcFlipTypes[cnt] == 0 ? miNonAdjacentL1.useIbcFilter : false;
+#else
+              mrgCtx.ibcFilterFlags[cnt] = miNonAdjacentL1.isIBCmot ? miNonAdjacentL1.useIbcFilter : false;
+#endif
+#endif
 #if JVET_Z0084_IBC_TM
               if (!mrgCtx.xCheckSimilarIBCMotion(cnt, mvdSimilarityThresh))
 #endif
@@ -5999,6 +6113,9 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
 #if JVET_AC0112_IBC_LIC
     mrgCtx.ibcLicFlags[cnt] = false;
 #endif
+#if JVET_AE0159_FIBC
+    mrgCtx.ibcFilterFlags[cnt] = false;
+#endif
 #if JVET_AA0070_RRIBC
     mrgCtx.rribcFlipTypes[cnt] = 0;
 #endif
@@ -6029,6 +6146,9 @@ void PU::getIBCMergeCandidates(const PredictionUnit &pu, MergeCtx& mrgCtx, const
 #if JVET_AC0112_IBC_LIC
     mrgCtx.ibcLicFlags[cnt] = false;
 #endif
+#if JVET_AE0159_FIBC
+    mrgCtx.ibcFilterFlags[cnt] = false;
+#endif
 #if JVET_AA0070_RRIBC
     mrgCtx.rribcFlipTypes[cnt] = 0;
 #endif
@@ -6194,6 +6314,16 @@ uint32_t PU::checkValidBv(const PredictionUnit& pu, ComponentID compID, int comp
 }
 #endif
 
+#if JVET_AE0159_FIBC
+bool PU::checkIsIBCFilterCandidateValid(const PredictionUnit& pu, const MotionInfo miNeighbor, int filterIdx, bool isRefTemplate, bool isRefAbove)
+{
+  int roiWidth  = (isRefTemplate && !isRefAbove) ? FIBC_TEMPLATE_SIZE : pu.lwidth();
+  int roiHeight = (isRefTemplate &&  isRefAbove) ? FIBC_TEMPLATE_SIZE : pu.lheight();
+  uint32_t validType = checkValidBv(pu, COMPONENT_Y, roiWidth, roiHeight, miNeighbor.mv[REF_PIC_LIST_0], true, filterIdx);
+  return validType != IBC_BV_INVALID;
+}
+#endif
+
 bool PU::searchBv(const PredictionUnit& pu, int xPos, int yPos, int width, int height, int picWidth, int picHeight, int xBv, int yBv, int ctuSize
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
                 , int xFilterTap, int yFilterTap, ComponentID compID
@@ -7149,6 +7279,9 @@ void PU::getInterMergeCandidates( const PredictionUnit &pu, MergeCtx& mrgCtx,
 #if JVET_AC0112_IBC_LIC
     mrgCtx.ibcLicFlags[ui] = false;
 #endif
+#if JVET_AE0159_FIBC
+    mrgCtx.ibcFilterFlags[ui] = false;
+#endif
 #if JVET_AA0070_RRIBC
     mrgCtx.rribcFlipTypes[ui] = 0;
 #endif
@@ -19380,6 +19513,9 @@ void PU::spanMotionInfo( PredictionUnit &pu, const MergeCtx &mrgCtx )
 #if JVET_AC0112_IBC_LIC
     mi.useIbcLic = mi.isIBCmot ? pu.cu->ibcLicFlag : 0;
 #endif
+#if JVET_AE0159_FIBC
+    mi.useIbcFilter = mi.isIBCmot ? pu.cu->ibcFilterFlag : false;
+#endif
 #if JVET_AA0070_RRIBC
     mi.rribcFlipType = mi.isIBCmot ? pu.cu->rribcFlipType : 0;
 #endif
diff --git a/source/Lib/CommonLib/UnitTools.h b/source/Lib/CommonLib/UnitTools.h
index 6c58fbd29c2f29e483d66f14115473d848754c07..55a999e2573550c9e4f4ff407e679391d92de6d0 100644
--- a/source/Lib/CommonLib/UnitTools.h
+++ b/source/Lib/CommonLib/UnitTools.h
@@ -307,6 +307,9 @@ namespace PU
                          , bool checkAllRefValid = false
   );
 #endif
+#if JVET_AE0159_FIBC
+  bool checkIsIBCFilterCandidateValid(const PredictionUnit &pu, const MotionInfo miNeighbor, int  filterIdx = 0, bool isRefTemplate = false, bool isRefAbove = false);
+#endif
 #if JVET_Y0058_IBC_LIST_MODIFY || JVET_Z0084_IBC_TM || JVET_AA0061_IBC_MBVD || JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
   bool searchBv(const PredictionUnit& pu, int xPos, int yPos, int width, int height, int picWidth, int picHeight, int xBv, int yBv, int ctuSize
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
diff --git a/source/Lib/DecoderLib/CABACReader.cpp b/source/Lib/DecoderLib/CABACReader.cpp
index b40d8aabc1d0b8df72fe0b1a1924ba59e5012c0c..50c0f00f19cfd506f917a7d9d7a894e1af73ee68 100644
--- a/source/Lib/DecoderLib/CABACReader.cpp
+++ b/source/Lib/DecoderLib/CABACReader.cpp
@@ -4707,6 +4707,46 @@ void CABACReader::ibcGpmAdaptBlendIdx(PredictionUnit& pu)
 #if JVET_AC0112_IBC_LIC
 void CABACReader::cuIbcLicFlag( CodingUnit& cu )
 {
+#if JVET_AE0159_FIBC
+  if (!(cu.cs->sps->getUseIbcLic() || cu.cs->sps->getUseIbcFilter()) || !CU::isIBC(cu) || cu.firstPU->mergeFlag)
+  {
+    cu.ibcLicFlag = false;
+    return;
+  }
+#if JVET_AA0070_RRIBC
+  if (cu.rribcFlipType > 0)
+  {
+    cu.ibcLicFlag = false;
+    return;
+  }
+#endif
+  if (cu.lwidth() * cu.lheight() < 32)
+  {
+    cu.ibcLicFlag = false;
+    return;
+  }
+  if ((cu.lx() >= FIBC_TEMPLATE_SIZE || cu.ly() >= FIBC_TEMPLATE_SIZE) && (cu.cs->slice->getSliceType() == I_SLICE) && cu.cs->sps->getUseIbcFilter() )
+  {
+    unsigned ctxIdx = 1 + DeriveCtx::ctxIbcFilterFlag(cu);
+    cu.ibcFilterFlag = m_BinDecoder.decodeBin(Ctx::IbcLicFlag(ctxIdx));
+  }
+  else
+  {
+    cu.ibcFilterFlag = false;
+  }
+  if (!cu.ibcFilterFlag && (cu.lwidth() * cu.lheight() <= 256))
+  {
+    cu.ibcLicFlag = m_BinDecoder.decodeBin(Ctx::IbcLicFlag(0));
+  }
+  else if (!cu.ibcFilterFlag) // (cu.lwidth() * cu.lheight() > 256)
+  {
+    cu.ibcLicFlag = false;
+  }
+  else 
+  {
+    cu.ibcLicFlag = true;
+  }
+#else
   if (!cu.cs->sps->getUseIbcLic() || !CU::isIBC(cu) || cu.firstPU->mergeFlag)
   {
     cu.ibcLicFlag = false;
@@ -4725,6 +4765,7 @@ void CABACReader::cuIbcLicFlag( CodingUnit& cu )
     return;
   }
   cu.ibcLicFlag = m_BinDecoder.decodeBin( Ctx::IbcLicFlag() );
+#endif
 }
 #endif
 
diff --git a/source/Lib/DecoderLib/DecCu.cpp b/source/Lib/DecoderLib/DecCu.cpp
index 8e32a9b281bc17ab7355f2c7c13a5839184821b4..eb1c1535c7e36eeb37856ec9ca8a68fb67fb1dd0 100644
--- a/source/Lib/DecoderLib/DecCu.cpp
+++ b/source/Lib/DecoderLib/DecCu.cpp
@@ -122,6 +122,9 @@ void DecCu::decompressCtu( CodingStructure& cs, const UnitArea& ctuArea )
 #if JVET_AE0059_INTER_CCCM
   m_pcInterPred->m_interCccm->setCccmBuffers(m_pcIntraPred->getCccmBufferA(),m_pcIntraPred->getCccmBufferCb(),m_pcIntraPred->getCccmBufferCr(),m_pcIntraPred->getCccmBufferSamples());
 #endif
+#if JVET_AE0159_FIBC
+  m_pcInterPred->setIbcFilterBuffers(m_pcIntraPred->getCccmBufferA(),m_pcIntraPred->getCccmBufferCb(),m_pcIntraPred->getCccmBufferSamples(), m_pcIntraPred);
+#endif
 #if JVET_Z0118_GDR
   // reset current IBC Buffer only when VB pass through
   if (cs.isGdrEnabled() && cs.isInGdrIntervalOrRecoveryPoc())
@@ -3844,6 +3847,9 @@ void DecCu::xDeriveCUMV( CodingUnit &cu )
               m_pcInterPred->adjustIbcMergeRribcCand(pu, mrgCtx, 0, IBC_MRG_MAX_NUM_CANDS_MEM);
               pu.cu->rribcFlipType = 0;
               pu.cu->ibcLicFlag = 0;
+#if JVET_AE0159_FIBC
+              pu.cu->ibcFilterFlag = false;
+#endif
             }
 #endif
             pu.cu->imv = imv;
diff --git a/source/Lib/DecoderLib/VLCReader.cpp b/source/Lib/DecoderLib/VLCReader.cpp
index 3ab589c1f422f6cf178fc0437cdfaf510e5eb43e..be18036c2c114fe7553e666bf93bb285f6109959 100644
--- a/source/Lib/DecoderLib/VLCReader.cpp
+++ b/source/Lib/DecoderLib/VLCReader.cpp
@@ -2713,6 +2713,9 @@ void HLSyntaxReader::parseSPS(SPS* pcSPS)
 #if JVET_AC0112_IBC_LIC
     READ_FLAG( uiCode, "sps_ibc_lic_enabled_flag" );                    pcSPS->setUseIbcLic              ( uiCode != 0 );
 #endif
+#if JVET_AE0159_FIBC
+    READ_FLAG( uiCode, "sps_ibc_filter_enabled_flag" );                 pcSPS->setUseIbcFilter           ( uiCode != 0 );
+#endif
 #if JVET_AE0094_IBC_NONADJACENT_SPATIAL_CANDIDATES
     READ_FLAG( uiCode, "sps_ibc_non_adjacent_spatial_candidates_enabled_flag");    pcSPS->setUseIbcNonAdjCand(uiCode != 0);
 #endif
diff --git a/source/Lib/EncoderLib/CABACWriter.cpp b/source/Lib/EncoderLib/CABACWriter.cpp
index d0e4cad1ae15ec3c462fab935b3563baec149218..61390d5dcb2f88de32ce57ceca99495e675e73a5 100644
--- a/source/Lib/EncoderLib/CABACWriter.cpp
+++ b/source/Lib/EncoderLib/CABACWriter.cpp
@@ -4206,7 +4206,11 @@ void CABACWriter::ibcGpmAdaptBlendIdx(const int flag)
 #if JVET_AC0112_IBC_LIC
 void CABACWriter::cuIbcLicFlag(const CodingUnit& cu)
 {
+#if JVET_AE0159_FIBC
+  if (!(cu.cs->sps->getUseIbcLic() || cu.cs->sps->getUseIbcFilter() ) || !CU::isIBC(cu) || cu.firstPU->mergeFlag)
+#else
   if (!cu.cs->sps->getUseIbcLic() || !CU::isIBC(cu) || cu.firstPU->mergeFlag)
+#endif
   {
     return;
   }
@@ -4216,11 +4220,35 @@ void CABACWriter::cuIbcLicFlag(const CodingUnit& cu)
     return;
   }
 #endif
+#if JVET_AE0159_FIBC
+  if (cu.lwidth() * cu.lheight() < 32 )
+#else
   if (cu.lwidth() * cu.lheight() < 32 || cu.lwidth() * cu.lheight() > 256)
+#endif
   {
     return;
   }
+#if JVET_AE0159_FIBC
+  if (cu.ibcFilterFlag)
+  {
+    CHECK(!cu.ibcLicFlag, "LIC flag has to be 1 when FIBC is 1");
+  }
+  if (cu.lx() < FIBC_TEMPLATE_SIZE && cu.ly() < FIBC_TEMPLATE_SIZE)
+  {
+    CHECK(cu.ibcFilterFlag, "FIBC has to be 0 when not enough template");
+  }
+  if ((cu.lx() >= FIBC_TEMPLATE_SIZE || cu.ly() >= FIBC_TEMPLATE_SIZE) && (cu.cs->slice->getSliceType() == I_SLICE) && cu.cs->sps->getUseIbcFilter() )
+  {
+    unsigned ctxIdx = 1 + DeriveCtx::ctxIbcFilterFlag(cu);
+    m_BinEncoder.encodeBin(cu.ibcFilterFlag ? 1 : 0, Ctx::IbcLicFlag(ctxIdx));
+  }
+  if (!cu.ibcFilterFlag && (cu.lwidth() * cu.lheight() <= 256))
+  {
+    m_BinEncoder.encodeBin(cu.ibcLicFlag ? 1 : 0, Ctx::IbcLicFlag(0));
+  }
+#else
   m_BinEncoder.encodeBin(cu.ibcLicFlag ? 1 : 0, Ctx::IbcLicFlag());
+#endif
 }
 #endif
 
diff --git a/source/Lib/EncoderLib/EncCfg.h b/source/Lib/EncoderLib/EncCfg.h
index 38ad1487530fd0d26b52461e5a57d17fd90965fe..f38777671e23aca917b76af4363d1169e14d87b8 100644
--- a/source/Lib/EncoderLib/EncCfg.h
+++ b/source/Lib/EncoderLib/EncCfg.h
@@ -553,6 +553,9 @@ protected:
 #if JVET_AC0112_IBC_LIC
   bool      m_ibcLic;
 #endif
+#if JVET_AE0159_FIBC
+  bool      m_ibcFilter;
+#endif
 #if JVET_AE0169_BIPREDICTIVE_IBC
   bool      m_ibcBiPred;
 #endif
@@ -1488,6 +1491,10 @@ public:
   void      setIbcLic                       ( bool b )       { m_ibcLic = b; }
   bool      getIbcLic                       ()         const { return m_ibcLic; }
 #endif
+#if JVET_AE0159_FIBC
+  void      setIbcFilter                       ( bool b )       { m_ibcFilter = b; }
+  bool      getIbcFilter                       ()         const { return m_ibcFilter; }
+#endif
 #if JVET_AE0169_BIPREDICTIVE_IBC
   void      setIbcBiPred                    ( bool b )       { m_ibcBiPred = b; }
   bool      getIbcBiPred                    ()         const { return m_ibcBiPred; }
diff --git a/source/Lib/EncoderLib/EncCu.cpp b/source/Lib/EncoderLib/EncCu.cpp
index f2f30f7cef50289212ad0cc31c047bb9fcb51cf9..998ed1a2079b0524e318befcac764f4a38f0465c 100644
--- a/source/Lib/EncoderLib/EncCu.cpp
+++ b/source/Lib/EncoderLib/EncCu.cpp
@@ -12426,6 +12426,9 @@ void EncCu::xCheckRDCostIBCModeMerge2Nx2N(CodingStructure *&tempCS, CodingStruct
 #if JVET_AC0112_IBC_LIC
     cu.ibcLicFlag = false;
 #endif
+#if JVET_AE0159_FIBC
+    cu.ibcFilterFlag = false;
+#endif
 #if JVET_AA0070_RRIBC
     cu.rribcFlipType = 0;
     pu.mergeFlag = true;
@@ -12656,6 +12659,9 @@ void EncCu::xCheckRDCostIBCModeMerge2Nx2N(CodingStructure *&tempCS, CodingStruct
 #endif
 #if JVET_AC0112_IBC_LIC
       cu.ibcLicFlag = false;
+#endif
+#if JVET_AE0159_FIBC
+      cu.ibcFilterFlag = false;
 #endif
       cu.geoFlag = false;
 
@@ -14530,6 +14536,9 @@ void EncCu::xCheckRDCostIBCModeMerge2Nx2N(CodingStructure *&tempCS, CodingStruct
 #if JVET_AC0112_IBC_LIC
             cu.ibcLicFlag = false;
 #endif
+#if JVET_AE0159_FIBC
+            cu.ibcFilterFlag = false;
+#endif
 
 #if JVET_AA0070_RRIBC
             cu.rribcFlipType = 0;
@@ -14989,7 +14998,11 @@ void EncCu::xCheckRDCostIBCMode(CodingStructure *&tempCS, CodingStructure *&best
   double curBestCost = bestCS->cost;
   bool searchedByHash[1] = {false};
   Distortion tempCost[1] = {0};
+#if JVET_AE0159_FIBC
+  Distortion searchCost[3] = {0, 0, 0};
+#else
   Distortion searchCost[2] = {0, 0};
+#endif
 #if JVET_AD0208_IBC_ADAPT_FOR_CAM_CAPTURED_CONTENTS
   m_pcInterSearch->m_bestSrchCostIntBv.init(true);
 #if JVET_AC0112_IBC_LIC
@@ -15014,8 +15027,14 @@ void EncCu::xCheckRDCostIBCMode(CodingStructure *&tempCS, CodingStructure *&best
 
 #if JVET_AC0112_IBC_LIC
   bool skipSecondLicPass = false;
+#if JVET_AE0159_FIBC
+  double bestNonLicCost = MAX_DOUBLE;
+  int licIdxMax = (m_pcEncCfg->getIntraPeriod() == 1) && tempCS->slice->getSPS()->getUseIbcLic() && (tempCS->area.lx() > 0 || tempCS->area.ly() > 0) ? 2 : tempCS->slice->getSPS()->getUseIbcLic() && (tempCS->area.lx() > FIBC_TEMPLATE_SIZE || tempCS->area.ly() > FIBC_TEMPLATE_SIZE) ? 3 : (tempCS->slice->getSPS()->getUseIbcLic() && (tempCS->area.lx() > 0 || tempCS->area.ly() > 0) ? 2 : 1);
+  if ( tempCS->area.lwidth() * tempCS->area.lheight() < 32 )
+#else
   int licIdxMax = tempCS->slice->getSPS()->getUseIbcLic() && (tempCS->area.lx() > 0 || tempCS->area.ly() > 0) ? 2 : 1;
   if (tempCS->area.lwidth() * tempCS->area.lheight() < 32 || tempCS->area.lwidth() * tempCS->area.lheight() > 256)
+#endif
   {
     licIdxMax = 1;
   }
@@ -15062,6 +15081,12 @@ void EncCu::xCheckRDCostIBCMode(CodingStructure *&tempCS, CodingStructure *&best
       }
       for (int licIdx = 0; licIdx < licIdxMax; licIdx++)
       {
+#if JVET_AE0159_FIBC
+        if (licIdx == 1 && tempCS->area.lwidth() * tempCS->area.lheight() > 256)
+        {
+          continue;
+        }
+#endif
         if (licIdx == 1 && skipSecondLicPass)
         {
           continue;
@@ -15095,7 +15120,27 @@ void EncCu::xCheckRDCostIBCMode(CodingStructure *&tempCS, CodingStructure *&best
     cu.imv = 0;
     cu.sbtInfo = 0;
 #if JVET_AC0112_IBC_LIC
+#if JVET_AE0159_FIBC
+    cu.ibcLicFlag = licIdx > 0 ? true: false;
+    if (m_pcEncCfg->getIntraPeriod() == 1)
+    {
+      cu.ibcFilterFlag = false;
+    }
+    else
+    {
+      cu.ibcFilterFlag = licIdx > 1 ? true : false;
+      if ( cu.ibcLicFlag && !cu.ibcFilterFlag && tempCS->area.lwidth() * tempCS->area.lheight() > 256)
+      {
+        continue;
+      }
+    }
+    if ((licIdx == 2) && ( !tempCS->slice->getSPS()->getUseIbcFilter() || cu.cs->slice->getSliceType() != I_SLICE))
+    {
+      continue;
+    }
+#else
     cu.ibcLicFlag = licIdx;
+#endif
 #endif
 
     CU::addPUs(cu);
@@ -15268,6 +15313,19 @@ void EncCu::xCheckRDCostIBCMode(CodingStructure *&tempCS, CodingStructure *&best
       }
 #endif
 #endif
+#if JVET_AE0159_FIBC
+      if (tempCS->slice->getSPS()->getUseIbcFilter())
+      {
+        if (!cu.ibcLicFlag)
+        {
+          bestNonLicCost = min(dCost, bestNonLicCost);
+        }
+        else if (dCost > 1.4 * bestNonLicCost)
+        {
+          continue;
+        }
+      }
+#endif
 #if JVET_AC0112_IBC_LIC
       if (licIdx == 0 && searchedByHash[0])
       {
@@ -15302,7 +15360,11 @@ void EncCu::xCheckRDCostIBCMode(CodingStructure *&tempCS, CodingStructure *&best
 #else
         searchCost[licIdx] = tempCost[0];
 #endif
+#if JVET_AE0159_FIBC
+        if (licIdx > 0 && searchCost[0] > 0 && searchCost[licIdx] > 1.05 * searchCost[0])
+#else
         if (licIdx > 0 && searchCost[0] > 0 && searchCost[1] > 1.05 * searchCost[0])
+#endif
         {
           continue;
         }
@@ -15419,7 +15481,11 @@ void EncCu::xCheckRDCostIBCMode(CodingStructure *&tempCS, CodingStructure *&best
           }
 #endif
 #if JVET_AC0112_IBC_LIC
+#if JVET_AE0159_FIBC
+          if ( !tempCS->slice->getSPS()->getUseIbcFilter() && licIdx == 0 && tempCS->cost > curBestCost * 1.4)
+#else
           if (licIdx == 0 && tempCS->cost > curBestCost * 1.4)
+#endif
           {
             skipSecondLicPass = true;
           }
diff --git a/source/Lib/EncoderLib/EncLib.cpp b/source/Lib/EncoderLib/EncLib.cpp
index a54e330dd3697d930f4c665422ec30c4803e2b3d..11fac02b8d19c4c56e5b0c11cbff34f8aec367ea 100644
--- a/source/Lib/EncoderLib/EncLib.cpp
+++ b/source/Lib/EncoderLib/EncLib.cpp
@@ -753,6 +753,9 @@ void EncLib::init( bool isFieldCoding, AUWriterIf* auWriterIf )
 
 #if JVET_AE0059_INTER_CCCM
   m_cInterSearch.m_interCccm->setCccmBuffers(m_cIntraSearch.getCccmBufferA(),m_cIntraSearch.getCccmBufferCb(),m_cIntraSearch.getCccmBufferCr(),m_cIntraSearch.getCccmBufferSamples());
+#endif
+#if JVET_AE0159_FIBC
+  m_cInterSearch.setIbcFilterBuffers(m_cIntraSearch.getCccmBufferA(),m_cIntraSearch.getCccmBufferCb(),m_cIntraSearch.getCccmBufferSamples(), &m_cIntraSearch);
 #endif
   m_iMaxRefPicNum = 0;
 
@@ -1943,6 +1946,9 @@ void EncLib::xInitSPS( SPS& sps )
 #if JVET_AC0112_IBC_LIC
   sps.setUseIbcLic                          ( m_ibcLic );
 #endif
+#if JVET_AE0159_FIBC
+  sps.setUseIbcFilter                       ( m_ibcFilter );
+#endif
 #if JVET_AE0094_IBC_NONADJACENT_SPATIAL_CANDIDATES
   sps.setUseIbcNonAdjCand                   ( m_ibcNonAdjCand );
 #endif
diff --git a/source/Lib/EncoderLib/EncSlice.cpp b/source/Lib/EncoderLib/EncSlice.cpp
index 0231639449b7320f9af15cdbc325989e104c2151..473babd859c22ed94e432adc19699c46ad3093b0 100644
--- a/source/Lib/EncoderLib/EncSlice.cpp
+++ b/source/Lib/EncoderLib/EncSlice.cpp
@@ -1857,6 +1857,15 @@ void EncSlice::encodeCtus( Picture* pcPic, const bool bCompressEntireSlice, cons
     }
   }
 #endif
+#if JVET_AE0159_FIBC
+  if (m_pcCuEncoder->getEncCfg()->getIbcFilter())
+  {
+    SPS* spsTmp = const_cast<SPS*>(cs.sps);
+    hashBlkHitPerc = (hashBlkHitPerc == -1) ? m_pcCuEncoder->getIbcHashMap().calHashBlkMatchPerc(cs.area.Y()) : hashBlkHitPerc;
+    bool isSCC = hashBlkHitPerc >= 20;
+    spsTmp->setUseIbcFilter(isSCC);   
+  }
+#endif
 #if JVET_AD0188_CCP_MERGE
   if ((pCfg->getSwitchPOC() != pcPic->poc || -1 == pCfg->getDebugCTU()))
   {
diff --git a/source/Lib/EncoderLib/InterSearch.cpp b/source/Lib/EncoderLib/InterSearch.cpp
index 2192f49efc6dec7fd5e557badb142b65f7b0dccc..9a6be749f330605e6f827f60baf688f635238d42 100644
--- a/source/Lib/EncoderLib/InterSearch.cpp
+++ b/source/Lib/EncoderLib/InterSearch.cpp
@@ -2487,8 +2487,16 @@ void InterSearch::xIBCEstimation(PredictionUnit& pu, PelUnitBuf& origBuf, Mv *pc
 
   m_cDistParam.useMR = false;
 #if JVET_AC0112_IBC_LIC
+#if JVET_AE0159_FIBC
+  PelUnitBuf predBuf = pu.cs->getPredBuf(pu);
+  if (!pu.cs->sps->getUseIbcFilter())
+  {
+    m_cDistParam.useMR = pu.cu->ibcLicFlag ? true : false ;
+  }
+#else
   m_cDistParam.useMR = pu.cu->ibcLicFlag;
 #endif
+#endif
 #if !JVET_AA0070_RRIBC
   m_pcRdCost->setDistParam(m_cDistParam, *cStruct.pcPatternKey, cStruct.piRefY, cStruct.iRefStride, m_lumaClpRng.bd, COMPONENT_Y, cStruct.subShiftMode);
 #endif
@@ -2800,7 +2808,29 @@ void InterSearch::xIBCEstimation(PredictionUnit& pu, PelUnitBuf& origBuf, Mv *pc
         buffered = true;
         Distortion sad = m_pcRdCost->getBvCostMultiplePreds(xBv, yBv, pu.cs->sps->getAMVREnabledFlag());
 #endif
+#if JVET_AE0159_FIBC
+        if (pu.cs->sps->getUseIbcFilter())
+        {
+          if (pu.cu->ibcLicFlag)
+          {
+            pu.mv[0] = Mv((xBv << MV_FRACTIONAL_BITS_INTERNAL), (yBv << MV_FRACTIONAL_BITS_INTERNAL));
+            getPredIBCBlk(pu, COMPONENT_Y, pu.cu->slice->getPic(), pu.mv[0], predBuf, false);
+            m_cDistParam.cur.buf = predBuf.Y().buf;
+            m_cDistParam.cur.stride = predBuf.Y().stride;
+          }
+          else
+          {
+            m_cDistParam.cur.buf = cStruct.piRefY + cStruct.iRefStride * yBv + xBv;
+            m_cDistParam.cur.stride = cStruct.iRefStride;
+          }
+        }
+        else
+        {
+          m_cDistParam.cur.buf = cStruct.piRefY + cStruct.iRefStride * yBv + xBv;
+        }
+#else
         m_cDistParam.cur.buf = cStruct.piRefY + cStruct.iRefStride * yBv + xBv;
+#endif
 #if JVET_AE0169_BIPREDICTIVE_IBC
         sad += ((m_cDistParam.distFunc(m_cDistParam)+sadAdd)>>sadShift);
 #else
@@ -2922,7 +2952,29 @@ void InterSearch::xIBCEstimation(PredictionUnit& pu, PelUnitBuf& origBuf, Mv *pc
 #else
           Distortion sad = m_pcRdCost->getBvCostMultiplePreds(xPred, yPred, pu.cs->sps->getAMVREnabledFlag());
 #endif
+#if JVET_AE0159_FIBC
+          if (pu.cs->sps->getUseIbcFilter())
+          {
+            if (pu.cu->ibcLicFlag)
+            {
+              pu.mv[0] = Mv((xPred << MV_FRACTIONAL_BITS_INTERNAL), (yPred << MV_FRACTIONAL_BITS_INTERNAL));
+              getPredIBCBlk(pu, COMPONENT_Y, pu.cu->slice->getPic(), pu.mv[0], predBuf, false);
+              m_cDistParam.cur.buf = predBuf.Y().buf;
+              m_cDistParam.cur.stride = predBuf.Y().stride;
+            }
+            else
+            {
+              m_cDistParam.cur.buf = cStruct.piRefY + cStruct.iRefStride * yPred + xPred;
+              m_cDistParam.cur.stride = cStruct.iRefStride;
+            }
+          }
+          else
+          {
+            m_cDistParam.cur.buf = cStruct.piRefY + cStruct.iRefStride * yPred + xPred;
+          }
+#else
           m_cDistParam.cur.buf = cStruct.piRefY + cStruct.iRefStride * yPred + xPred;
+#endif
 #if JVET_AE0169_BIPREDICTIVE_IBC
           sad += ((m_cDistParam.distFunc(m_cDistParam)+sadAdd)>>sadShift);
 #else
@@ -3506,6 +3558,9 @@ bool InterSearch::predIBCSearch(CodingUnit& cu, Partitioner& partitioner, const
       pu.interDir = 1;
       pu.mergeFlag = false;
       pu.cu->ibcLicFlag = 0;
+#if JVET_AE0159_FIBC
+      pu.cu->ibcFilterFlag = false;
+#endif    
     }
 #endif
     pu.cu->rribcFlipType = 0;
@@ -4197,7 +4252,14 @@ Distortion InterSearch::xPredIBCFracPelSearch(PredictionUnit&              pu
   PelUnitBuf predBuf = pu.cs->getPredBuf(pu);
   DistParam distParam;
 #if JVET_AC0112_IBC_LIC
+#if JVET_AE0159_FIBC
+  if (!pu.cs->sps->getUseIbcFilter())
+  {
+    distParam.useMR = pu.cu->ibcLicFlag  ? true : false ;
+  }
+#else
   distParam.useMR = pu.cu->ibcLicFlag;
+#endif
 #endif
   m_pcRdCost->setDistParam(distParam, tmpOrgLuma[0], predBuf.Y().buf, predBuf.Y().stride, pu.cs->sps->getBitDepth(CHANNEL_TYPE_LUMA), COMPONENT_Y, 0, 1, false);
   m_pcRdCost->getMotionCost(0);
@@ -4205,6 +4267,112 @@ Distortion InterSearch::xPredIBCFracPelSearch(PredictionUnit&              pu
 
   for (int i = 0; i < intBvList.cnt; ++i)
   {
+#if JVET_AE0159_FIBC
+    if (m_pcEncCfg->getIntraPeriod() != 1) //non-AI
+    {
+      pu.cu->imv = IMV_FPEL;
+      pu.bv      = intBvList.mvList[i];
+      pu.mv[0]   = intBvList.mvList[i];
+      pu.mv[0].changePrecision(MV_PRECISION_INT, MV_PRECISION_INTERNAL);
+#if JVET_AC0112_IBC_LIC
+      if (pu.cu->ibcLicFlag)
+      {
+        getPredIBCBlk(pu, COMPONENT_Y, pu.cu->slice->getPic(), pu.mv[0], predBuf, useBilinearMC);
+        distParam.cur.buf = predBuf.Y().buf;
+        distParam.cur.stride = predBuf.Y().stride;
+      }
+      else
+#endif
+      {
+        Position offset = pu.Y().pos().offset(pu.bv.getHor(), pu.bv.getVer());
+        CPelBuf  refBuf = pu.cu->slice->getPic()->getRecoBuf(CompArea(COMPONENT_Y, pu.chromaFormat, offset, Size(pu.lwidth(), pu.lheight())), false);
+        distParam.cur.buf = refBuf.buf;
+        distParam.cur.stride = refBuf.stride;
+      }
+#if JVET_AC0060_IBC_BVP_CLUSTER_RRIBC_BVD_SIGN_DERIV || JVET_AA0070_RRIBC
+      int bvType = intBvList.bvTypeList[i];
+#else
+      int bvType = 0;
+#endif
+#if JVET_AE0169_BIPREDICTIVE_IBC
+      if (pu.interDir == 3)
+      {
+#if JVET_AA0070_RRIBC 
+        tmpOrgLuma[0] = tmpOrgBvpMerge[i];
+#else
+        distParam.org = tmpOrgBvpMerge[i];
+#endif
+      }
+#endif
+#if JVET_AA0070_RRIBC 
+#if JVET_AC0060_IBC_BVP_CLUSTER_RRIBC_BVD_SIGN_DERIV
+      distParam.org = tmpOrgLuma[intBvList.bvFlipList[i] ? bvType : 0];
+#else
+      distParam.org = tmpOrgLuma[bvType];
+#endif
+#endif
+
+      uint32_t   addExtraBits = 1 + bvType
+#if JVET_AC0060_IBC_BVP_CLUSTER_RRIBC_BVD_SIGN_DERIV && JVET_AA0070_RRIBC
+        + (bvType == 0 ? 0 : (intBvList.bvFlipList[i] ? 2 : 0))
+#endif
+        ;
+      bool       has4PelIMV = (pu.bv.getHor() & 3) == 0 && (pu.bv.getVer() & 3) == 0;
+#if JVET_AE0169_BIPREDICTIVE_IBC
+        uint8_t    tempMvpIdx[3] = { 0, 0, 0 };
+        Distortion tempBvCost[3] =
+        {
+          m_pcRdCost->getBvCostSingle(pu.mv[0], amvpInfoList[IMV_OFF][bvType], IMV_OFF, imvForZeroMvd, bvType == 2, bvType == 1, addExtraBits, tempMvpIdx[0]),
+          m_pcRdCost->getBvCostSingle(pu.mv[0], amvpInfoList[IMV_FPEL][bvType], IMV_FPEL, imvForZeroMvd, bvType == 2, bvType == 1, addExtraBits, tempMvpIdx[1]),
+          has4PelIMV ? m_pcRdCost->getBvCostSingle(pu.mv[0], amvpInfoList[IMV_4PEL][bvType], IMV_4PEL, imvForZeroMvd, bvType == 2, bvType == 1, addExtraBits, tempMvpIdx[2]) : std::numeric_limits<Distortion>::max()
+        };
+#if JVET_AE0169_BIPREDICTIVE_IBC
+      if (pu.interDir == 3)
+      {
+        tempBvCost[0] += m_pcRdCost->getBvpMergeCost(intBvList.mergeIdxList[i]);
+        tempBvCost[1] += m_pcRdCost->getBvpMergeCost(intBvList.mergeIdxList[i]);
+        if (has4PelIMV)
+        {
+          tempBvCost[2] += m_pcRdCost->getBvpMergeCost(intBvList.mergeIdxList[i]);
+        }
+      }
+#endif
+      uint8_t bestIdx = (tempBvCost[2] < tempBvCost[1] && tempBvCost[2] < tempBvCost[0]) ? 2 : (tempBvCost[1] < tempBvCost[0]) ? 1 : 0;
+      intBvList.imvList   [i] = ImvMode(bestIdx);
+#else
+      uint8_t    tempMvpIdx[2] = { 0, 0 };
+      Distortion tempBvCost[2] =
+      {
+        m_pcRdCost->getBvCostSingle(pu.mv[0], amvpInfoList[IMV_FPEL][bvType], IMV_FPEL, imvForZeroMvd, bvType == 2, bvType == 1, addExtraBits, tempMvpIdx[0]),
+        has4PelIMV ? m_pcRdCost->getBvCostSingle(pu.mv[0], amvpInfoList[IMV_4PEL][bvType], IMV_4PEL, imvForZeroMvd, bvType == 2, bvType == 1, addExtraBits, tempMvpIdx[1]) : std::numeric_limits<Distortion>::max()
+      };
+#if JVET_AE0169_BIPREDICTIVE_IBC
+      if (pu.interDir == 3)
+      {
+        tempBvCost[0] += m_pcRdCost->getBvpMergeCost(intBvList.mergeIdxList[i]);
+        if (has4PelIMV)
+        {
+          tempBvCost[1] += m_pcRdCost->getBvpMergeCost(intBvList.mergeIdxList[i]);
+        }
+      }
+#endif
+      uint8_t bestIdx = tempBvCost[1] < tempBvCost[0] ? 1 : 0;
+      intBvList.imvList[i] = bestIdx == 0 ? IMV_FPEL : IMV_4PEL;
+#endif
+      intBvList.mvpIdxList[i] = tempMvpIdx[bestIdx];
+      intBvList.costList[i] = tempBvCost[bestIdx] + distParam.distFunc(distParam);
+      intBvList.mvList[i] = pu.mv[0];
+    }
+    else
+    {
+      const int ibcLicLoopNum = pu.cu->ibcLicFlag && pu.cs->sps->getUseIbcFilter() && (pu.cu->slice->getSliceType() == I_SLICE)  ? 2 : 1;
+      int bestLicIdc = 0;
+      Distortion bestLicCost = MAX_UINT64;
+      for (int licIdc = 0; licIdc < ibcLicLoopNum; licIdc++)
+      {
+        pu.cu->ibcFilterFlag = licIdc > 0 ? true: false;
+#endif
+
     pu.cu->imv = IMV_FPEL;
     pu.bv      = intBvList.mvList[i];
     pu.mv[0]   = intBvList.mvList[i];
@@ -4275,7 +4443,24 @@ Distortion InterSearch::xPredIBCFracPelSearch(PredictionUnit&              pu
         }
       }
 #endif
-
+#if JVET_AE0159_FIBC
+      if (pu.cu->ibcLicFlag )
+      {
+        uint8_t bestIdx = (tempBvCost[2] < tempBvCost[1] && tempBvCost[2] < tempBvCost[0]) ? 2 : (tempBvCost[1] < tempBvCost[0]) ? 1 : 0;
+        m_bestSrchCostIbcFilter.imvList[licIdc] = ImvMode(bestIdx);
+        m_bestSrchCostIbcFilter.mvpIdxList[licIdc] = tempMvpIdx[bestIdx];
+        m_bestSrchCostIbcFilter.costList[licIdc] = tempBvCost[bestIdx] + distParam.distFunc(distParam);
+        m_bestSrchCostIbcFilter.mvList[licIdc] = pu.mv[0];
+        Distortion currCost = licIdc == 0 ? Distortion(m_bestSrchCostIbcFilter.costList[licIdc] * 0.95) : m_bestSrchCostIbcFilter.costList[licIdc];
+        if (currCost < bestLicCost)
+        {
+          bestLicIdc = licIdc;
+          bestLicCost = currCost;
+        }
+      }
+      else 
+      {
+#endif
       uint8_t bestIdx = (tempBvCost[2] < tempBvCost[1] && tempBvCost[2] < tempBvCost[0]) ? 2 : (tempBvCost[1] < tempBvCost[0]) ? 1 : 0;
       intBvList.imvList   [i] = ImvMode(bestIdx);
 #else
@@ -4302,7 +4487,22 @@ Distortion InterSearch::xPredIBCFracPelSearch(PredictionUnit&              pu
       intBvList.mvpIdxList[i] = tempMvpIdx[bestIdx];
       intBvList.costList  [i] = tempBvCost[bestIdx] + distParam.distFunc(distParam);
       intBvList.mvList    [i] = pu.mv[0];
+#if JVET_AE0159_FIBC
+    }
+#endif
+    }
+#if JVET_AE0159_FIBC
     }
+      if (pu.cu->ibcLicFlag)
+      {
+        intBvList.imvList[i] = m_bestSrchCostIbcFilter.imvList[bestLicIdc];
+        intBvList.mvpIdxList[i] = m_bestSrchCostIbcFilter.mvpIdxList[bestLicIdc];
+        intBvList.costList[i] = m_bestSrchCostIbcFilter.costList[bestLicIdc];
+        intBvList.mvList[i] = m_bestSrchCostIbcFilter.mvList[bestLicIdc];
+        intBvList.bvFilter[i] = bestLicIdc;
+      }
+  }
+#endif
   }
   distParam.cur.buf    = predBuf.Y().buf;
   distParam.cur.stride = predBuf.Y().stride;
@@ -4324,6 +4524,12 @@ Distortion InterSearch::xPredIBCFracPelSearch(PredictionUnit&              pu
 #if JVET_AA0070_RRIBC && JVET_AC0060_IBC_BVP_CLUSTER_RRIBC_BVD_SIGN_DERIV
     bool&       bestFlipMode = intBvList.bvFlipList[candIdx];
 #endif
+#if JVET_AE0159_FIBC
+    if (pu.cs->sps->getUseIbcFilter() && m_pcEncCfg->getIntraPeriod() == 1)
+    {
+      pu.cu->ibcFilterFlag = intBvList.bvFilter[candIdx];
+    }
+#endif
 
     pu.cu->imv = imv;
     const Mv  centerMv = bestMv;
@@ -4516,6 +4722,12 @@ Distortion InterSearch::xPredIBCFracPelSearch(PredictionUnit&              pu
 #if JVET_AC0060_IBC_BVP_CLUSTER_RRIBC_BVD_SIGN_DERIV
   pu.cu->rribcFlipType = intBvList.bvFlipList[bestCandIdx] ? pu.cu->rribcFlipType : 0;
 #endif
+#endif
+#if JVET_AE0159_FIBC
+  if (pu.cs->sps->getUseIbcFilter() && m_pcEncCfg->getIntraPeriod() == 1)
+  {
+    pu.cu->ibcFilterFlag = pu.cu->ibcLicFlag ? intBvList.bvFilter[bestCandIdx] : 0;
+  }
 #endif
 
   pu.cu->imv   = intBvList.imvList   [bestCandIdx];
diff --git a/source/Lib/EncoderLib/InterSearch.h b/source/Lib/EncoderLib/InterSearch.h
index 10b005c5e8798f3eb3bc8b7e4c74b621834399de..110bb6af18459586d22b91671337e3edaee930ec 100644
--- a/source/Lib/EncoderLib/InterSearch.h
+++ b/source/Lib/EncoderLib/InterSearch.h
@@ -413,6 +413,9 @@ struct SrchCostBv
 #if JVET_AA0070_RRIBC && JVET_AC0060_IBC_BVP_CLUSTER_RRIBC_BVD_SIGN_DERIV
   bool       bvFlipList[capacity];
 #endif
+#if JVET_AE0159_FIBC
+  bool       bvFilter[capacity];
+#endif
 
   SrchCostBv()
   : cnt                 (0)
@@ -430,6 +433,9 @@ struct SrchCostBv
     if (resetHistoryMv)
     {
       mvList[maxSize].setZero();
+#if JVET_AE0159_FIBC
+      bvFilter[maxSize] = false;
+#endif
     }
   }
 
@@ -635,6 +641,9 @@ private:
   PelStorage      m_tmpStorageCUflipV;
 public:
   SrchCostIntBv   m_bestSrchCostIntBv;
+#if JVET_AE0159_FIBC
+  SrchCostIntBv   m_bestSrchCostIbcFilter;
+#endif
 private:
 #endif
   PelStorage      m_tmpAffiStorage;
diff --git a/source/Lib/EncoderLib/VLCWriter.cpp b/source/Lib/EncoderLib/VLCWriter.cpp
index a101845d880e5a0b5df33bf6b16c454425fb2f0a..75784221cd2c3441a2b270bc0d1c4e445248d3ab 100644
--- a/source/Lib/EncoderLib/VLCWriter.cpp
+++ b/source/Lib/EncoderLib/VLCWriter.cpp
@@ -1752,6 +1752,9 @@ void HLSWriter::codeSPS( const SPS* pcSPS )
 #if JVET_AC0112_IBC_LIC
     WRITE_FLAG( pcSPS->getUseIbcLic() ? 1 : 0,                                          "sps_ibc_lic_enabled_flag" );
 #endif
+#if JVET_AE0159_FIBC
+    WRITE_FLAG( pcSPS->getUseIbcFilter() ? 1 : 0,                                       "sps_ibc_filter_enabled_flag" );
+#endif
 #if JVET_AE0094_IBC_NONADJACENT_SPATIAL_CANDIDATES
     WRITE_FLAG(pcSPS->getUseIbcNonAdjCand() ? 1 : 0, "sps_ibc_non_adjacent_spatial_candidates_enabled_flag");
 #endif