From c7ea678347e4300da6aee2dd184a3d0821cd2ecd Mon Sep 17 00:00:00 2001
From: Kai Zhang <zhangkai.video@bytedance.com>
Date: Tue, 13 Sep 2022 22:42:14 -0700
Subject: [PATCH] initial commitment of test 1.9

---
 source/App/EncoderApp/EncApp.cpp         |    3 +
 source/App/EncoderApp/EncAppCfg.cpp      |    6 +
 source/App/EncoderApp/EncAppCfg.h        |    3 +
 source/Lib/CommonLib/Buffer.cpp          |   32 +
 source/Lib/CommonLib/Buffer.h            |    5 +
 source/Lib/CommonLib/CommonDef.h         |    5 +
 source/Lib/CommonLib/Contexts.cpp        |   17 +
 source/Lib/CommonLib/Contexts.h          |    3 +
 source/Lib/CommonLib/IntraPrediction.cpp |  801 +++++++++-
 source/Lib/CommonLib/IntraPrediction.h   |   17 +
 source/Lib/CommonLib/Slice.cpp           |    3 +
 source/Lib/CommonLib/Slice.h             |    7 +
 source/Lib/CommonLib/TypeDef.h           |    1 +
 source/Lib/CommonLib/Unit.cpp            |   14 +
 source/Lib/CommonLib/Unit.h              |    4 +
 source/Lib/CommonLib/UnitTools.cpp       |   33 +
 source/Lib/CommonLib/UnitTools.h         |    4 +
 source/Lib/CommonLib/x86/IntraX86.h      | 1813 ++++++++++++++++++++++
 source/Lib/DecoderLib/CABACReader.cpp    |   13 +-
 source/Lib/DecoderLib/DecCu.cpp          |   18 +-
 source/Lib/DecoderLib/VLCReader.cpp      |    3 +
 source/Lib/EncoderLib/CABACWriter.cpp    |   10 +
 source/Lib/EncoderLib/EncCfg.h           |    8 +
 source/Lib/EncoderLib/EncLib.cpp         |    5 +
 source/Lib/EncoderLib/IntraSearch.cpp    |  125 +-
 source/Lib/EncoderLib/VLCWriter.cpp      |    4 +
 26 files changed, 2945 insertions(+), 12 deletions(-)

diff --git a/source/App/EncoderApp/EncApp.cpp b/source/App/EncoderApp/EncApp.cpp
index c392226f..26338055 100644
--- a/source/App/EncoderApp/EncApp.cpp
+++ b/source/App/EncoderApp/EncApp.cpp
@@ -774,6 +774,9 @@ void EncApp::xInitLibCfg()
 #if TM_AMVP || TM_MRG || JVET_Z0084_IBC_TM || MULTI_PASS_DMVR
   m_cEncLib.setUseDMVDMode                                       ( m_DMVDMode );
 #endif
+#if SELF_AWARE_FILTERING_ESTIMATION
+  m_cEncLib.setUseSafeCCLM(m_SafeCCLMFlag);
+#endif
 #if JVET_AA0132_CONFIGURABLE_TM_TOOLS
   m_cEncLib.setTMToolsEnableFlag                                 ( m_tmToolsEnableFlag );
 #if TM_AMVP
diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp
index 1a66fc89..9f3841c0 100644
--- a/source/App/EncoderApp/EncAppCfg.cpp
+++ b/source/App/EncoderApp/EncAppCfg.cpp
@@ -1042,6 +1042,9 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
   ("TMMMVD",                                          m_useTMMMVD,                                       true, "Enable TM-MMVD (0:off, 1:on)  [default: on]")
 #endif
 #endif
+#if SELF_AWARE_FILTERING_ESTIMATION
+  ("SAFE_CCLM",                                       m_SafeCCLMFlag,                                     1, "SAFE CCLM (0:off, 1:on)  [default: on]" )
+#endif
 #if JVET_Z0056_GPM_SPLIT_MODE_REORDERING
   ("AltGPMSplitModeCode",                             m_altGPMSplitModeCode,                             true, "Enable alternative GPM split mode coding (0:off, 1:on)  [default: on]" )
 #endif
@@ -5248,6 +5251,9 @@ void EncAppCfg::xPrintParameter()
 #if TM_AMVP || TM_MRG || JVET_Z0084_IBC_TM || MULTI_PASS_DMVR
   msg( VERBOSE, "DMVD:%d ", m_DMVDMode );
 #endif
+#if SELF_AWARE_FILTERING_ESTIMATION
+  msg(VERBOSE, "SAFE_CCLM:%d ", m_SafeCCLMFlag);
+#endif
 #if JVET_AA0132_CONFIGURABLE_TM_TOOLS
   msg( VERBOSE, "EnableTMTools:%d ( ", m_tmToolsEnableFlag );
 #if TM_AMVP
diff --git a/source/App/EncoderApp/EncAppCfg.h b/source/App/EncoderApp/EncAppCfg.h
index 157621de..cc9b5d0a 100644
--- a/source/App/EncoderApp/EncAppCfg.h
+++ b/source/App/EncoderApp/EncAppCfg.h
@@ -394,6 +394,9 @@ protected:
 #if TM_AMVP || TM_MRG || JVET_Z0084_IBC_TM || MULTI_PASS_DMVR
   bool      m_DMVDMode;
 #endif
+#if SELF_AWARE_FILTERING_ESTIMATION
+  int      m_SafeCCLMFlag;
+#endif
 #if JVET_AA0132_CONFIGURABLE_TM_TOOLS
   bool      m_tmToolsEnableFlag;
 #if TM_AMVP
diff --git a/source/Lib/CommonLib/Buffer.cpp b/source/Lib/CommonLib/Buffer.cpp
index 336107db..37636f68 100644
--- a/source/Lib/CommonLib/Buffer.cpp
+++ b/source/Lib/CommonLib/Buffer.cpp
@@ -1668,6 +1668,38 @@ void AreaBuf<Pel>::linearTransform( const int scale, const int shift, const int
   }
 }
 
+#if SELF_AWARE_FILTERING_ESTIMATION
+void gLinearTransform(const Pel *src, Pel *dst, const int stride, const int width, const int height, const int scale,
+                     const int shift,
+                     const int offset, bool bClip, const ClpRng &clpRng)
+{
+#if ENABLE_SIMD_OPT_BUFFER && defined(TARGET_SIMD_X86)
+  if ((width & 7) == 0)
+  {
+    g_pelBufOP.linTf8(src, stride, dst, stride, width, height, scale, shift, offset, clpRng, bClip);
+  }
+  else if ((width & 3) == 0)
+  {
+    g_pelBufOP.linTf4(src, stride, dst, stride, width, height, scale, shift, offset, clpRng, bClip);
+  }
+#endif
+  else
+  {
+#define LINTF_OP(ADDR)                                                                                                 \
+  dst[ADDR] = (Pel) bClip ? ClipPel(rightShift(scale * src[ADDR], shift) + offset, clpRng)                             \
+                          : (rightShift(scale * src[ADDR], shift) + offset)
+#define LINTF_INC                                                                                                      \
+  src += stride;                                                                                                       \
+  dst += stride;
+
+    SIZE_AWARE_PER_EL_OP(LINTF_OP, LINTF_INC);
+
+#undef LINTF_OP
+#undef LINTF_INC
+  }
+}
+#endif
+
 #if ENABLE_SIMD_OPT_BUFFER && defined(TARGET_SIMD_X86)
 template<>
 void AreaBuf<Pel>::subtract( const Pel val )
diff --git a/source/Lib/CommonLib/Buffer.h b/source/Lib/CommonLib/Buffer.h
index a0e6f1f3..bbfc5d01 100644
--- a/source/Lib/CommonLib/Buffer.h
+++ b/source/Lib/CommonLib/Buffer.h
@@ -535,6 +535,11 @@ void AreaBuf<T>::linearTransform( const int scale, const int shift, const int of
 template<>
 void AreaBuf<Pel>::linearTransform( const int scale, const int shift, const int offset, bool bClip, const ClpRng& clpRng );
 
+#if SELF_AWARE_FILTERING_ESTIMATION
+void gLinearTransform(const Pel *src, Pel *dst, const int stride, const int width, const int height, const int scale,
+                     const int shift, const int offset, bool bClip, const ClpRng &clpRng);
+#endif
+
 template<typename T>
 void AreaBuf<T>::toLast( const ClpRng& clpRng )
 {
diff --git a/source/Lib/CommonLib/CommonDef.h b/source/Lib/CommonLib/CommonDef.h
index 898abd3b..6d00b8bd 100644
--- a/source/Lib/CommonLib/CommonDef.h
+++ b/source/Lib/CommonLib/CommonDef.h
@@ -1131,6 +1131,11 @@ static const int TEMP_CABAC_BUFFER_SIZE =                        5;
 static const int ADJUSTMENT_RANGE =                              7;
 #endif
 
+#if SELF_AWARE_FILTERING_ESTIMATION
+static const int CCLM_LUMA_EXT    =                              1;
+static const int CCLM_LUMA_EXT_MEM =                             8;
+static const int MAX_FILTER_NUM   =                              32;
+#endif
 #if JVET_AA0096_MC_BOUNDARY_PADDING
 static const int MC_PAD_SIZE = 16;
 static const int PAD_MORE_TL = 1;
diff --git a/source/Lib/CommonLib/Contexts.cpp b/source/Lib/CommonLib/Contexts.cpp
index 78339e8f..eb215a2b 100644
--- a/source/Lib/CommonLib/Contexts.cpp
+++ b/source/Lib/CommonLib/Contexts.cpp
@@ -1447,6 +1447,23 @@ const CtxSet ContextSetCfg::CclmModeIdx = ContextSetCfg::addCtxSet
   { 131, },
   });
 
+#if SELF_AWARE_FILTERING_ESTIMATION
+const CtxSet ContextSetCfg::SafeCCLMFlag = ContextSetCfg::addCtxSet
+({
+  {  6,  },
+  {  6,  },
+  {  6,  },
+  { DWS, },
+  { DWS, },
+  { DWS, },
+  { DWE, },
+  { DWE, },
+  { DWE, },
+  { DWO, },
+  { DWO, },
+  });
+#endif
+
 const CtxSet ContextSetCfg::IntraChromaPredMode = ContextSetCfg::addCtxSet
 ({
   {  25, },
diff --git a/source/Lib/CommonLib/Contexts.h b/source/Lib/CommonLib/Contexts.h
index 19352e54..70a25331 100644
--- a/source/Lib/CommonLib/Contexts.h
+++ b/source/Lib/CommonLib/Contexts.h
@@ -366,6 +366,9 @@ public:
   static const CtxSet   DimdChromaMode;
 #endif
   static const CtxSet   ChromaFusionMode;
+#endif
+#if SELF_AWARE_FILTERING_ESTIMATION
+  static const CtxSet   SafeCCLMFlag;
 #endif
   static const CtxSet   MipFlag;
 #if JVET_V0130_INTRA_TMP
diff --git a/source/Lib/CommonLib/IntraPrediction.cpp b/source/Lib/CommonLib/IntraPrediction.cpp
index b276406c..e71fae87 100644
--- a/source/Lib/CommonLib/IntraPrediction.cpp
+++ b/source/Lib/CommonLib/IntraPrediction.cpp
@@ -104,8 +104,13 @@ IntraPrediction::IntraPrediction()
 #if JVET_W0123_TIMD_FUSION
   m_timdSatdCost = nullptr;
 #endif
+#if SELF_AWARE_FILTERING_ESTIMATION
+  m_piTemp[0] = m_piTemp[1] = m_piTemp[2] = nullptr;
+  m_pMdlmTemp[0] = m_pMdlmTemp[1] = m_pMdlmTemp[2] = nullptr;
+#else
   m_piTemp = nullptr;
   m_pMdlmTemp = nullptr;
+#endif
 #if JVET_AA0126_GLM
   for (int i = 0; i < NUM_GLM_IDC; i++)
   {
@@ -145,10 +150,21 @@ void IntraPrediction::destroy()
 #if JVET_W0123_TIMD_FUSION
   delete m_timdSatdCost;
 #endif
+#if SELF_AWARE_FILTERING_ESTIMATION
+  delete m_piTemp[0];
+  delete m_piTemp[1];
+  delete m_piTemp[2];
+  m_piTemp[0] = m_piTemp[1] = m_piTemp[2] = nullptr;
+  delete m_pMdlmTemp[0];
+  delete m_pMdlmTemp[1];
+  delete m_pMdlmTemp[2];
+  m_pMdlmTemp[0] = m_pMdlmTemp[1] = m_pMdlmTemp[2] = nullptr;
+#else
   delete[] m_piTemp;
   m_piTemp = nullptr;
   delete[] m_pMdlmTemp;
   m_pMdlmTemp = nullptr;
+#endif
 #if JVET_AA0126_GLM
   for (int i = 0; i < NUM_GLM_IDC; i++)
   {
@@ -240,6 +256,32 @@ void IntraPrediction::init(ChromaFormat chromaFormatIDC, const unsigned bitDepth
     m_timdSatdCost = new RdCost;
   }
 #endif
+#if SELF_AWARE_FILTERING_ESTIMATION
+  if (m_piTemp[0] == nullptr)
+  {
+      m_piTemp[0] = new Pel[(MAX_CU_SIZE + CCLM_LUMA_EXT_MEM) * (MAX_CU_SIZE + CCLM_LUMA_EXT_MEM)];
+  }
+  if (m_piTemp[1] == nullptr)
+  {
+      m_piTemp[1] = new Pel[(MAX_CU_SIZE + CCLM_LUMA_EXT_MEM) * (MAX_CU_SIZE + CCLM_LUMA_EXT_MEM)];
+  }
+  if (m_piTemp[2] == nullptr)
+  {
+      m_piTemp[2] = new Pel[(MAX_CU_SIZE + CCLM_LUMA_EXT_MEM) * (MAX_CU_SIZE + CCLM_LUMA_EXT_MEM)];
+  }
+  if (m_pMdlmTemp[0] == nullptr)
+  {
+      m_pMdlmTemp[0] = new Pel[(2 * MAX_CU_SIZE + CCLM_LUMA_EXT_MEM)*(2 * MAX_CU_SIZE + CCLM_LUMA_EXT_MEM)];//MDLM will use top-above and left-below samples.
+  }
+  if (m_pMdlmTemp[1] == nullptr)
+  {
+      m_pMdlmTemp[1] = new Pel[(2 * MAX_CU_SIZE + CCLM_LUMA_EXT_MEM) * (2 * MAX_CU_SIZE + CCLM_LUMA_EXT_MEM)];//MDLM will use top-above and left-below samples.
+  }
+  if (m_pMdlmTemp[2] == nullptr)
+  {
+      m_pMdlmTemp[2] = new Pel[(2 * MAX_CU_SIZE + CCLM_LUMA_EXT_MEM) * (2 * MAX_CU_SIZE + CCLM_LUMA_EXT_MEM)];//MDLM will use top-above and left-below samples.
+  }
+#else
   if (m_piTemp == nullptr)
   {
     m_piTemp = new Pel[(MAX_CU_SIZE + 1) * (MAX_CU_SIZE + 1)];
@@ -248,6 +290,7 @@ void IntraPrediction::init(ChromaFormat chromaFormatIDC, const unsigned bitDepth
   {
     m_pMdlmTemp = new Pel[(2 * MAX_CU_SIZE + 1)*(2 * MAX_CU_SIZE + 1)];//MDLM will use top-above and left-below samples.
   }
+#endif
 #if JVET_AA0126_GLM
   for (int i = 0; i < NUM_GLM_IDC; i++)
   {
@@ -274,6 +317,9 @@ void IntraPrediction::init(ChromaFormat chromaFormatIDC, const unsigned bitDepth
 #if JVET_W0123_TIMD_FUSION && INTRA_TRANS_ENC_OPT
   m_timdBlending = timdBlending;
 #endif
+#if SELF_AWARE_FILTERING_ESTIMATION && INTRA_TRANS_ENC_OPT
+  m_safeFilterSamples = xSafeFilterSamples;
+#endif
 #if JVET_V0130_INTRA_TMP
   unsigned int blkSize;
   if( m_pppTarPatch == NULL )
@@ -1176,6 +1222,9 @@ void IntraPrediction::predIntraChromaLM(const ComponentID compID, PelBuf &piPred
 {
   int  iLumaStride = 0;
   PelBuf Temp;
+#if SELF_AWARE_FILTERING_ESTIMATION
+  int buffIdx = PU::isSafeCCLM(pu) ? (int)compID : 0;
+#endif
 #if JVET_AA0126_GLM
   if (pu.glmIdc.isActive())
   {
@@ -1193,13 +1242,23 @@ void IntraPrediction::predIntraChromaLM(const ComponentID compID, PelBuf &piPred
   if ((intraDir == MDLM_L_IDX) || (intraDir == MDLM_T_IDX))
 #endif
   {
+#if SELF_AWARE_FILTERING_ESTIMATION
+    iLumaStride = 2 * MAX_CU_SIZE + CCLM_LUMA_EXT_MEM;
+    Temp = PelBuf(m_pMdlmTemp[buffIdx] + iLumaStride * CCLM_LUMA_EXT_MEM + CCLM_LUMA_EXT_MEM, iLumaStride, Size(chromaArea));
+#else
     iLumaStride = 2 * MAX_CU_SIZE + 1;
     Temp = PelBuf(m_pMdlmTemp + iLumaStride + 1, iLumaStride, Size(chromaArea));
+#endif
   }
   else
   {
+#if SELF_AWARE_FILTERING_ESTIMATION
+    iLumaStride = MAX_CU_SIZE + CCLM_LUMA_EXT_MEM;
+    Temp = PelBuf(m_piTemp[buffIdx] + iLumaStride * CCLM_LUMA_EXT_MEM + CCLM_LUMA_EXT_MEM, iLumaStride, Size(chromaArea));
+#else
     iLumaStride = MAX_CU_SIZE + 1;
     Temp = PelBuf(m_piTemp + iLumaStride + 1, iLumaStride, Size(chromaArea));
+#endif
   }
 #if JVET_AA0126_GLM
   }
@@ -1859,7 +1918,20 @@ void IntraPrediction::geneChromaFusionPred(const ComponentID compId, PelBuf &piP
 
   const CompArea &area = pu2.blocks[compId];
 
+#if SELF_AWARE_FILTERING_ESTIMATION
+  if (PU::isSafeCCLM(pu2))
+  {
+      int compIdx = (compId == COMPONENT_Cb ? 0 : 1);
+      pu2.filterIdxForCCLM[compIdx] = xDeriveCCLMFilter(compId, pu2, area);
+      xGetLumaRecPixels(pu2, area, (int)compId, pu2.filterIdxForCCLM[compIdx]);
+  }
+  else
+  {
+#endif
   xGetLumaRecPixels(pu2, area);
+#if SELF_AWARE_FILTERING_ESTIMATION
+  }
+#endif
   predIntraChromaLM(compId, predLmBuffer, pu2, area, pu2.intraDir[1]);
 
   Pel *pelPred = piPred.buf;
@@ -4994,7 +5066,582 @@ void IntraPrediction::xGetLumaRecPixelsGlmAll(const PredictionUnit &pu, CompArea
 #endif
 
 // LumaRecPixels
+#if SELF_AWARE_FILTERING_ESTIMATION
+uint32_t IntraPrediction::xCalculateSAFEcost(const PredictionUnit &pu, const ComponentID compID,
+                                             const CompArea &chromaArea, const CclmModel &cclmModel)
+{
+  uint32_t totalSAD = 0;
+
+  const SizeType cWidth  = chromaArea.width;
+  const SizeType cHeight = chromaArea.height;
+
+  CodingStructure  &cs = *(pu.cs);
+  const CodingUnit &cu = *(pu.cu);
+
+  const SPS         &sps           = *cs.sps;
+  const ChromaFormat nChromaFormat = sps.getChromaFormatIdc();
+
+  CHECK(CHROMA_420 != nChromaFormat, "SAFE-CCLM can only be applied to 4:2:0 format");
+
+  const bool aboveAvailable = cu.cs->getCU(cu.blocks[compID].pos().offset(0, -1), toChannelType(compID)) ? true : false;
+  const bool leftAvailable  = cu.cs->getCU(cu.blocks[compID].pos().offset(-1, 0), toChannelType(compID)) ? true : false;
+
+  Pel *srcColor0, *curChroma0;
+  int  srcStride;
+
+  int buffIdx = (int) compID;
+
+  PelBuf temp;
+  srcStride = MAX_CU_SIZE + CCLM_LUMA_EXT_MEM;
+  temp = PelBuf(m_piTemp[buffIdx] + CCLM_LUMA_EXT_MEM * srcStride + CCLM_LUMA_EXT_MEM, srcStride, Size(chromaArea));
+
+  srcColor0 = temp.bufAt(0, 0);
+
+  PelBuf chromaReco = cs.picture->getRecoBuf(chromaArea);
+
+  Pel      *curChromaBuf = chromaReco.buf;
+  const int curStride    = chromaReco.stride;
+
+  Pel *src = srcColor0 - srcStride;
+
+  if (aboveAvailable)
+  {
+    curChroma0 = curChromaBuf - curStride;
+    src        = srcColor0 - srcStride;
+    Pel predV[128];
+    gLinearTransform(src, predV, srcStride, cWidth, 1, cclmModel.a, cclmModel.shift, cclmModel.b, true,
+                     pu.cs->slice->clpRng(compID));
+    for (int pos = 0; pos < cWidth; pos++)
+    {
+      // Pel predChroma =
+      //   ClipPel(rightShift(cclmModel.a * src[pos], cclmModel.shift) + cclmModel.b, pu.cs->slice->clpRng(compID));
+      // totalSAD += abs(predChroma - curChroma0[pos]);
+      totalSAD += abs(predV[pos] - curChroma0[pos]);
+#if CCLM_LUMA_EXT == 2
+      predChroma = ClipPel(rightShift(cclmModel.a * src[pos - srcStride], cclmModel.shift) + cclmModel.b,
+                           pu.cs->slice->clpRng(compID));
+      totalSAD += abs(predChroma - curChroma0[pos - curStride]);
+#endif
+    }
+  }
+
+  if (leftAvailable)
+  {
+    curChroma0 = curChromaBuf - 1;
+
+    src = srcColor0 - 1;
+    for (int pos = 0; pos < cHeight; pos++)
+    {
+      Pel predChroma = ClipPel(rightShift(cclmModel.a * src[pos * srcStride], cclmModel.shift) + cclmModel.b,
+                               pu.cs->slice->clpRng(compID));
+      totalSAD += abs(predChroma - curChroma0[pos * curStride]);
+#if CCLM_LUMA_EXT == 2
+      predChroma = ClipPel(rightShift(cclmModel.a * src[pos * srcStride - 1], cclmModel.shift) + cclmModel.b,
+                           pu.cs->slice->clpRng(compID));
+      totalSAD += abs(predChroma - curChroma0[pos * curStride - 1]);
+#endif
+    }
+  }
+  return totalSAD;
+}
+
+int IntraPrediction::xDeriveCCLMFilter(const ComponentID compID, const PredictionUnit &pu, const CompArea &chromaArea)
+{
+  uint32_t bestCost       = MAX_UINT;
+  int      bestFilterType = -1;
+
+  CHECK(compID == COMPONENT_Y, "");
+  bool curSafeFlag                              = pu.safeCCLMFlag;
+  int  curChromaMode                            = pu.intraDir[1];
+  const_cast<PredictionUnit &>(pu).safeCCLMFlag = true;
+  const_cast<PredictionUnit &>(pu).intraDir[1]  = LM_CHROMA_IDX;
+
+  for (int testFilter = 1; testFilter <= MAX_FILTER_NUM; testFilter++)
+  {
+    CclmModel cclmModel;
+    xGetLumaRecPixels(pu, chromaArea, (int) compID, testFilter, false);
+    xGetLMParametersLMS(pu, compID, chromaArea, cclmModel);
+
+    uint32_t curCost = xCalculateSAFEcost(pu, compID, chromaArea, cclmModel);
+    if (curCost < bestCost)
+    {
+      bestCost       = curCost;
+      bestFilterType = testFilter;
+    }
+  }
+
+  // printf( "%d: %d\n", compID, bestFilterType);
+  const_cast<PredictionUnit &>(pu).intraDir[1]  = curChromaMode;
+  const_cast<PredictionUnit &>(pu).safeCCLMFlag = curSafeFlag;
+  return bestFilterType;
+}
+
+void IntraPrediction::xSafeFilterSamples(int filterType, const Pel *piSrc, Pel *pDst, int filterWidth, int filterHeight,
+                                         int iDstStride, int iRecStride, int iRecStride2)
+{
+  if (filterType == 0)
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        int s = 4;
+        s += piSrc[2 * i] * 2;
+        s += piSrc[2 * i + 1];
+        s += piSrc[2 * i - 1];
+        s += piSrc[2 * i + iRecStride] * 2;
+        s += piSrc[2 * i + 1 + iRecStride];
+        s += piSrc[2 * i + iRecStride - 1];
+        pDst[i] = s >> 3;
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else if (filterType == 1)
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        int s = 2;
+        s += piSrc[2 * i];
+        s += piSrc[2 * i + 1];
+        s += piSrc[2 * i + iRecStride];
+        s += piSrc[2 * i + 1 + iRecStride];
+        pDst[i] = s >> 2;
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else if (filterType == 2)   // North
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        int s = 1;
+        s += piSrc[2 * i];
+        s += piSrc[2 * i + 1];
+        pDst[i] = s >> 1;
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else if (filterType == 3)   // East
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        int s = 1;
+        s += piSrc[2 * i + 1];
+        s += piSrc[2 * i + 1 + iRecStride];
+        pDst[i] = s >> 1;
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else if (filterType == 4)   // South
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        int s = 1;
+        s += piSrc[2 * i + iRecStride];
+        s += piSrc[2 * i + 1 + iRecStride];
+        pDst[i] = s >> 1;
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else if (filterType == 5)   // Horizontal Gradient
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        pDst[i] =
+          ((piSrc[2 * i] + piSrc[2 * i + iRecStride]) - (piSrc[2 * i + 1] + piSrc[2 * i + iRecStride + 1]) + 1) >> 1;
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else if (filterType == 6)   // vertical Gradient
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        int s1 = 0;
+        s1 += piSrc[2 * i] * 2;
+        s1 += piSrc[2 * i + 1];
+        s1 += piSrc[2 * i - 1];
+        int s2 = 0;
+        s2 += piSrc[2 * i + iRecStride] * 2;
+        s2 += piSrc[2 * i + 1 + iRecStride];
+        s2 += piSrc[2 * i + iRecStride - 1];
+        pDst[i] = (s1 - s2 + 2) >> 2;
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else if (filterType == 7)
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        pDst[i] = (piSrc[2 * i] - piSrc[2 * i + iRecStride] + 1) >> 1;
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else if (filterType == 8)
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        pDst[i] = (piSrc[2 * i + 1] - piSrc[2 * i + iRecStride + 1] + 1) >> 1;
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else if (filterType == 9)
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        pDst[i] = piSrc[2 * i];
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else if (filterType == 10)
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        pDst[i] = piSrc[2 * i + 1];
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else if (filterType == 11)
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        pDst[i] = piSrc[2 * i + iRecStride];
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else if (filterType == 12)
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        pDst[i] = piSrc[2 * i + 1 + iRecStride];
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else if (filterType == 13)
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        pDst[i] = piSrc[2 * i - 1];
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else if (filterType == 14)   // South
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        pDst[i] = piSrc[2 * i - 1 + iRecStride];
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else if (filterType == 15)   // East
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        int s = 2;
+        s += piSrc[2 * i] * 2;
+        s += piSrc[2 * i + 1];
+        s += piSrc[2 * i - 1];
+        pDst[i] = s >> 2;
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else if (filterType == 16)   // South
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        int s = 2;
+        s += piSrc[2 * i + iRecStride] * 2;
+        s += piSrc[2 * i + 1 + iRecStride];
+        s += piSrc[2 * i + iRecStride - 1];
+        pDst[i] = s >> 2;
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else if (filterType == 17)
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        pDst[i] =
+          ((piSrc[2 * i] + piSrc[2 * i + iRecStride]) - (piSrc[2 * i - 1] + piSrc[2 * i + iRecStride - 1]) + 1) >> 1;
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else if (filterType == 18)
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        pDst[i] =
+          ((piSrc[2 * i + 1] + piSrc[2 * i + iRecStride + 1]) - (piSrc[2 * i - 1] + piSrc[2 * i + iRecStride - 1]) + 1)
+          >> 1;
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else if (filterType == 19)
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        pDst[i] =
+          (3 * (piSrc[2 * i] + piSrc[2 * i + iRecStride]) - (piSrc[2 * i + 1] + piSrc[2 * i + iRecStride + 1]) + 1)
+          >> 1;
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else if (filterType == 20)
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        int s1 = 0;
+        s1 += piSrc[2 * i] * 2;
+        s1 += piSrc[2 * i + 1];
+        s1 += piSrc[2 * i - 1];
+        int s2 = 0;
+        s2 += piSrc[2 * i + iRecStride] * 2;
+        s2 += piSrc[2 * i + 1 + iRecStride];
+        s2 += piSrc[2 * i + iRecStride - 1];
+        pDst[i] = (3 * s1 - s2 + 2) >> 2;
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else if (filterType == 21)
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        pDst[i] = (piSrc[2 * i] - piSrc[2 * i + iRecStride + 1] + 1) >> 1;
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else if (filterType == 22)
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        pDst[i] = (piSrc[2 * i + 1] - piSrc[2 * i + iRecStride] + 1) >> 1;
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else if (filterType == 23)
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        pDst[i] = (piSrc[2 * i] - piSrc[2 * i + iRecStride - 1] + 1) >> 1;
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else if (filterType == 24)
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        pDst[i] = (piSrc[2 * i - 1] - piSrc[2 * i + iRecStride] + 1) >> 1;
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else if (filterType == 25)
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        pDst[i] = (piSrc[2 * i] + piSrc[2 * i + iRecStride]) * 2 - piSrc[2 * i + 1] - piSrc[2 * i - 1]
+                  - piSrc[2 * i + 1 + iRecStride] - piSrc[2 * i + iRecStride - 1];
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else if (filterType == 26)
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        pDst[i] = piSrc[2 * i + 1] - piSrc[2 * i + iRecStride - 1];
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else if (filterType == 27)   // Horizontal Gradient
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        pDst[i] = piSrc[2 * i - 1] - piSrc[2 * i + iRecStride + 1];
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else if (filterType == 28)   // vertical Gradient
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        int s = 2;
+        s += piSrc[2 * i + 1];
+        s += piSrc[2 * i - 1];
+        s += piSrc[2 * i + 1 + iRecStride];
+        s += piSrc[2 * i + iRecStride - 1];
+        pDst[i] = s >> 2;
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else if (filterType == 29)
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        pDst[i] = piSrc[2 * i] + piSrc[2 * i - 1] + piSrc[2 * i - 1 + iRecStride] - piSrc[2 * i + iRecStride];
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else if (filterType == 30)
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        pDst[i] = piSrc[2 * i] + piSrc[2 * i + 1] + piSrc[2 * i + 1 + iRecStride] - piSrc[2 * i + iRecStride];
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else if (filterType == 31)
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        pDst[i] = piSrc[2 * i - 1] + piSrc[2 * i + 1] + piSrc[2 * i + iRecStride];
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else if (filterType == 32)
+  {
+    for (int j = 0; j < filterHeight; j++)
+    {
+      for (int i = 0; i < filterWidth; i++)
+      {
+        pDst[i] = piSrc[2 * i + iRecStride - 1] + piSrc[2 * i + iRecStride + 1] + piSrc[2 * i];
+      }
+      pDst += iDstStride;
+      piSrc += iRecStride2;
+    }
+  }
+  else
+  {
+    THROW("Invalid filter index!");
+  }
+}
+
+void IntraPrediction::xGetLumaRecPixels(const PredictionUnit& pu, CompArea chromaArea, int buffIdx, int filterType, bool bFilterInside)
+#else  
 void IntraPrediction::xGetLumaRecPixels(const PredictionUnit &pu, CompArea chromaArea)
+#endif
 {
 #if JVET_AA0057_CCCM
   if ( pu.cccmFlag )
@@ -5034,18 +5681,37 @@ void IntraPrediction::xGetLumaRecPixels(const PredictionUnit &pu, CompArea chrom
   {
 #endif
 #if MMLM
+#if SELF_AWARE_FILTERING_ESTIMATION
+    CHECK(buffIdx < 0 || buffIdx > 2, "Invalide BuffIdx");
+    if (bFilterInside && ((curChromaMode == MDLM_L_IDX) || (curChromaMode == MDLM_T_IDX) || (curChromaMode == MMLM_L_IDX) || (curChromaMode == MMLM_T_IDX)))
+#else
   if ((curChromaMode == MDLM_L_IDX) || (curChromaMode == MDLM_T_IDX) || (curChromaMode == MMLM_L_IDX) || (curChromaMode == MMLM_T_IDX))
+#endif
+#else
+#if SELF_AWARE_FILTERING_ESTIMATION
+    if (bFilterInside && ((curChromaMode == MDLM_L_IDX) || (curChromaMode == MDLM_T_IDX)))
 #else
   if ((curChromaMode == MDLM_L_IDX) || (curChromaMode == MDLM_T_IDX))
+#endif
 #endif
   {
+#if SELF_AWARE_FILTERING_ESTIMATION
+    iDstStride = 2 * MAX_CU_SIZE + CCLM_LUMA_EXT_MEM;
+    pDst0 = m_pMdlmTemp[buffIdx] + CCLM_LUMA_EXT_MEM * iDstStride + CCLM_LUMA_EXT_MEM;
+#else
     iDstStride = 2 * MAX_CU_SIZE + 1;
     pDst0 = m_pMdlmTemp + iDstStride + 1;
+#endif
   }
   else
   {
+#if SELF_AWARE_FILTERING_ESTIMATION
+    iDstStride = MAX_CU_SIZE + CCLM_LUMA_EXT_MEM;
+    pDst0 = m_piTemp[buffIdx] + CCLM_LUMA_EXT_MEM * iDstStride + CCLM_LUMA_EXT_MEM; //MMLM_SAMPLE_NEIGHBOR_LINES;
+#else
     iDstStride = MAX_CU_SIZE + 1;
     pDst0 = m_piTemp + iDstStride + 1; //MMLM_SAMPLE_NEIGHBOR_LINES;
+#endif
   }
 #if JVET_AA0126_GLM
   }
@@ -5126,6 +5792,41 @@ void IntraPrediction::xGetLumaRecPixels(const PredictionUnit &pu, CompArea chrom
   Pel const* piSrc = nullptr;
 
   bool isFirstRowOfCtu = (lumaArea.y & ((pu.cs->sps)->getCTUSize() - 1)) == 0;
+#if SELF_AWARE_FILTERING_ESTIMATION
+  isFirstRowOfCtu = false;
+  Pel tempLumaBuf[(MAX_CU_SIZE + (CCLM_LUMA_EXT << 1) + 1) * (MAX_CU_SIZE + (CCLM_LUMA_EXT << 1) + 1)];
+  int  iRecPadStride  = MAX_CU_SIZE + (CCLM_LUMA_EXT << 1) + 1;
+  int  iRecPadStride2 = iRecPadStride << logSubHeightC;
+  Pel *piRecPad0      = tempLumaBuf + CCLM_LUMA_EXT * iRecPadStride2 + CCLM_LUMA_EXT * (logSubWidthC + 1) + 1;
+  //Note: not consider slice or tile partitioning
+  if ((((filterType > 0) || (pu.chromaFormat == CHROMA_420 && !pu.glmIdc.isActive())))
+      && ((int) lumaArea.x - ((CCLM_LUMA_EXT) << logSubWidthC) - 1 < 0))
+  {
+      Pel *piSrcPad = piRecPad0;
+      piSrc = pRecSrc0;
+      int copyH = lumaArea.height;
+      if (aboveIsAvailable)
+      {
+          copyH += CCLM_LUMA_EXT << logSubHeightC;
+          piSrc = pRecSrc0 - CCLM_LUMA_EXT * iRecStride2;
+          piSrcPad = piRecPad0 - CCLM_LUMA_EXT * iRecPadStride2;
+      }
+      // The full W should be lumaW + ((CCLM_LUMA_EXT )<< iScaleX) + 1
+      int availableW = lumaArea.x + lumaArea.width;
+      int unavailW = (CCLM_LUMA_EXT << logSubWidthC) + 1 - lumaArea.x;
+      int availNeiW = lumaArea.x;
+      for (int j = 0; j < copyH; j++)
+      {
+          memcpy(piSrcPad - availNeiW, piSrc - availNeiW, sizeof(Pel) * availableW);
+          for (int i = 1; i <= unavailW; i++)
+          {
+              piSrcPad[-availNeiW - i] = piSrcPad[-availNeiW];
+          }
+          piSrcPad += iRecPadStride;
+          piSrc += iRecStride;
+      }
+  }
+#endif
 
   if (aboveIsAvailable)
   {
@@ -5139,6 +5840,27 @@ void IntraPrediction::xGetLumaRecPixels(const PredictionUnit &pu, CompArea chrom
     {
       addedAboveRight = avaiAboveRightUnits*chromaUnitWidth;
     }
+#if SELF_AWARE_FILTERING_ESTIMATION
+    if (filterType > 0)
+    {
+      if (((int) lumaArea.x - ((CCLM_LUMA_EXT) << logSubWidthC) - 1 < 0))
+      {
+        pRecSrc0    = piRecPad0;
+        iRecStride  = iRecPadStride;
+        iRecStride2 = iRecPadStride2;
+      }
+      piSrc = pRecSrc0 - CCLM_LUMA_EXT * iRecStride2;
+      pDst  = pDst0 - CCLM_LUMA_EXT * iDstStride;
+
+#if INTRA_TRANS_ENC_OPT
+      m_safeFilterSamples(filterType, piSrc, pDst, uiCWidth, CCLM_LUMA_EXT, iDstStride, iRecStride, iRecStride2);
+#else
+      xSafeFilterSamples(filterType, piSrc, pDst, uiCWidth, CCLM_LUMA_EXT, iDstStride, iRecStride, iRecStride2);
+#endif
+    }
+    else
+    {
+#endif
     for (int i = 0; i < uiCWidth + addedAboveRight; i++)
     {
       const bool leftPadding = i == 0 && !leftIsAvailable;
@@ -5195,6 +5917,9 @@ void IntraPrediction::xGetLumaRecPixels(const PredictionUnit &pu, CompArea chrom
                      piSrc[2 * i + iRecStride - l], piSrc[2 * i + iRecStride    ], piSrc[2 * i + iRecStride + 1] };
         pDst[i] = xGlmGetLumaVal(s, c, glmIdc - 1, pDst[i]);
       }
+#endif
+#if SELF_AWARE_FILTERING_ESTIMATION
+      }
 #endif
     }
   }
@@ -5213,7 +5938,26 @@ void IntraPrediction::xGetLumaRecPixels(const PredictionUnit &pu, CompArea chrom
     {
       addedLeftBelow = avaiLeftBelowUnits*chromaUnitHeight;
     }
-
+#if SELF_AWARE_FILTERING_ESTIMATION
+    if (filterType > 0)
+    {
+      if (((int) lumaArea.x - ((CCLM_LUMA_EXT) << logSubWidthC) - 1 < 0))
+      {
+        pRecSrc0    = piRecPad0;
+        iRecStride  = iRecPadStride;
+        iRecStride2 = iRecPadStride2;
+      }
+      piSrc = pRecSrc0 - CCLM_LUMA_EXT * (1 + logSubWidthC);
+      pDst  = pDst0 - CCLM_LUMA_EXT;
+#if INTRA_TRANS_ENC_OPT
+      m_safeFilterSamples(filterType, piSrc, pDst, CCLM_LUMA_EXT, uiCHeight, iDstStride, iRecStride, iRecStride2);
+#else
+      xSafeFilterSamples(filterType, piSrc, pDst, CCLM_LUMA_EXT, uiCHeight, iDstStride, iRecStride, iRecStride2);
+#endif
+    }
+    else
+    {
+#endif
     for (int j = 0; j < uiCHeight + addedLeftBelow; j++)
     {
       if (pu.chromaFormat == CHROMA_444)
@@ -5263,7 +6007,33 @@ void IntraPrediction::xGetLumaRecPixels(const PredictionUnit &pu, CompArea chrom
       piSrc += iRecStride2;
       pDst  += iDstStride;
     }
+#if SELF_AWARE_FILTERING_ESTIMATION
   }
+#endif
+    }
+#if SELF_AWARE_FILTERING_ESTIMATION
+    if (!bFilterInside)
+    {
+        return;
+    }
+    if ((filterType > 0) || (pu.chromaFormat == CHROMA_420 && !pu.glmIdc.isActive()))
+    {
+      if (((int) lumaArea.x - ((CCLM_LUMA_EXT) << logSubWidthC) - 1 < 0))
+      {
+        pRecSrc0    = piRecPad0;
+        iRecStride  = iRecPadStride;
+        iRecStride2 = iRecPadStride2;
+      }
+      piSrc = pRecSrc0;
+      pDst  = pDst0;
+#if INTRA_TRANS_ENC_OPT
+      m_safeFilterSamples(filterType, piSrc, pDst, uiCWidth, uiCHeight, iDstStride, iRecStride, iRecStride2);
+#else
+      xSafeFilterSamples(filterType, piSrc, pDst, uiCWidth, uiCHeight, iDstStride, iRecStride, iRecStride2);
+#endif
+      return;
+    }
+#endif
 
   // inner part from reconstructed picture buffer
   for( int j = 0; j < uiCHeight; j++ )
@@ -5396,7 +6166,11 @@ void IntraPrediction::xGetLMParameters(const PredictionUnit &pu, const Component
   Pel *srcColor0, *curChroma0;
   int srcStride;
 
-  PelBuf temp;
+#if SELF_AWARE_FILTERING_ESTIMATION
+    int buffIdx = PU::isSafeCCLM(pu) ? (int)compID : 0;
+#endif
+
+    PelBuf temp;
 #if MMLM
   if ((curChromaMode == MDLM_L_IDX) || (curChromaMode == MDLM_T_IDX) || (curChromaMode == MMLM_L_IDX) || (curChromaMode == MMLM_T_IDX)
     || (m_encPreRDRun && curChromaMode == MMLM_CHROMA_IDX))
@@ -5404,13 +6178,23 @@ void IntraPrediction::xGetLMParameters(const PredictionUnit &pu, const Component
   if ((curChromaMode == MDLM_L_IDX) || (curChromaMode == MDLM_T_IDX))
 #endif
   {
+#if SELF_AWARE_FILTERING_ESTIMATION
+    srcStride = 2 * MAX_CU_SIZE + CCLM_LUMA_EXT_MEM;
+    temp = PelBuf(m_pMdlmTemp[buffIdx] + srcStride * CCLM_LUMA_EXT_MEM + CCLM_LUMA_EXT_MEM, srcStride, Size(chromaArea));
+#else
     srcStride = 2 * MAX_CU_SIZE + 1;
     temp = PelBuf(m_pMdlmTemp + srcStride + 1, srcStride, Size(chromaArea));
+#endif
   }
   else
   {
+#if SELF_AWARE_FILTERING_ESTIMATION
+    srcStride = MAX_CU_SIZE + CCLM_LUMA_EXT_MEM;
+    temp = PelBuf(m_piTemp[buffIdx] + srcStride * CCLM_LUMA_EXT_MEM + CCLM_LUMA_EXT_MEM, srcStride, Size(chromaArea));
+#else
     srcStride = MAX_CU_SIZE + 1;
     temp        = PelBuf(m_piTemp + srcStride + 1, srcStride, Size(chromaArea));
+#endif
   }
   srcColor0 = temp.bufAt(0, 0);
   curChroma0 = getPredictorPtr(compID);
@@ -6259,6 +7043,9 @@ void IntraPrediction::xGetLMParametersLMS(const PredictionUnit &pu, const Compon
     avaiAboveUnits = tuWidthInUnits;
     avaiAboveRightUnits = isAboveRightAvailable(cu, CHANNEL_TYPE_CHROMA, chromaArea.topRightComp(chromaArea.compID), aboveRightUnits, unitWidth, (neighborFlags + leftUnits + leftBelowUnits + aboveUnits + 1));
   }
+#if SELF_AWARE_FILTERING_ESTIMATION
+  int buffIdx = PU::isSafeCCLM(pu) ? (int)compID : 0;
+#endif
   Pel *srcColor0, *curChroma0;
   int srcStride;
 
@@ -6281,13 +7068,23 @@ void IntraPrediction::xGetLMParametersLMS(const PredictionUnit &pu, const Compon
   if ((curChromaMode == MDLM_L_IDX) || (curChromaMode == MDLM_T_IDX))
 #endif
   {
+#if SELF_AWARE_FILTERING_ESTIMATION
+    srcStride = 2 * MAX_CU_SIZE + CCLM_LUMA_EXT_MEM;
+    temp = PelBuf(m_pMdlmTemp[buffIdx] + srcStride * CCLM_LUMA_EXT_MEM + CCLM_LUMA_EXT_MEM, srcStride, Size(chromaArea));
+#else
     srcStride = 2 * MAX_CU_SIZE + 1;
     temp = PelBuf(m_pMdlmTemp + srcStride + 1, srcStride, Size(chromaArea));
+#endif
   }
   else
   {
+#if SELF_AWARE_FILTERING_ESTIMATION
+    srcStride = MAX_CU_SIZE + CCLM_LUMA_EXT_MEM;
+    temp = PelBuf(m_piTemp[buffIdx] + srcStride * CCLM_LUMA_EXT_MEM + CCLM_LUMA_EXT_MEM, srcStride, Size(chromaArea));
+#else
     srcStride = MAX_CU_SIZE + 1;
     temp = PelBuf(m_piTemp + srcStride + 1, srcStride, Size(chromaArea));
+#endif
   }
 #if JVET_AA0126_GLM
   }
diff --git a/source/Lib/CommonLib/IntraPrediction.h b/source/Lib/CommonLib/IntraPrediction.h
index 10bc05f7..df6bb0ee 100644
--- a/source/Lib/CommonLib/IntraPrediction.h
+++ b/source/Lib/CommonLib/IntraPrediction.h
@@ -217,8 +217,13 @@ private:
 
   IntraPredParam m_ipaParam;
 
+#if SELF_AWARE_FILTERING_ESTIMATION
+  Pel* m_piTemp[3];
+  Pel* m_pMdlmTemp[3]; // for MDLM mode
+#else
   Pel* m_piTemp;
   Pel* m_pMdlmTemp; // for MDLM mode
+#endif
 #if JVET_AA0126_GLM
   Pel* m_glmTempCb[NUM_GLM_IDC];
   Pel* m_glmTempCr[NUM_GLM_IDC];
@@ -377,7 +382,19 @@ public:
 
   // Cross-component Chroma
   void predIntraChromaLM(const ComponentID compID, PelBuf &piPred, const PredictionUnit &pu, const CompArea& chromaArea, int intraDir, bool createModel = true, CclmModel *cclmModelStored = nullptr);
+#if SELF_AWARE_FILTERING_ESTIMATION
+  uint32_t xCalculateSAFEcost(const PredictionUnit& pu, const ComponentID compID, const CompArea& chromaArea, const CclmModel& cclmModel);
+  int xDeriveCCLMFilter(const ComponentID compID, const PredictionUnit& pu, const CompArea& chromaArea);
+
+  static void xSafeFilterSamples(int filterType, const Pel* piSrc, Pel* pDst, int filterWidth, int filterHeight, int iDstStride, int iRecStride, int iRecStride2);
+#if INTRA_TRANS_ENC_OPT 
+  void (*m_safeFilterSamples)(int filterType, const Pel *piSrc, Pel *pDst, int filterWidth, int filterHeight,
+                                 int iDstStride, int iRecStride, int iRecStride2);
+#endif
+  void xGetLumaRecPixels(const PredictionUnit& pu, CompArea chromaArea, int buffIdx = 0, int filterType = 0, bool bFilterInside = true);
+#else  
   void xGetLumaRecPixels(const PredictionUnit &pu, CompArea chromaArea);
+#endif
 #if JVET_AA0126_GLM
   void xGetLumaRecPixelsGlmAll(const PredictionUnit &pu, CompArea chromaArea);
   Pel xGlmGetLumaVal    (const int s[6], const int c[6], const int glmIdx, const Pel val) const;
diff --git a/source/Lib/CommonLib/Slice.cpp b/source/Lib/CommonLib/Slice.cpp
index 11c4616b..5ffaa164 100644
--- a/source/Lib/CommonLib/Slice.cpp
+++ b/source/Lib/CommonLib/Slice.cpp
@@ -3497,6 +3497,9 @@ SPS::SPS()
 #if TM_AMVP || TM_MRG || JVET_Z0084_IBC_TM || MULTI_PASS_DMVR
  , m_DMVDMode                 ( false )
 #endif
+#if SELF_AWARE_FILTERING_ESTIMATION
+,m_SafeCCLMFlag(0)
+#endif
 #if JVET_AA0132_CONFIGURABLE_TM_TOOLS
 , m_tmToolsEnableFlag         ( false )
 #if TM_AMVP
diff --git a/source/Lib/CommonLib/Slice.h b/source/Lib/CommonLib/Slice.h
index 4e498324..910a3520 100644
--- a/source/Lib/CommonLib/Slice.h
+++ b/source/Lib/CommonLib/Slice.h
@@ -1526,6 +1526,9 @@ private:
 #if TM_AMVP || TM_MRG || JVET_Z0084_IBC_TM || MULTI_PASS_DMVR
   bool              m_DMVDMode;
 #endif
+#if SELF_AWARE_FILTERING_ESTIMATION
+  int8_t            m_SafeCCLMFlag;
+#endif
 #if JVET_AA0132_CONFIGURABLE_TM_TOOLS
   bool              m_tmToolsEnableFlag;
 #if TM_AMVP
@@ -2046,6 +2049,10 @@ void                    setCCALFEnabledFlag( bool b )
   void                    setUseIbcMbvd(bool b)                                                           { m_ibcMbvd = b; }
   bool                    getUseIbcMbvd() const                                                           { return m_ibcMbvd; }
 #endif
+#if SELF_AWARE_FILTERING_ESTIMATION
+  void                    setSafeCCLMFlag(int8_t b)                                                       { m_SafeCCLMFlag = b; }
+  int8_t                  getSafeCCLMFlag() const                                                         { return m_SafeCCLMFlag; }
+#endif
 #if TM_AMVP || TM_MRG || JVET_Z0084_IBC_TM || MULTI_PASS_DMVR
   void                    setUseDMVDMode(bool b)                                                          { m_DMVDMode = b; }
   bool                    getUseDMVDMode() const                                                          { return m_DMVDMode; }
diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index 60430c6f..9ab82254 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -55,6 +55,7 @@
 #define BASE_NORMATIVE                                    1
 #define TOOLS                                             1
 
+#define SELF_AWARE_FILTERING_ESTIMATION                   1
 
 #if BASE_ENCODER
 // Lossy encoder speedups
diff --git a/source/Lib/CommonLib/Unit.cpp b/source/Lib/CommonLib/Unit.cpp
index cb7507a4..9a9cf97d 100644
--- a/source/Lib/CommonLib/Unit.cpp
+++ b/source/Lib/CommonLib/Unit.cpp
@@ -641,6 +641,10 @@ void PredictionUnit::initData()
   intraDir[1] = PLANAR_IDX;
 #if JVET_Z0050_DIMD_CHROMA_FUSION
   isChromaFusion = false;
+#endif
+#if SELF_AWARE_FILTERING_ESTIMATION
+  filterIdxForCCLM[0] = filterIdxForCCLM[1] = 0;
+  safeCCLMFlag = 0;
 #endif
   mipTransposedFlag = false;
   multiRefIdx = 0;
@@ -772,6 +776,11 @@ PredictionUnit& PredictionUnit::operator=(const IntraPredictionData& predData)
   }
 #if JVET_Z0050_DIMD_CHROMA_FUSION
   isChromaFusion = predData.isChromaFusion;
+#endif
+#if SELF_AWARE_FILTERING_ESTIMATION
+  filterIdxForCCLM[0] = predData.filterIdxForCCLM[0];
+  filterIdxForCCLM[1] = predData.filterIdxForCCLM[1];
+  safeCCLMFlag = predData.safeCCLMFlag;
 #endif
   mipTransposedFlag = predData.mipTransposedFlag;
   multiRefIdx = predData.multiRefIdx;
@@ -907,6 +916,11 @@ PredictionUnit& PredictionUnit::operator=( const PredictionUnit& other )
   }
 #if JVET_Z0050_DIMD_CHROMA_FUSION
   isChromaFusion = other.isChromaFusion;
+#endif
+#if SELF_AWARE_FILTERING_ESTIMATION
+  filterIdxForCCLM[0] = other.filterIdxForCCLM[0];
+  filterIdxForCCLM[1] = other.filterIdxForCCLM[1];
+  safeCCLMFlag = other.safeCCLMFlag;
 #endif
   mipTransposedFlag = other.mipTransposedFlag;
   multiRefIdx = other.multiRefIdx;
diff --git a/source/Lib/CommonLib/Unit.h b/source/Lib/CommonLib/Unit.h
index 66f95218..ec641da8 100644
--- a/source/Lib/CommonLib/Unit.h
+++ b/source/Lib/CommonLib/Unit.h
@@ -427,6 +427,10 @@ struct IntraPredictionData
   uint8_t  intraDir[MAX_NUM_CHANNEL_TYPE];
 #if JVET_Z0050_DIMD_CHROMA_FUSION
   bool      isChromaFusion;
+#endif
+#if SELF_AWARE_FILTERING_ESTIMATION
+  uint8_t  filterIdxForCCLM[2];
+  uint8_t  safeCCLMFlag;
 #endif
   bool      mipTransposedFlag;
   int8_t    multiRefIdx;
diff --git a/source/Lib/CommonLib/UnitTools.cpp b/source/Lib/CommonLib/UnitTools.cpp
index 094995ed..f709b278 100644
--- a/source/Lib/CommonLib/UnitTools.cpp
+++ b/source/Lib/CommonLib/UnitTools.cpp
@@ -1724,6 +1724,39 @@ bool PU::isMultiModeLM(unsigned mode)
   return (mode == MMLM_CHROMA_IDX || mode == MMLM_L_IDX || mode == MMLM_T_IDX);
 }
 #endif
+#if SELF_AWARE_FILTERING_ESTIMATION
+bool PU::isSafeCCLM(const PredictionUnit& pu)
+{
+  //return pu.intraDir[1] == LM_CHROMA_IDX;
+  return pu.safeCCLMFlag;
+}
+bool PU::useSafeCCLM(const PredictionUnit &pu, const int intraMode)
+{
+  if (!pu.cu->slice->getSPS()->getSafeCCLMFlag())
+  {
+    return false;
+  }
+#if JVET_AA0057_CCCM
+  if (pu.cccmFlag)
+  {
+    return false;
+  }
+#endif
+#if JVET_AA0126_GLM
+  if (pu.glmIdc.isActive())
+  {
+    return false;
+  }
+#endif
+  if (pu.blocks[1].width * pu.blocks[1].height <= 16)
+  {
+    return false;
+  }
+
+  return intraMode == LM_CHROMA_IDX /*|| intraMode == MMLM_CHROMA_IDX*/;
+}
+#endif
+
 bool PU::isLMCModeEnabled(const PredictionUnit &pu, unsigned mode)
 {
 #if CCLM_LATENCY_RESTRICTION_RMV
diff --git a/source/Lib/CommonLib/UnitTools.h b/source/Lib/CommonLib/UnitTools.h
index 5831d487..a41c7e50 100644
--- a/source/Lib/CommonLib/UnitTools.h
+++ b/source/Lib/CommonLib/UnitTools.h
@@ -414,6 +414,10 @@ namespace PU
   bool isLMCMode                      (                          unsigned mode);
 #if MMLM
   bool isMultiModeLM(unsigned mode);
+#endif
+#if SELF_AWARE_FILTERING_ESTIMATION
+  bool isSafeCCLM(const PredictionUnit& pu);
+  bool useSafeCCLM(const PredictionUnit& pu, const int intraMode);
 #endif
   bool isLMCModeEnabled               (const PredictionUnit &pu, unsigned mode);
   bool isChromaIntraModeCrossCheckMode(const PredictionUnit &pu);
diff --git a/source/Lib/CommonLib/x86/IntraX86.h b/source/Lib/CommonLib/x86/IntraX86.h
index faba4e21..bf90c6ba 100644
--- a/source/Lib/CommonLib/x86/IntraX86.h
+++ b/source/Lib/CommonLib/x86/IntraX86.h
@@ -369,6 +369,1815 @@ void timdBlendingSIMD( Pel *pDst, int strideDst, Pel *pSrc, int strideSrc, int w
 }
 #endif
 
+#if SELF_AWARE_FILTERING_ESTIMATION
+template<X86_VEXT vext>
+void xSafeFilterSamples_sse(int filterType, const Pel *piSrc, Pel *pDst, int filterWidth,
+                                             int filterHeight, int iDstStride, int iRecStride, int iRecStride2)
+{
+  if (filterHeight % 2) 
+  {
+    IntraPrediction::xSafeFilterSamples(filterType, piSrc, pDst, filterWidth, filterHeight, iDstStride, iRecStride,
+                                        iRecStride2);
+    return;
+  }
+  if (filterType == 0)
+  {
+    __m128i src1, src2, src3, src4, src5, src6, src7, src8;
+    __m128i dst1, dst2, dst3, dst4;
+    __m128i mm_offset = _mm_set1_epi16(4);
+    int     shift     = 3;
+    if (filterWidth % 8 == 0)
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 8)
+        {
+          int i2 = i << 1;
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2 - 1));                    // a0-7
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + i2));                        // b0-7
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + 8 + i2 - 1));                // a8-15
+          src4   = _mm_loadu_si128((__m128i *) (piSrc + 8 + i2));                    // b8-15
+          src5   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2 - 1));       // a0-7
+          src6   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2));           // b0-7
+          src7   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + 8 + i2 - 1));   // a8-15
+          src8   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + 8 + i2));       // b8-15
+          dst1   = _mm_hadd_epi16(src1, src3);
+          dst2   = _mm_hadd_epi16(src2, src4);
+          dst3   = _mm_hadd_epi16(src5, src7);
+          dst4   = _mm_hadd_epi16(src6, src8);
+          dst1   = _mm_add_epi16(dst1, dst2);
+          dst1   = _mm_add_epi16(dst1, dst3);
+          dst1   = _mm_add_epi16(dst1, dst4);
+          dst1   = _mm_add_epi16(dst1, mm_offset);
+          dst1   = _mm_srai_epi16(dst1, shift);   // >> 3
+          _mm_store_si128((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else if (filterWidth % 4 == 0)
+    {
+      assert(filterHeight % 2 == 0);
+      for (int j = 0; j < filterHeight; j += 2)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = i << 1;
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2 - 1));                              // a0-7
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + i2));                                  // b0-7
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride2 + i2 - 1));                // a8-15
+          src4   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride2 + i2));                    // b8-15
+          src5   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2 - 1));                 // a0-7
+          src6   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2));                     // b0-7
+          src7   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + iRecStride2 + i2 - 1));   // a8-15
+          src8   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + iRecStride2 + i2));       // b8-15
+          dst1   = _mm_hadd_epi16(src1, src3);
+          dst2   = _mm_hadd_epi16(src2, src4);
+          dst3   = _mm_hadd_epi16(src5, src7);
+          dst4   = _mm_hadd_epi16(src6, src8);
+          dst1   = _mm_add_epi16(dst1, dst2);
+          dst1   = _mm_add_epi16(dst1, dst3);
+          dst1   = _mm_add_epi16(dst1, dst4);
+          dst1   = _mm_add_epi16(dst1, mm_offset);
+          dst1   = _mm_srai_epi16(dst1, shift);   // >> 3
+          _mm_storel_epi64((__m128i *) (pDst + i), (dst1));
+          dst1 = _mm_srli_si128(dst1, 8);
+          _mm_storel_epi64((__m128i *) (pDst + iDstStride + i), (dst1));
+        }
+        pDst += iDstStride * 2;
+        piSrc += iRecStride2 * 2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          int s = 4;
+          s += piSrc[2 * i] * 2;
+          s += piSrc[2 * i + 1];
+          s += piSrc[2 * i - 1];
+          s += piSrc[2 * i + iRecStride] * 2;
+          s += piSrc[2 * i + 1 + iRecStride];
+          s += piSrc[2 * i + iRecStride - 1];
+          pDst[i] = s >> 3;
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else if (filterType == 1)
+  {
+    __m128i src1, src2, src3, src4;
+    __m128i dst1, dst2;
+    __m128i mm_offset = _mm_set1_epi16(2);
+    int     shift     = 2;
+    if (filterWidth % 8 == 0)
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 8)
+        {
+          int i2 = i << 1;
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2));                    // a0-7
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + 8 + i2));                // a8-15
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2));       // a0-7
+          src4   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + 8 + i2));   // a8-15
+          dst1   = _mm_hadd_epi16(src1, src2);
+          dst2   = _mm_hadd_epi16(src3, src4);
+          dst1   = _mm_add_epi16(dst1, dst2);
+          dst1   = _mm_add_epi16(dst1, mm_offset);
+          dst1   = _mm_srai_epi16(dst1, shift);
+          _mm_store_si128((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else if (filterWidth % 4 == 0)
+    {
+      assert(filterHeight % 2 == 0);
+      for (int j = 0; j < filterHeight; j += 2)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = i << 1;
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2));                              // a0-7
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride2 + i2));                // a8-15
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2));                 // a0-7
+          src4   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + iRecStride2 + i2));   // a8-15
+          dst1   = _mm_hadd_epi16(src1, src2);
+          dst2   = _mm_hadd_epi16(src3, src4);
+          dst1   = _mm_add_epi16(dst1, dst2);
+          dst1   = _mm_add_epi16(dst1, mm_offset);
+          dst1   = _mm_srai_epi16(dst1, shift);
+          _mm_storel_epi64((__m128i *) (pDst + i), (dst1));
+          dst1 = _mm_srli_si128(dst1, 8);
+          _mm_storel_epi64((__m128i *) (pDst + iDstStride + i), (dst1));
+        }
+        pDst += iDstStride * 2;
+        piSrc += iRecStride2 * 2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          int s = 2;
+          s += piSrc[2 * i];
+          s += piSrc[2 * i + 1];
+          s += piSrc[2 * i + iRecStride];
+          s += piSrc[2 * i + 1 + iRecStride];
+          pDst[i] = s >> 2;
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else if (filterType == 2)   // North
+  {
+    __m128i src1, src2;
+    __m128i dst1;
+    __m128i mm_offset = _mm_set1_epi16(1);
+    int     shift     = 1;
+    if (filterWidth % 8 == 0)
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 8)
+        {
+          int i2 = i << 1;
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2));       // a0-7
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + 8 + i2));   // a8-15
+          dst1   = _mm_hadd_epi16(src1, src2);
+          dst1   = _mm_add_epi16(dst1, mm_offset);
+          dst1   = _mm_srai_epi16(dst1, shift);
+          _mm_store_si128((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else if (filterWidth % 4 == 0)
+    {
+      assert(filterHeight % 2 == 0);
+      for (int j = 0; j < filterHeight; j += 2)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = i << 1;
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2));                 // a0-7
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride2 + i2));   // a8-15
+          dst1   = _mm_hadd_epi16(src1, src2);
+          dst1   = _mm_add_epi16(dst1, mm_offset);
+          dst1   = _mm_srai_epi16(dst1, shift);
+          _mm_storel_epi64((__m128i *) (pDst + i), dst1);
+          dst1 = _mm_srli_si128(dst1, 8);
+          _mm_storel_epi64((__m128i *) (pDst + iDstStride + i), (dst1));
+        }
+        pDst += iDstStride * 2;
+        piSrc += iRecStride2 * 2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          int s = 1;
+          s += piSrc[2 * i];
+          s += piSrc[2 * i + 1];
+          pDst[i] = s >> 1;
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else if (filterType == 3)   // East
+  {
+    __m128i src1, src3;
+    __m128i dst1;
+    __m128i mm_offset = _mm_set1_epi16(1);
+    int     shift     = 1;
+    if (filterWidth % 4 == 0)
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = (i << 1) + 1;
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2));                // a0-7
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2));   // b0-7
+          dst1   = _mm_add_epi16(src1, src3);
+          dst1   = _mm_shufflelo_epi16(dst1, 0xD8);   // 0 2 1 3
+          dst1   = _mm_shufflehi_epi16(dst1, 0xD8);   // 0 2 1 3
+          dst1   = _mm_shuffle_epi32(dst1, 0xD8);
+          dst1   = _mm_add_epi16(dst1, mm_offset);
+          dst1   = _mm_srai_epi16(dst1, shift);
+          _mm_storel_epi64((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          int s = 1;
+          s += piSrc[2 * i + 1];
+          s += piSrc[2 * i + 1 + iRecStride];
+          pDst[i] = s >> 1;
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else if (filterType == 4)   // South
+  {
+    __m128i src1, src2;
+    __m128i dst1;
+    __m128i mm_offset = _mm_set1_epi16(1);
+    int     shift     = 1;
+    if (filterWidth % 8 == 0)
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 8)
+        {
+          int i2 = i << 1;
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2));       // a0-7
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + 8 + i2));   // a8-15
+          dst1   = _mm_hadd_epi16(src1, src2);
+          dst1   = _mm_add_epi16(dst1, mm_offset);
+          dst1   = _mm_srai_epi16(dst1, shift);
+          _mm_store_si128((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else if (filterWidth % 4 == 0)
+    {
+      assert(filterHeight % 2 == 0);
+      for (int j = 0; j < filterHeight; j += 2)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = i << 1;
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2));                 // a0-7
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + iRecStride2 + i2));   // a8-15
+          dst1   = _mm_hadd_epi16(src1, src2);
+          dst1   = _mm_add_epi16(dst1, mm_offset);
+          dst1   = _mm_srai_epi16(dst1, shift);
+          _mm_storel_epi64((__m128i *) (pDst + i), dst1);
+          dst1 = _mm_srli_si128(dst1, 8);
+          _mm_storel_epi64((__m128i *) (pDst + iDstStride + i), (dst1));
+        }
+        pDst += iDstStride * 2;
+        piSrc += iRecStride2 * 2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          int s = 1;
+          s += piSrc[2 * i + iRecStride];
+          s += piSrc[2 * i + 1 + iRecStride];
+          pDst[i] = s >> 1;
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else if (filterType == 5)   // Horizontal Gradient
+  {
+    __m128i src1, src2, src3, src4;
+    __m128i dst1, dst2;
+    __m128i mm_offset = _mm_set1_epi16(1);
+    int     shift     = 1;
+    if (filterWidth % 8 == 0)
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 8)
+        {
+          int i2 = i << 1;
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2));                    // a0-7
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + 8 + i2));                // a8-15
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2));       // a0-7
+          src4   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + 8 + i2));   // a8-15
+          dst1   = _mm_hsub_epi16(src1, src2);
+          dst2   = _mm_hsub_epi16(src3, src4);
+          dst1   = _mm_add_epi16(dst1, dst2);
+          dst1   = _mm_add_epi16(dst1, mm_offset);
+          dst1   = _mm_srai_epi16(dst1, shift);
+          _mm_store_si128((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else if (filterWidth % 4 == 0)
+    {
+      assert(filterHeight % 2 == 0);
+      for (int j = 0; j < filterHeight; j += 2)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = i << 1;
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2));                              // a0-7
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride2 + i2));                // a8-15
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2));                 // a0-7
+          src4   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + iRecStride2 + i2));   // a8-15
+          dst1   = _mm_hsub_epi16(src1, src2);
+          dst2   = _mm_hsub_epi16(src3, src4);
+          dst1   = _mm_add_epi16(dst1, dst2);
+          dst1   = _mm_add_epi16(dst1, mm_offset);
+          dst1   = _mm_srai_epi16(dst1, shift);
+          _mm_storel_epi64((__m128i *) (pDst + i), (dst1));
+          dst1 = _mm_srli_si128(dst1, 8);
+          _mm_storel_epi64((__m128i *) (pDst + iDstStride + i), (dst1));
+        }
+        pDst += iDstStride * 2;
+        piSrc += iRecStride2 * 2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          pDst[i] =
+            ((piSrc[2 * i] + piSrc[2 * i + iRecStride]) - (piSrc[2 * i + 1] + piSrc[2 * i + iRecStride + 1]) + 1) >> 1;
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else if (filterType == 6)   // vertical Gradient
+  {
+    __m128i src1, src2, src3, src4, src5, src6, src7, src8;
+    __m128i dst1, dst2, dst3, dst4;
+    __m128i mm_offset = _mm_set1_epi16(2);
+    int     shift     = 2;
+    if (filterWidth % 8 == 0)
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 8)
+        {
+          int i2 = i << 1;
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2 - 1));                    // a0-7
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + i2));                        // b0-7
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + 8 + i2 - 1));                // a8-15
+          src4   = _mm_loadu_si128((__m128i *) (piSrc + 8 + i2));                    // b8-15
+          src5   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2 - 1));       // a0-7
+          src6   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2));           // b0-7
+          src7   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + 8 + i2 - 1));   // a8-15
+          src8   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + 8 + i2));       // b8-15
+          dst1   = _mm_hadd_epi16(src1, src3);
+          dst2   = _mm_hadd_epi16(src2, src4);
+          dst3   = _mm_hadd_epi16(src5, src7);
+          dst4   = _mm_hadd_epi16(src6, src8);
+          dst1   = _mm_add_epi16(dst1, dst2);
+          dst3   = _mm_add_epi16(dst3, dst4);
+          dst1   = _mm_sub_epi16(dst1, dst3);
+          dst1   = _mm_add_epi16(dst1, mm_offset);
+          dst1   = _mm_srai_epi16(dst1, shift);
+          _mm_store_si128((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else if (filterWidth % 4 == 0)
+    {
+      assert(filterHeight % 2 == 0);
+      for (int j = 0; j < filterHeight; j += 2)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = i << 1;
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2 - 1));                              // a0-7
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + i2));                                  // b0-7
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride2 + i2 - 1));                // a8-15
+          src4   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride2 + i2));                    // b8-15
+          src5   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2 - 1));                 // a0-7
+          src6   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2));                     // b0-7
+          src7   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + iRecStride2 + i2 - 1));   // a8-15
+          src8   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + iRecStride2 + i2));       // b8-15
+          dst1   = _mm_hadd_epi16(src1, src3);
+          dst2   = _mm_hadd_epi16(src2, src4);
+          dst3   = _mm_hadd_epi16(src5, src7);
+          dst4   = _mm_hadd_epi16(src6, src8);
+          dst1   = _mm_add_epi16(dst1, dst2);
+          dst3   = _mm_add_epi16(dst3, dst4);
+          dst1   = _mm_sub_epi16(dst1, dst3);
+          dst1   = _mm_add_epi16(dst1, mm_offset);
+          dst1   = _mm_srai_epi16(dst1, shift);   // >> 3
+          _mm_storel_epi64((__m128i *) (pDst + i), (dst1));
+          dst1 = _mm_srli_si128(dst1, 8);
+          _mm_storel_epi64((__m128i *) (pDst + iDstStride + i), (dst1));
+        }
+        pDst += iDstStride * 2;
+        piSrc += iRecStride2 * 2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          int s1 = 0;
+          s1 += piSrc[2 * i] * 2;
+          s1 += piSrc[2 * i + 1];
+          s1 += piSrc[2 * i - 1];
+          int s2 = 0;
+          s2 += piSrc[2 * i + iRecStride] * 2;
+          s2 += piSrc[2 * i + 1 + iRecStride];
+          s2 += piSrc[2 * i + iRecStride - 1];
+          pDst[i] = (s1 - s2 + 2) >> 2;
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else if (filterType == 7)
+  {
+    __m128i src1, src3;
+    __m128i dst1;
+    __m128i mm_offset = _mm_set1_epi16(1);
+    int     shift     = 1;
+    if (filterWidth % 4 == 0)
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = (i << 1);
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2));                // a0-7
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2));   // b0-7
+          dst1   = _mm_sub_epi16(src1, src3);
+          dst1   = _mm_shufflelo_epi16(dst1, 0xD8);   // 0 2 1 3
+          dst1   = _mm_shufflehi_epi16(dst1, 0xD8);   // 0 2 1 3
+          dst1   = _mm_shuffle_epi32(dst1, 0xD8);
+          dst1   = _mm_add_epi16(dst1, mm_offset);
+          dst1   = _mm_srai_epi16(dst1, shift);
+          _mm_storel_epi64((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          pDst[i] = (piSrc[2 * i] - piSrc[2 * i + iRecStride] + 1) >> 1;
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else if (filterType == 8)
+  {
+    __m128i src1, src3;
+    __m128i dst1;
+    __m128i mm_offset = _mm_set1_epi16(1);
+    int     shift     = 1;
+    if (filterWidth % 4 == 0)
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = (i << 1);
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2 + 1));                // a0-7
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + 1 + i2));   // b0-7
+          dst1   = _mm_sub_epi16(src1, src3);
+          dst1   = _mm_shufflelo_epi16(dst1, 0xD8);   // 0 2 1 3
+          dst1   = _mm_shufflehi_epi16(dst1, 0xD8);   // 0 2 1 3
+          dst1   = _mm_shuffle_epi32(dst1, 0xD8);
+          dst1   = _mm_add_epi16(dst1, mm_offset);
+          dst1   = _mm_srai_epi16(dst1, shift);
+          _mm_storel_epi64((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          pDst[i] = (piSrc[2 * i + 1] - piSrc[2 * i + iRecStride + 1] + 1) >> 1;
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else if (filterType == 9)
+  {
+    __m128i src1;
+    __m128i dst1;
+    if (filterWidth % 4 == 0)
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = (i << 1);
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2));   // a0-7
+          dst1   = _mm_shufflelo_epi16(src1, 0xD8);             // 0 2 1 3
+          dst1   = _mm_shufflehi_epi16(dst1, 0xD8);             // 0 2 1 3
+          dst1   = _mm_shuffle_epi32(dst1, 0xD8);
+          _mm_storel_epi64((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          pDst[i] = piSrc[2 * i];
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else if (filterType == 10)
+  {
+    __m128i src1;
+    __m128i dst1;
+    if (filterWidth % 4 == 0)
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = (i << 1);
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2 + 1));   // a0-7
+          dst1   = _mm_shufflelo_epi16(src1, 0xD8);                 // 0 2 1 3
+          dst1   = _mm_shufflehi_epi16(dst1, 0xD8);                 // 0 2 1 3
+          dst1   = _mm_shuffle_epi32(dst1, 0xD8);
+          _mm_storel_epi64((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          pDst[i] = piSrc[2 * i + 1];
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else if (filterType == 11)
+  {
+    __m128i src1;
+    __m128i dst1;
+    if (filterWidth % 4 == 0)
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = (i << 1);
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2 + iRecStride));   // a0-7
+          dst1   = _mm_shufflelo_epi16(src1, 0xD8);                          // 0 2 1 3
+          dst1   = _mm_shufflehi_epi16(dst1, 0xD8);                          // 0 2 1 3
+          dst1   = _mm_shuffle_epi32(dst1, 0xD8);
+          _mm_storel_epi64((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          pDst[i] = piSrc[2 * i + iRecStride];
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else if (filterType == 12)
+  {
+    __m128i src1;
+    __m128i dst1;
+    if (filterWidth % 4 == 0)
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = (i << 1);
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2 + iRecStride + 1));   // a0-7
+          dst1   = _mm_shufflelo_epi16(src1, 0xD8);                              // 0 2 1 3
+          dst1   = _mm_shufflehi_epi16(dst1, 0xD8);                              // 0 2 1 3
+          dst1   = _mm_shuffle_epi32(dst1, 0xD8);
+          _mm_storel_epi64((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          pDst[i] = piSrc[2 * i + 1 + iRecStride];
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else if (filterType == 13)
+  {
+    __m128i src1;
+    __m128i dst1;
+    if (filterWidth % 4 == 0)
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = (i << 1);
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2 - 1));   // a0-7
+          dst1   = _mm_shufflelo_epi16(src1, 0xD8);                 // 0 2 1 3
+          dst1   = _mm_shufflehi_epi16(dst1, 0xD8);                 // 0 2 1 3
+          dst1   = _mm_shuffle_epi32(dst1, 0xD8);
+          _mm_storel_epi64((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          pDst[i] = piSrc[2 * i - 1];
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else if (filterType == 14)   // South
+  {
+    __m128i src1;
+    __m128i dst1;
+    if (filterWidth % 4 == 0)
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = (i << 1);
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2 - 1 + iRecStride));   // a0-7
+          dst1   = _mm_shufflelo_epi16(src1, 0xD8);                              // 0 2 1 3
+          dst1   = _mm_shufflehi_epi16(dst1, 0xD8);                              // 0 2 1 3
+          dst1   = _mm_shuffle_epi32(dst1, 0xD8);
+          _mm_storel_epi64((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          pDst[i] = piSrc[2 * i - 1 + iRecStride];
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else if (filterType == 15)   // East
+  {
+    __m128i src1, src2, src3, src4;
+    __m128i dst1, dst2;
+    __m128i mm_offset = _mm_set1_epi16(2);
+    int     shift     = 2;
+    if (filterWidth % 8 == 0)
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 8)
+        {
+          int i2 = i << 1;
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2 - 1));       // a0-7
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + i2));           // b0-7
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + 8 + i2 - 1));   // a8-15
+          src4   = _mm_loadu_si128((__m128i *) (piSrc + 8 + i2));       // b8-15
+          dst1   = _mm_hadd_epi16(src1, src3);
+          dst2   = _mm_hadd_epi16(src2, src4);
+          dst1   = _mm_add_epi16(dst1, dst2);
+          dst1   = _mm_add_epi16(dst1, mm_offset);
+          dst1   = _mm_srai_epi16(dst1, shift);
+          _mm_store_si128((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else if (filterWidth % 4 == 0)
+    {
+      assert(filterHeight % 2 == 0);
+      for (int j = 0; j < filterHeight; j += 2)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = i << 1;
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2 - 1));                 // a0-7
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + i2));                     // b0-7
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride2 + i2 - 1));   // a8-15
+          src4   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride2 + i2));       // b8-15
+          dst1   = _mm_hadd_epi16(src1, src3);
+          dst2   = _mm_hadd_epi16(src2, src4);
+          dst1   = _mm_add_epi16(dst1, dst2);
+          dst1   = _mm_add_epi16(dst1, mm_offset);
+          dst1   = _mm_srai_epi16(dst1, shift);
+          _mm_storel_epi64((__m128i *) (pDst + i), (dst1));
+          dst1 = _mm_srli_si128(dst1, 8);
+          _mm_storel_epi64((__m128i *) (pDst + iDstStride + i), (dst1));
+        }
+        pDst += iDstStride * 2;
+        piSrc += iRecStride2 * 2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          int s = 2;
+          s += piSrc[2 * i] * 2;
+          s += piSrc[2 * i + 1];
+          s += piSrc[2 * i - 1];
+          pDst[i] = s >> 2;
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else if (filterType == 16)   // South
+  {
+    __m128i src1, src2, src3, src4;
+    __m128i dst1, dst2;
+    __m128i mm_offset = _mm_set1_epi16(2);
+    int     shift     = 2;
+    if (filterWidth % 8 == 0)
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 8)
+        {
+          int i2 = (i << 1) + iRecStride;
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2 - 1));       // a0-7
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + i2));           // b0-7
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + 8 + i2 - 1));   // a8-15
+          src4   = _mm_loadu_si128((__m128i *) (piSrc + 8 + i2));       // b8-15
+          dst1   = _mm_hadd_epi16(src1, src3);
+          dst2   = _mm_hadd_epi16(src2, src4);
+          dst1   = _mm_add_epi16(dst1, dst2);
+          dst1   = _mm_add_epi16(dst1, mm_offset);
+          dst1   = _mm_srai_epi16(dst1, shift);
+          _mm_store_si128((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else if (filterWidth % 4 == 0)
+    {
+      assert(filterHeight % 2 == 0);
+      for (int j = 0; j < filterHeight; j += 2)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = (i << 1) + iRecStride;
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2 - 1));                 // a0-7
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + i2));                     // b0-7
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride2 + i2 - 1));   // a8-15
+          src4   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride2 + i2));       // b8-15
+          dst1   = _mm_hadd_epi16(src1, src3);
+          dst2   = _mm_hadd_epi16(src2, src4);
+          dst1   = _mm_add_epi16(dst1, dst2);
+          dst1   = _mm_add_epi16(dst1, mm_offset);
+          dst1   = _mm_srai_epi16(dst1, shift);
+          _mm_storel_epi64((__m128i *) (pDst + i), (dst1));
+          dst1 = _mm_srli_si128(dst1, 8);
+          _mm_storel_epi64((__m128i *) (pDst + iDstStride + i), (dst1));
+        }
+        pDst += iDstStride * 2;
+        piSrc += iRecStride2 * 2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          int s = 2;
+          s += piSrc[2 * i + iRecStride] * 2;
+          s += piSrc[2 * i + 1 + iRecStride];
+          s += piSrc[2 * i + iRecStride - 1];
+          pDst[i] = s >> 2;
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else if (filterType == 17)
+  {
+    __m128i src1, src2, src3, src4;
+    __m128i dst1, dst2;
+    __m128i mm_offset = _mm_set1_epi16(1);
+    int     shift     = 1;
+    if (filterWidth % 8 == 0)
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 8)
+        {
+          int i2 = (i << 1) - 1;
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2));                    // a0-7
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + 8 + i2));                // a8-15
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2));       // a0-7
+          src4   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + 8 + i2));   // a8-15
+          dst1   = _mm_hsub_epi16(src1, src2);
+          dst2   = _mm_hsub_epi16(src3, src4);
+          dst1   = _mm_add_epi16(dst1, dst2);
+          dst1   = _mm_sub_epi16(mm_offset, dst1);
+          dst1   = _mm_srai_epi16(dst1, shift);
+          _mm_store_si128((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else if (filterWidth % 4 == 0)
+    {
+      assert(filterHeight % 2 == 0);
+      for (int j = 0; j < filterHeight; j += 2)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = (i << 1) - 1;
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2));                              // a0-7
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride2 + i2));                // a8-15
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2));                 // a0-7
+          src4   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + iRecStride2 + i2));   // a8-15
+          dst1   = _mm_hsub_epi16(src1, src2);
+          dst2   = _mm_hsub_epi16(src3, src4);
+          dst1   = _mm_add_epi16(dst1, dst2);
+          dst1   = _mm_sub_epi16(mm_offset, dst1);
+          dst1   = _mm_srai_epi16(dst1, shift);
+          _mm_storel_epi64((__m128i *) (pDst + i), (dst1));
+          dst1 = _mm_srli_si128(dst1, 8);
+          _mm_storel_epi64((__m128i *) (pDst + iDstStride + i), (dst1));
+        }
+        pDst += iDstStride * 2;
+        piSrc += iRecStride2 * 2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          pDst[i] =
+            ((piSrc[2 * i] + piSrc[2 * i + iRecStride]) - (piSrc[2 * i - 1] + piSrc[2 * i + iRecStride - 1]) + 1) >> 1;
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else if (filterType == 18)
+  {
+    __m128i src1, src2, src3, src4, src5, src6, src7, src8;
+    __m128i dst1, dst2, dst3, dst4;
+    __m128i mm_offset = _mm_set1_epi16(1);
+    int     shift     = 1;
+    if (filterWidth % 8 == 0)
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 8)
+        {
+          int i2 = i << 1;
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2 - 1));                    // a0-7
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + i2));                        // b0-7
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + 8 + i2 - 1));                // a8-15
+          src4   = _mm_loadu_si128((__m128i *) (piSrc + 8 + i2));                    // b8-15
+          src5   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2 - 1));       // a0-7
+          src6   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2));           // b0-7
+          src7   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + 8 + i2 - 1));   // a8-15
+          src8   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + 8 + i2));       // b8-15
+          dst1   = _mm_hsub_epi16(src1, src3);
+          dst2   = _mm_hsub_epi16(src2, src4);
+          dst3   = _mm_hsub_epi16(src5, src7);
+          dst4   = _mm_hsub_epi16(src6, src8);
+          dst1   = _mm_add_epi16(dst1, dst2);
+          dst1   = _mm_add_epi16(dst1, dst3);
+          dst1   = _mm_add_epi16(dst1, dst4);
+          dst1   = _mm_sub_epi16(mm_offset, dst1);
+          dst1   = _mm_srai_epi16(dst1, shift);
+          _mm_store_si128((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else if (filterWidth % 4 == 0)
+    {
+      assert(filterHeight % 2 == 0);
+      for (int j = 0; j < filterHeight; j += 2)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = i << 1;
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2 - 1));                              // a0-7
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + i2));                                  // b0-7
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride2 + i2 - 1));                // a8-15
+          src4   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride2 + i2));                    // b8-15
+          src5   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2 - 1));                 // a0-7
+          src6   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2));                     // b0-7
+          src7   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + iRecStride2 + i2 - 1));   // a8-15
+          src8   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + iRecStride2 + i2));       // b8-15
+          dst1   = _mm_hsub_epi16(src1, src3);
+          dst2   = _mm_hsub_epi16(src2, src4);
+          dst3   = _mm_hsub_epi16(src5, src7);
+          dst4   = _mm_hsub_epi16(src6, src8);
+          dst1   = _mm_add_epi16(dst1, dst2);
+          dst1   = _mm_add_epi16(dst1, dst3);
+          dst1   = _mm_add_epi16(dst1, dst4);
+          dst1   = _mm_sub_epi16(mm_offset, dst1);
+          dst1   = _mm_srai_epi16(dst1, shift);
+          _mm_storel_epi64((__m128i *) (pDst + i), (dst1));
+          dst1 = _mm_srli_si128(dst1, 8);
+          _mm_storel_epi64((__m128i *) (pDst + iDstStride + i), (dst1));
+        }
+        pDst += iDstStride * 2;
+        piSrc += iRecStride2 * 2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          pDst[i] = ((piSrc[2 * i + 1] + piSrc[2 * i + iRecStride + 1])
+                     - (piSrc[2 * i - 1] + piSrc[2 * i + iRecStride - 1]) + 1)
+                    >> 1;
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else if (filterType == 19)
+  {
+    __m128i src1, src2, src3, src4;
+    __m128i dst1, dst2;
+    __m128i mm_offset = _mm_set1_epi16(1);
+    int     shift     = 1;
+    if (filterWidth % 4 == 0)
+    {
+      assert(filterHeight % 2 == 0);
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = i << 1;
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2));
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + i2 + 1));
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2));
+          src4   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2 + 1));
+          dst1   = _mm_add_epi16(src1, src3);
+          dst2   = _mm_add_epi16(dst1, dst1);
+          dst2   = _mm_add_epi16(dst2, dst1);
+          dst1   = _mm_add_epi16(src2, src4);
+          dst1   = _mm_sub_epi16(dst2, dst1);
+          dst1   = _mm_shufflelo_epi16(dst1, 0xD8);   // 0 2 1 3
+          dst1   = _mm_shufflehi_epi16(dst1, 0xD8);   // 0 2 1 3
+          dst1   = _mm_shuffle_epi32(dst1, 0xD8);
+          dst1   = _mm_add_epi16(dst1, mm_offset);
+          dst1   = _mm_srai_epi16(dst1, shift);
+          _mm_storel_epi64((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          pDst[i] =
+            (3 * (piSrc[2 * i] + piSrc[2 * i + iRecStride]) - (piSrc[2 * i + 1] + piSrc[2 * i + iRecStride + 1]) + 1)
+            >> 1;
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else if (filterType == 20)
+  {
+    __m128i src1, src2, src3, src4, src5, src6, src7, src8;
+    __m128i dst1, dst2, dst3, dst4;
+    __m128i mm_offset = _mm_set1_epi16(2);
+    int     shift     = 2;
+    if (filterWidth % 8 == 0)
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 8)
+        {
+          int i2 = i << 1;
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2 - 1));                    // a0-7
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + i2));                        // b0-7
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + 8 + i2 - 1));                // a8-15
+          src4   = _mm_loadu_si128((__m128i *) (piSrc + 8 + i2));                    // b8-15
+          src5   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2 - 1));       // a0-7
+          src6   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2));           // b0-7
+          src7   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + 8 + i2 - 1));   // a8-15
+          src8   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + 8 + i2));       // b8-15
+          dst1   = _mm_hadd_epi16(src1, src3);
+          dst2   = _mm_hadd_epi16(src2, src4);
+          dst3   = _mm_hadd_epi16(src5, src7);
+          dst4   = _mm_hadd_epi16(src6, src8);
+          dst1   = _mm_add_epi16(dst1, dst2);
+          dst2   = _mm_add_epi16(dst1, dst1);
+          dst1   = _mm_add_epi16(dst1, dst2);
+          dst3   = _mm_add_epi16(dst3, dst4);
+          dst1   = _mm_sub_epi16(dst1, dst3);
+          dst1   = _mm_add_epi16(dst1, mm_offset);
+          dst1   = _mm_srai_epi16(dst1, shift);
+          _mm_store_si128((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else if (filterWidth % 4 == 0)
+    {
+      assert(filterHeight % 2 == 0);
+      for (int j = 0; j < filterHeight; j += 2)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = i << 1;
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2 - 1));                              // a0-7
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + i2));                                  // b0-7
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride2 + i2 - 1));                // a8-15
+          src4   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride2 + i2));                    // b8-15
+          src5   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2 - 1));                 // a0-7
+          src6   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2));                     // b0-7
+          src7   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + iRecStride2 + i2 - 1));   // a8-15
+          src8   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + iRecStride2 + i2));       // b8-15
+          dst1   = _mm_hadd_epi16(src1, src3);
+          dst2   = _mm_hadd_epi16(src2, src4);
+          dst3   = _mm_hadd_epi16(src5, src7);
+          dst4   = _mm_hadd_epi16(src6, src8);
+          dst1   = _mm_add_epi16(dst1, dst2);
+          dst2   = _mm_add_epi16(dst1, dst1);
+          dst1   = _mm_add_epi16(dst1, dst2);
+          dst3   = _mm_add_epi16(dst3, dst4);
+          dst1   = _mm_sub_epi16(dst1, dst3);
+          dst1   = _mm_add_epi16(dst1, mm_offset);
+          dst1   = _mm_srai_epi16(dst1, shift);   // >> 3
+          _mm_storel_epi64((__m128i *) (pDst + i), (dst1));
+          dst1 = _mm_srli_si128(dst1, 8);
+          _mm_storel_epi64((__m128i *) (pDst + iDstStride + i), (dst1));
+        }
+        pDst += iDstStride * 2;
+        piSrc += iRecStride2 * 2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          int s1 = 0;
+          s1 += piSrc[2 * i] * 2;
+          s1 += piSrc[2 * i + 1];
+          s1 += piSrc[2 * i - 1];
+          int s2 = 0;
+          s2 += piSrc[2 * i + iRecStride] * 2;
+          s2 += piSrc[2 * i + 1 + iRecStride];
+          s2 += piSrc[2 * i + iRecStride - 1];
+          pDst[i] = (3 * s1 - s2 + 2) >> 2;
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else if (filterType == 21)
+  {
+    __m128i src1, src3;
+    __m128i dst1;
+    __m128i mm_offset = _mm_set1_epi16(1);
+    int     shift     = 1;
+    if (filterWidth % 4 == 0)
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = (i << 1);
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2));                    // a0-7
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + 1 + i2));   // b0-7
+          dst1   = _mm_sub_epi16(src1, src3);
+          dst1   = _mm_shufflelo_epi16(dst1, 0xD8);   // 0 2 1 3
+          dst1   = _mm_shufflehi_epi16(dst1, 0xD8);   // 0 2 1 3
+          dst1   = _mm_shuffle_epi32(dst1, 0xD8);
+          dst1   = _mm_add_epi16(dst1, mm_offset);
+          dst1   = _mm_srai_epi16(dst1, shift);
+          _mm_storel_epi64((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          pDst[i] = (piSrc[2 * i] - piSrc[2 * i + iRecStride + 1] + 1) >> 1;
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else if (filterType == 22)
+  {
+    __m128i src1, src3;
+    __m128i dst1;
+    __m128i mm_offset = _mm_set1_epi16(1);
+    int     shift     = 1;
+    if (filterWidth % 4 == 0)
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = (i << 1);
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2 + 1));            // a0-7
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2));   // b0-7
+          dst1   = _mm_sub_epi16(src1, src3);
+          dst1   = _mm_shufflelo_epi16(dst1, 0xD8);   // 0 2 1 3
+          dst1   = _mm_shufflehi_epi16(dst1, 0xD8);   // 0 2 1 3
+          dst1   = _mm_shuffle_epi32(dst1, 0xD8);
+          dst1   = _mm_add_epi16(dst1, mm_offset);
+          dst1   = _mm_srai_epi16(dst1, shift);
+          _mm_storel_epi64((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          pDst[i] = (piSrc[2 * i + 1] - piSrc[2 * i + iRecStride] + 1) >> 1;
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else if (filterType == 23)
+  {
+    __m128i src1, src3;
+    __m128i dst1;
+    __m128i mm_offset = _mm_set1_epi16(1);
+    int     shift     = 1;
+    if (filterWidth % 4 == 0)
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = (i << 1);
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2));                    // a0-7
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride - 1 + i2));   // b0-7
+          dst1   = _mm_sub_epi16(src1, src3);
+          dst1   = _mm_shufflelo_epi16(dst1, 0xD8);   // 0 2 1 3
+          dst1   = _mm_shufflehi_epi16(dst1, 0xD8);   // 0 2 1 3
+          dst1   = _mm_shuffle_epi32(dst1, 0xD8);
+          dst1   = _mm_add_epi16(dst1, mm_offset);
+          dst1   = _mm_srai_epi16(dst1, shift);
+          _mm_storel_epi64((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          pDst[i] = (piSrc[2 * i] - piSrc[2 * i + iRecStride - 1] + 1) >> 1;
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else if (filterType == 24)
+  {
+    __m128i src1, src3;
+    __m128i dst1;
+    __m128i mm_offset = _mm_set1_epi16(1);
+    int     shift     = 1;
+    if (filterWidth % 4 == 0)
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = (i << 1);
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2 - 1));            // a0-7
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2));   // b0-7
+          dst1   = _mm_sub_epi16(src1, src3);
+          dst1   = _mm_shufflelo_epi16(dst1, 0xD8);   // 0 2 1 3
+          dst1   = _mm_shufflehi_epi16(dst1, 0xD8);   // 0 2 1 3
+          dst1   = _mm_shuffle_epi32(dst1, 0xD8);
+          dst1   = _mm_add_epi16(dst1, mm_offset);
+          dst1   = _mm_srai_epi16(dst1, shift);
+          _mm_storel_epi64((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          pDst[i] = (piSrc[2 * i - 1] - piSrc[2 * i + iRecStride] + 1) >> 1;
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else if (filterType == 25)
+  {
+    __m128i src1, src2, src3, src4, src5, src6, src7, src8;
+    __m128i dst1, dst2, dst3, dst4;
+    if (filterWidth % 8 == 0)
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 8)
+        {
+          int i2 = i << 1;
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2 - 1));                    // a0-7
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + i2));                        // b0-7
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + 8 + i2 - 1));                // a8-15
+          src4   = _mm_loadu_si128((__m128i *) (piSrc + 8 + i2));                    // b8-15
+          src5   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2 - 1));       // a0-7
+          src6   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2));           // b0-7
+          src7   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + 8 + i2 - 1));   // a8-15
+          src8   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + 8 + i2));       // b8-15
+          dst1   = _mm_hsub_epi16(src1, src3);
+          dst2   = _mm_hsub_epi16(src2, src4);
+          dst3   = _mm_hsub_epi16(src5, src7);
+          dst4   = _mm_hsub_epi16(src6, src8);
+          dst1   = _mm_sub_epi16(dst2, dst1);
+          dst1   = _mm_sub_epi16(dst1, dst3);
+          dst1   = _mm_add_epi16(dst1, dst4);
+          _mm_store_si128((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else if (filterWidth % 4 == 0)
+    {
+      assert(filterHeight % 2 == 0);
+      for (int j = 0; j < filterHeight; j += 2)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = i << 1;
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2 - 1));                              // a0-7
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + i2));                                  // b0-7
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride2 + i2 - 1));                // a8-15
+          src4   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride2 + i2));                    // b8-15
+          src5   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2 - 1));                 // a0-7
+          src6   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2));                     // b0-7
+          src7   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + iRecStride2 + i2 - 1));   // a8-15
+          src8   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + iRecStride2 + i2));       // b8-15
+          dst1   = _mm_hsub_epi16(src1, src3);
+          dst2   = _mm_hsub_epi16(src2, src4);
+          dst3   = _mm_hsub_epi16(src5, src7);
+          dst4   = _mm_hsub_epi16(src6, src8);
+          dst1   = _mm_sub_epi16(dst2, dst1);
+          dst1   = _mm_sub_epi16(dst1, dst3);
+          dst1   = _mm_add_epi16(dst1, dst4);
+          _mm_storel_epi64((__m128i *) (pDst + i), (dst1));
+          dst1 = _mm_srli_si128(dst1, 8);
+          _mm_storel_epi64((__m128i *) (pDst + iDstStride + i), (dst1));
+        }
+        pDst += iDstStride * 2;
+        piSrc += iRecStride2 * 2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          pDst[i] = (piSrc[2 * i] + piSrc[2 * i + iRecStride]) * 2 - piSrc[2 * i + 1] - piSrc[2 * i - 1]
+                    - piSrc[2 * i + 1 + iRecStride] - piSrc[2 * i + iRecStride - 1];
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else if (filterType == 26)
+  {
+    __m128i src1, src3;
+    __m128i dst1;
+    if (filterWidth % 4 == 0)
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = (i << 1);
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2 + 1));                // a0-7
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2 - 1));   // b0-7
+          dst1   = _mm_sub_epi16(src1, src3);
+          dst1   = _mm_shufflelo_epi16(dst1, 0xD8);   // 0 2 1 3
+          dst1   = _mm_shufflehi_epi16(dst1, 0xD8);   // 0 2 1 3
+          dst1   = _mm_shuffle_epi32(dst1, 0xD8);
+          _mm_storel_epi64((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          pDst[i] = piSrc[2 * i + 1] - piSrc[2 * i + iRecStride - 1];
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else if (filterType == 27)   // Horizontal Gradient
+  {
+    __m128i src1, src3;
+    __m128i dst1;
+    if (filterWidth % 4 == 0)
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = (i << 1);
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2 - 1));                // a0-7
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + 1 + i2));   // b0-7
+          dst1   = _mm_sub_epi16(src1, src3);
+          dst1   = _mm_shufflelo_epi16(dst1, 0xD8);   // 0 2 1 3
+          dst1   = _mm_shufflehi_epi16(dst1, 0xD8);   // 0 2 1 3
+          dst1   = _mm_shuffle_epi32(dst1, 0xD8);
+          _mm_storel_epi64((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          pDst[i] = piSrc[2 * i - 1] - piSrc[2 * i + iRecStride + 1];
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else if (filterType == 28)   // vertical Gradient
+  {
+    __m128i src1, src2, src3, src4, src5, src6, src7, src8;
+    __m128i dst1, dst2, dst3, dst4;
+    __m128i mm_offset = _mm_set1_epi16(2);
+    int     shift     = 2;
+    if (filterWidth % 8 == 0)
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 8)
+        {
+          int i2 = i << 1;
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2 - 1));                    // a0-7
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + i2));                        // b0-7
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + 8 + i2 - 1));                // a8-15
+          src4   = _mm_loadu_si128((__m128i *) (piSrc + 8 + i2));                    // b8-15
+          src5   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2 - 1));       // a0-7
+          src6   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2));           // b0-7
+          src7   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + 8 + i2 - 1));   // a8-15
+          src8   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + 8 + i2));       // b8-15
+          dst1   = _mm_hsub_epi16(src1, src3);
+          dst2   = _mm_hadd_epi16(src2, src4);
+          dst3   = _mm_hsub_epi16(src5, src7);
+          dst4   = _mm_hadd_epi16(src6, src8);
+          dst1   = _mm_add_epi16(dst1, dst2);
+          dst1   = _mm_add_epi16(dst1, dst3);
+          dst1   = _mm_add_epi16(dst1, dst4);
+          dst1   = _mm_add_epi16(dst1, mm_offset);
+          dst1   = _mm_srai_epi16(dst1, shift);
+          _mm_store_si128((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else if (filterWidth % 4 == 0)
+    {
+      assert(filterHeight % 2 == 0);
+      for (int j = 0; j < filterHeight; j += 2)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = i << 1;
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2 - 1));                              // a0-7
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + i2));                                  // b0-7
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride2 + i2 - 1));                // a8-15
+          src4   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride2 + i2));                    // b8-15
+          src5   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2 - 1));                 // a0-7
+          src6   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2));                     // b0-7
+          src7   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + iRecStride2 + i2 - 1));   // a8-15
+          src8   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + iRecStride2 + i2));       // b8-15
+          dst1   = _mm_hsub_epi16(src1, src3);
+          dst2   = _mm_hadd_epi16(src2, src4);
+          dst3   = _mm_hsub_epi16(src5, src7);
+          dst4   = _mm_hadd_epi16(src6, src8);
+          dst1   = _mm_add_epi16(dst1, dst2);
+          dst1   = _mm_add_epi16(dst1, dst3);
+          dst1   = _mm_add_epi16(dst1, dst4);
+          dst1   = _mm_add_epi16(dst1, mm_offset);
+          dst1   = _mm_srai_epi16(dst1, shift);
+          _mm_storel_epi64((__m128i *) (pDst + i), (dst1));
+          dst1 = _mm_srli_si128(dst1, 8);
+          _mm_storel_epi64((__m128i *) (pDst + iDstStride + i), (dst1));
+        }
+        pDst += iDstStride * 2;
+        piSrc += iRecStride2 * 2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          int s = 2;
+          s += piSrc[2 * i + 1];
+          s += piSrc[2 * i - 1];
+          s += piSrc[2 * i + 1 + iRecStride];
+          s += piSrc[2 * i + iRecStride - 1];
+          pDst[i] = s >> 2;
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else if (filterType == 29)
+  {
+    __m128i src1, src2, src3, src4;
+    __m128i dst1, dst2;
+    if (filterWidth % 8 == 0)
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 8)
+        {
+          int i2 = (i << 1) - 1;
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2));                    // a0-7
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + 8 + i2));                // a8-15
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2));       // a0-7
+          src4   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + 8 + i2));   // a8-15
+          dst1   = _mm_hadd_epi16(src1, src2);
+          dst2   = _mm_hsub_epi16(src3, src4);
+          dst1   = _mm_add_epi16(dst1, dst2);
+          _mm_store_si128((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else if (filterWidth % 4 == 0)
+    {
+      assert(filterHeight % 2 == 0);
+      for (int j = 0; j < filterHeight; j += 2)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = (i << 1) - 1;
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2));                              // a0-7
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride2 + i2));                // a8-15
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2));                 // a0-7
+          src4   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + iRecStride2 + i2));   // a8-15
+          dst1   = _mm_hadd_epi16(src1, src2);
+          dst2   = _mm_hsub_epi16(src3, src4);
+          dst1   = _mm_add_epi16(dst1, dst2);
+          _mm_storel_epi64((__m128i *) (pDst + i), (dst1));
+          dst1 = _mm_srli_si128(dst1, 8);
+          _mm_storel_epi64((__m128i *) (pDst + iDstStride + i), (dst1));
+        }
+        pDst += iDstStride * 2;
+        piSrc += iRecStride2 * 2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          pDst[i] = piSrc[2 * i] + piSrc[2 * i - 1] + piSrc[2 * i - 1 + iRecStride] - piSrc[2 * i + iRecStride];
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else if (filterType == 30)
+  {
+    __m128i src1, src2, src3, src4;
+    __m128i dst1, dst2;
+    if (filterWidth % 8 == 0)
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 8)
+        {
+          int i2 = (i << 1);
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2));                    // a0-7
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + 8 + i2));                // a8-15
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2));       // a0-7
+          src4   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + 8 + i2));   // a8-15
+          dst1   = _mm_hadd_epi16(src1, src2);
+          dst2   = _mm_hsub_epi16(src3, src4);
+          dst1   = _mm_sub_epi16(dst1, dst2);
+          _mm_store_si128((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else if (filterWidth % 4 == 0)
+    {
+      assert(filterHeight % 2 == 0);
+      for (int j = 0; j < filterHeight; j += 2)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = (i << 1);
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2));                              // a0-7
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride2 + i2));                // a8-15
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + i2));                 // a0-7
+          src4   = _mm_loadu_si128((__m128i *) (piSrc + iRecStride + iRecStride2 + i2));   // a8-15
+          dst1   = _mm_hadd_epi16(src1, src2);
+          dst2   = _mm_hsub_epi16(src3, src4);
+          dst1   = _mm_sub_epi16(dst1, dst2);
+          _mm_storel_epi64((__m128i *) (pDst + i), (dst1));
+          dst1 = _mm_srli_si128(dst1, 8);
+          _mm_storel_epi64((__m128i *) (pDst + iDstStride + i), (dst1));
+        }
+        pDst += iDstStride * 2;
+        piSrc += iRecStride2 * 2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          pDst[i] = piSrc[2 * i] + piSrc[2 * i + 1] + piSrc[2 * i + 1 + iRecStride] - piSrc[2 * i + iRecStride];
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else if (filterType == 31)
+  {
+    __m128i src1, src2, src3;
+    __m128i dst1;
+    if (filterWidth % 4 == 0)
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = (i << 1);
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2 - 1));
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + i2 + iRecStride));
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + i2 + 1));
+          dst1   = _mm_add_epi16(src1, src2);
+          dst1   = _mm_add_epi16(dst1, src3);
+          dst1   = _mm_shufflelo_epi16(dst1, 0xD8);   // 0 2 1 3
+          dst1   = _mm_shufflehi_epi16(dst1, 0xD8);   // 0 2 1 3
+          dst1   = _mm_shuffle_epi32(dst1, 0xD8);
+          _mm_storel_epi64((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          pDst[i] = piSrc[2 * i - 1] + piSrc[2 * i + 1] + piSrc[2 * i + iRecStride];
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else if (filterType == 32)
+  {
+    __m128i src1, src2, src3;
+    __m128i dst1;
+    if (filterWidth % 4 == 0)
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i += 4)
+        {
+          int i2 = (i << 1);
+          src1   = _mm_loadu_si128((__m128i *) (piSrc + i2 + iRecStride - 1));
+          src2   = _mm_loadu_si128((__m128i *) (piSrc + i2));
+          src3   = _mm_loadu_si128((__m128i *) (piSrc + i2 + iRecStride + 1));
+          dst1   = _mm_add_epi16(src1, src2);
+          dst1   = _mm_add_epi16(dst1, src3);
+          dst1   = _mm_shufflelo_epi16(dst1, 0xD8);   // 0 2 1 3
+          dst1   = _mm_shufflehi_epi16(dst1, 0xD8);   // 0 2 1 3
+          dst1   = _mm_shuffle_epi32(dst1, 0xD8);
+          _mm_storel_epi64((__m128i *) (pDst + i), dst1);
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+    else
+    {
+      for (int j = 0; j < filterHeight; j++)
+      {
+        for (int i = 0; i < filterWidth; i++)
+        {
+          pDst[i] = piSrc[2 * i + iRecStride - 1] + piSrc[2 * i + iRecStride + 1] + piSrc[2 * i];
+        }
+        pDst += iDstStride;
+        piSrc += iRecStride2;
+      }
+    }
+  }
+  else
+  {
+    THROW("Invalid filter index!");
+  }
+}
+#endif
 template <X86_VEXT vext>
 void IntraPrediction::_initIntraX86()
 {
@@ -381,6 +2190,10 @@ void IntraPrediction::_initIntraX86()
 #if JVET_W0123_TIMD_FUSION && INTRA_TRANS_ENC_OPT
   m_timdBlending = timdBlendingSIMD<vext>;
 #endif
+
+#if SELF_AWARE_FILTERING_ESTIMATION && INTRA_TRANS_ENC_OPT
+  m_safeFilterSamples = xSafeFilterSamples_sse<vext>;
+#endif
 }
 
 template void IntraPrediction::_initIntraX86<SIMDX86>();
diff --git a/source/Lib/DecoderLib/CABACReader.cpp b/source/Lib/DecoderLib/CABACReader.cpp
index 6e830aeb..d7fdd3f2 100644
--- a/source/Lib/DecoderLib/CABACReader.cpp
+++ b/source/Lib/DecoderLib/CABACReader.cpp
@@ -2364,12 +2364,16 @@ void CABACReader::cccmFlag(PredictionUnit& pu)
 void CABACReader::intra_chroma_pred_mode(PredictionUnit& pu)
 {
   RExt__DECODER_DEBUG_BIT_STATISTICS_CREATE_SET_SIZE2(STATS__CABAC_BITS__INTRA_DIR_ANG, pu.cu->blocks[pu.chType].lumaSize(), CHANNEL_TYPE_CHROMA);
+#if SELF_AWARE_FILTERING_ESTIMATION
+  pu.safeCCLMFlag = 0;
+#endif  
+  
   if (pu.cu->colorTransform)
   {
     pu.intraDir[CHANNEL_TYPE_CHROMA] = DM_CHROMA_IDX;
     return;
   }
-
+  
   // LM chroma mode
 #if CCLM_LATENCY_RESTRICTION_RMV
   if (pu.cs->sps->getUseLMChroma() )
@@ -2381,6 +2385,13 @@ void CABACReader::intra_chroma_pred_mode(PredictionUnit& pu)
     if (isLMCMode)
     {
       intra_chroma_lmc_mode(pu);
+#if SELF_AWARE_FILTERING_ESTIMATION
+      if (PU::useSafeCCLM(pu, pu.intraDir[1]))
+      {
+
+        pu.safeCCLMFlag = m_BinDecoder.decodeBin(Ctx::SafeCCLMFlag(0));
+      }
+#endif
       return;
     }
   }
diff --git a/source/Lib/DecoderLib/DecCu.cpp b/source/Lib/DecoderLib/DecCu.cpp
index 230f322f..61cfe0e4 100644
--- a/source/Lib/DecoderLib/DecCu.cpp
+++ b/source/Lib/DecoderLib/DecCu.cpp
@@ -479,7 +479,7 @@ void DecCu::xIntraRecBlk( TransformUnit& tu, const ComponentID compID )
     return;
   }
 
-        CodingStructure &cs = *tu.cs;
+  CodingStructure &cs = *tu.cs;
   const CompArea &area      = tu.blocks[compID];
 #if SIGN_PREDICTION
   const bool  isJCCR = tu.jointCbCr && isChroma(compID);
@@ -563,6 +563,14 @@ void DecCu::xIntraRecBlk( TransformUnit& tu, const ComponentID compID )
   if( compID != COMPONENT_Y && PU::isLMCMode( uiChFinalMode ) )
   {
     const PredictionUnit& pu = *tu.cu->firstPU;
+#if SELF_AWARE_FILTERING_ESTIMATION
+    if (PU::isSafeCCLM(pu))
+    {
+      tu.cu->firstPU->filterIdxForCCLM[compID == COMPONENT_Cb ? 0 : 1] = m_pcIntraPred->xDeriveCCLMFilter(compID, pu, area);
+      m_pcIntraPred->xGetLumaRecPixels(pu, area, (int)compID, tu.cu->firstPU->filterIdxForCCLM[compID == COMPONENT_Cb ? 0 : 1]);
+    }
+    else
+#endif
     m_pcIntraPred->xGetLumaRecPixels( pu, area );
     m_pcIntraPred->predIntraChromaLM( compID, piPred, pu, area, uiChFinalMode );
   }
@@ -632,6 +640,14 @@ void DecCu::xIntraRecBlk( TransformUnit& tu, const ComponentID compID )
     if( PU::isLMCMode( uiChFinalMode ) )
     {
       const PredictionUnit& pu = *tu.cu->firstPU;
+#if SELF_AWARE_FILTERING_ESTIMATION
+      if (PU::isSafeCCLM(pu))
+      {
+        tu.cu->firstPU->filterIdxForCCLM[1] = m_pcIntraPred->xDeriveCCLMFilter(COMPONENT_Cr, pu, areaCr);
+        m_pcIntraPred->xGetLumaRecPixels(pu, areaCr, (int)COMPONENT_Cr, tu.cu->firstPU->filterIdxForCCLM[1]);
+      }
+      else
+#endif
       m_pcIntraPred->xGetLumaRecPixels( pu, areaCr );
       m_pcIntraPred->predIntraChromaLM( COMPONENT_Cr, piPredCr, pu, areaCr, uiChFinalMode );
     }
diff --git a/source/Lib/DecoderLib/VLCReader.cpp b/source/Lib/DecoderLib/VLCReader.cpp
index eaa5b292..8138a567 100644
--- a/source/Lib/DecoderLib/VLCReader.cpp
+++ b/source/Lib/DecoderLib/VLCReader.cpp
@@ -2336,6 +2336,9 @@ void HLSyntaxReader::parseSPS(SPS* pcSPS)
 #if TM_AMVP || TM_MRG || JVET_Z0084_IBC_TM || MULTI_PASS_DMVR
   READ_FLAG( uiCode,    "sps_dmvd_enabled_flag" );                      pcSPS->setUseDMVDMode( uiCode != 0 );
 #endif
+#if SELF_AWARE_FILTERING_ESTIMATION
+  READ_FLAG(uiCode, "sps_safe_cclm_enabled_flag"); pcSPS->setSafeCCLMFlag(uiCode != 0);
+#endif
 #if JVET_AA0132_CONFIGURABLE_TM_TOOLS && TM_AMVP
   pcSPS->setUseTMAmvpMode(false);
   if (pcSPS->getTMToolsEnableFlag())
diff --git a/source/Lib/EncoderLib/CABACWriter.cpp b/source/Lib/EncoderLib/CABACWriter.cpp
index f5134722..4ee13c5b 100644
--- a/source/Lib/EncoderLib/CABACWriter.cpp
+++ b/source/Lib/EncoderLib/CABACWriter.cpp
@@ -2116,6 +2116,16 @@ void CABACWriter::intra_chroma_pred_mode(const PredictionUnit& pu)
     if (PU::isLMCMode(intraDir))
     {
       intra_chroma_lmc_mode(pu);
+#if SELF_AWARE_FILTERING_ESTIMATION
+      if (PU::useSafeCCLM(pu, intraDir))
+      {
+        m_BinEncoder.encodeBin(pu.safeCCLMFlag, Ctx::SafeCCLMFlag(0));
+      }
+      else
+      {
+        CHECK(pu.safeCCLMFlag, "Invalid safeCCLMFlag");
+      }
+#endif
       return;
     }
   }
diff --git a/source/Lib/EncoderLib/EncCfg.h b/source/Lib/EncoderLib/EncCfg.h
index c5503db4..0549a1de 100644
--- a/source/Lib/EncoderLib/EncCfg.h
+++ b/source/Lib/EncoderLib/EncCfg.h
@@ -409,6 +409,9 @@ protected:
 #if TM_AMVP || TM_MRG || JVET_Z0084_IBC_TM || MULTI_PASS_DMVR
   bool      m_DMVDMode;
 #endif
+#if SELF_AWARE_FILTERING_ESTIMATION
+  int8_t   m_SafeCCLMFlag;
+#endif
 #if JVET_AA0132_CONFIGURABLE_TM_TOOLS
   bool      m_tmToolsEnableFlag;
 #if TM_AMVP
@@ -1363,6 +1366,11 @@ public:
   void      setUseDMVDMode                  (bool b)         { m_DMVDMode = b; }
   bool      getUseDMVDMode                  ()         const { return m_DMVDMode; }
 #endif
+#if SELF_AWARE_FILTERING_ESTIMATION
+  void   setUseSafeCCLM(int8_t b) { m_SafeCCLMFlag = b; }
+  int8_t getUseSafeCCLM() const { return m_SafeCCLMFlag; }
+#endif
+
 #if JVET_AA0132_CONFIGURABLE_TM_TOOLS
   void      setTMToolsEnableFlag            (bool b)         { m_tmToolsEnableFlag = b; }
   bool      getTMToolsEnableFlag            ()         const { return m_tmToolsEnableFlag; }
diff --git a/source/Lib/EncoderLib/EncLib.cpp b/source/Lib/EncoderLib/EncLib.cpp
index 55cfebe1..cafdb2ae 100644
--- a/source/Lib/EncoderLib/EncLib.cpp
+++ b/source/Lib/EncoderLib/EncLib.cpp
@@ -1689,6 +1689,11 @@ void EncLib::xInitSPS( SPS& sps )
 #if JVET_AA0061_IBC_MBVD
   sps.setUseIbcMbvd                         ( m_ibcMbvd );
 #endif
+
+#if SELF_AWARE_FILTERING_ESTIMATION
+  sps.setSafeCCLMFlag(m_SafeCCLMFlag);
+#endif
+
   sps.setWrapAroundEnabledFlag                      ( m_wrapAround );
 #if MULTI_HYP_PRED
   sps.setMaxNumAddHyps(m_maxNumAddHyps);
diff --git a/source/Lib/EncoderLib/IntraSearch.cpp b/source/Lib/EncoderLib/IntraSearch.cpp
index 0a655a2d..b60c6766 100644
--- a/source/Lib/EncoderLib/IntraSearch.cpp
+++ b/source/Lib/EncoderLib/IntraSearch.cpp
@@ -1884,7 +1884,11 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
 #endif
 #if JVET_Z0050_CCLM_SLOPE
     CclmOffsets bestCclmOffsets = {};
+#if SELF_AWARE_FILTERING_ESTIMATION
+    CclmOffsets satdCclmOffsetsBest[2][NUM_CHROMA_MODE];
+#else
     CclmOffsets satdCclmOffsetsBest[NUM_CHROMA_MODE];
+#endif
     int64_t     satdCclmCosts      [NUM_CHROMA_MODE] = { 0 };
 #endif
 #if JVET_AA0126_GLM
@@ -1895,7 +1899,10 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
 #if JVET_Z0050_DIMD_CHROMA_FUSION
     bool isChromaFusion = false;
 #endif
-
+#if SELF_AWARE_FILTERING_ESTIMATION
+    int bestSafeCCLMflag[MMLM_T_IDX - LM_CHROMA_IDX + 1] = { 0 , 0, 0, 0, 0, 0 };
+    pu.safeCCLMFlag = 0;
+#endif  
     //----- init mode list ----
     {
       int32_t  uiMinMode = 0;
@@ -2002,6 +2009,14 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
 
       initIntraPatternChType(cu, pu.Cb());
       initIntraPatternChType(cu, pu.Cr());
+#if SELF_AWARE_FILTERING_ESTIMATION
+      pu.intraDir[1] = LM_CHROMA_IDX;
+      pu.filterIdxForCCLM[0] = xDeriveCCLMFilter(COMPONENT_Cb, pu, pu.Cb());
+      pu.filterIdxForCCLM[1] = xDeriveCCLMFilter(COMPONENT_Cr, pu, pu.Cr());
+      xGetLumaRecPixels(pu, pu.Cb(), (int)COMPONENT_Cb, pu.filterIdxForCCLM[0]);
+      xGetLumaRecPixels(pu, pu.Cr(), (int)COMPONENT_Cr, pu.filterIdxForCCLM[1]);
+      pu.intraDir[1] = MDLM_L_IDX;
+#endif
       xGetLumaRecPixels(pu, pu.Cb());
 
 #if JVET_AA0126_GLM
@@ -2052,12 +2067,23 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
       {
         // Fill luma reference buffer for the two-sided CCLM
         pu.intraDir[1] = LM_CHROMA_IDX;
+#if SELF_AWARE_FILTERING_ESTIMATION && 0
+        xGetLumaRecPixels(pu, pu.Cb(), (int)COMPONENT_Cb, pu.filterIdxForCCLM[0]);
+        xGetLumaRecPixels(pu, pu.Cr(), (int)COMPONENT_Cr, pu.filterIdxForCCLM[1]);
+#endif
         xGetLumaRecPixels(pu, pu.Cb());
 
         for ( int mode = LM_CHROMA_IDX; mode <= MDLM_T_IDX; mode++ )
         {
+#if SELF_AWARE_FILTERING_ESTIMATION
+          int useSelfCCLM = PU::useSafeCCLM(pu, mode);
+          for (int isSelfCCLM = 0; isSelfCCLM < (useSelfCCLM + 1); isSelfCCLM++)
+          {
+            pu.safeCCLMFlag = useSelfCCLM;
+            satdCclmOffsetsBest[isSelfCCLM][mode - LM_CHROMA_IDX].setAllZero();
+#else
           satdCclmOffsetsBest[mode - LM_CHROMA_IDX].setAllZero();
-          
+#endif
           if ( PU::hasCclmDeltaFlag( pu, mode ) )
           {
             for ( int comp = COMPONENT_Cb; comp <= COMPONENT_Cr; comp++ )
@@ -2065,8 +2091,11 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
               ComponentID       compID = ComponentID( comp );
               int            deltaBest = 0;
               int64_t         satdBest = 0;
+#if SELF_AWARE_FILTERING_ESTIMATION
+              CclmOffsets& offsetsBest = satdCclmOffsetsBest[isSelfCCLM][mode - LM_CHROMA_IDX];
+#else
               CclmOffsets& offsetsBest = satdCclmOffsetsBest[mode - LM_CHROMA_IDX];
-              
+#endif
               pu.intraDir[1] = mode;
               pu.cclmOffsets.setAllZero();
 
@@ -2089,7 +2118,10 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
               satdCclmCosts[mode - LM_CHROMA_IDX] += satdBest; // Summing up Cb and Cr cost
             }
           }
+#if SELF_AWARE_FILTERING_ESTIMATION
         }
+#endif
+      }
       }
 
       pu.cclmOffsets.setAllZero();
@@ -2221,7 +2253,7 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
           chromaIntraMode = chromaCandModes[uiMode];
 
           cu.bdpcmModeChroma = 0;
-          if( PU::isLMCMode( chromaIntraMode ) && ! PU::isLMCModeEnabled( pu, chromaIntraMode ) )
+          if( PU::isLMCMode( chromaIntraMode ) && ! PU::isLMCModeEnabled( pu, chromaIntraMode ))
           {
             continue;
           }
@@ -2236,6 +2268,16 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
           }
 #endif
         }
+
+#if SELF_AWARE_FILTERING_ESTIMATION
+        int useSelfCCLM = PU::useSafeCCLM(pu, chromaIntraMode);
+        int startIdx = 0;
+        for (int isSelfCCLM = startIdx; isSelfCCLM < (useSelfCCLM + 1); isSelfCCLM++)
+        {
+          pu.safeCCLMFlag = isSelfCCLM;
+          CHECK(!PU::isLMCMode(chromaIntraMode) && isSelfCCLM, "Invliad safe CCLM checking");
+#endif  
+
         cs.setDecomp( pu.Cb(), false );
         cs.dist = baseDist;
         //----- restore context models -----
@@ -2288,7 +2330,12 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
               saveCS.tus[j]->copyComponentFrom( *orgTUs[j], area.compID );
             }
           }
-
+#if SELF_AWARE_FILTERING_ESTIMATION    
+            if(PU::isLMCMode(chromaIntraMode))
+            {
+              bestSafeCCLMflag[chromaIntraMode - LM_CHROMA_IDX] = pu.safeCCLMFlag;
+            }
+#endif  
           dBestCost  = dCost;
           uiBestDist = uiDist;
           uiBestMode = chromaIntraMode;
@@ -2302,6 +2349,9 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
           dBestNonLmCost = dCost;
         }
 #endif
+#if SELF_AWARE_FILTERING_ESTIMATION
+      }
+#endif  
       }
 
 #if JVET_AA0126_GLM
@@ -2315,6 +2365,9 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
             pu.intraDir[1] = chromaIntraMode;
             pu.glmIdc      = satdGlmIdcBest[chromaIntraMode - LM_CHROMA_IDX];
 
+#if SELF_AWARE_FILTERING_ESTIMATION
+            pu.safeCCLMFlag = 0;
+#endif
             // RD search replicated from above
             cs.setDecomp( pu.Cb(), false );
             cs.dist = baseDist;
@@ -2457,17 +2510,33 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
       {
         int chromaIntraMode = LM_CHROMA_IDX;
 #endif
+#if SELF_AWARE_FILTERING_ESTIMATION
+        int useSelfCCLM = PU::useSafeCCLM(pu, chromaIntraMode);
+        for (int isSelfCCLM = 0; isSelfCCLM < (useSelfCCLM + 1); isSelfCCLM++)
+        {
 
+#endif
         if ( PU::isLMCModeEnabled( pu, chromaIntraMode ) && PU::hasCclmDeltaFlag( pu, chromaIntraMode ) )
         {
+#if SELF_AWARE_FILTERING_ESTIMATION
+            if (satdCclmOffsetsBest[isSelfCCLM][chromaIntraMode - LM_CHROMA_IDX].isActive())
+#else
           if ( satdCclmOffsetsBest[chromaIntraMode - LM_CHROMA_IDX].isActive() )
+#endif
           {
             pu.intraDir[1] = chromaIntraMode;
+#if SELF_AWARE_FILTERING_ESTIMATION
+              pu.safeCCLMFlag = useSelfCCLM;
+              pu.cclmOffsets = satdCclmOffsetsBest[isSelfCCLM][chromaIntraMode - LM_CHROMA_IDX];
+#else
             pu.cclmOffsets = satdCclmOffsetsBest[chromaIntraMode - LM_CHROMA_IDX];
+#endif
 #if JVET_Z0050_DIMD_CHROMA_FUSION
             pu.isChromaFusion = false;
 #endif
-
+#if SELF_AWARE_FILTERING_ESTIMATION    
+              pu.safeCCLMFlag = bestSafeCCLMflag[chromaIntraMode - LM_CHROMA_IDX];
+#endif  
             // RD search replicated from above
             cs.setDecomp( pu.Cb(), false );
             cs.dist = baseDist;
@@ -2523,12 +2592,18 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
 #if JVET_Z0050_DIMD_CHROMA_FUSION
               isChromaFusion  = pu.isChromaFusion;
 #endif
+#if SELF_AWARE_FILTERING_ESTIMATION    
+                bestSafeCCLMflag[chromaIntraMode - LM_CHROMA_IDX] = pu.safeCCLMFlag;
+#endif  
 #if JVET_AA0126_GLM
               bestGlmIdc      = pu.glmIdc;
 #endif
             }
           }
         }
+#if SELF_AWARE_FILTERING_ESTIMATION    
+      }
+#endif  
       }
       
       pu.cclmOffsets.setAllZero();
@@ -2548,7 +2623,9 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
         if ( PU::cccmSingleModeAvail(pu, chromaIntraMode) || PU::cccmMultiModeAvail(pu, chromaIntraMode) )
         {
           pu.cccmFlag = 1;
-
+#if SELF_AWARE_FILTERING_ESTIMATION
+          pu.safeCCLMFlag = 0;
+#endif
           // Original RD check code replicated from above
           cs.setDecomp( pu.Cb(), false );
           cs.dist = baseDist;
@@ -2659,6 +2736,21 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
 #if JVET_AA0126_GLM
     pu.glmIdc          = bestGlmIdc;
 #endif
+#if SELF_AWARE_FILTERING_ESTIMATION    
+    pu.safeCCLMFlag = 0;
+    if (PU::isLMCMode(uiBestMode)
+ #if JVET_AA0057_CCCM
+      && (!pu.cccmFlag )
+#endif   
+#if JVET_AA0126_GLM
+      && (!pu.glmIdc.isActive())
+#endif
+      )
+    {
+
+      pu.safeCCLMFlag = bestSafeCCLMflag[uiBestMode - LM_CHROMA_IDX];
+  }
+#endif  
   }
 
   //----- restore context models -----
@@ -4459,8 +4551,9 @@ void IntraSearch::xIntraCodingTUBlock(TransformUnit &tu, const ComponentID &comp
   PelBuf         piReco                     = cs.getRecoBuf   (area);
 
   const PredictionUnit &pu                  = *cs.getPU(area.pos(), chType);
+#if !SELF_AWARE_FILTERING_ESTIMATION
   const uint32_t           uiChFinalMode        = PU::getFinalIntraMode(pu, chType);
-
+#endif
   //===== init availability pattern =====
   CHECK( tu.jointCbCr && compID == COMPONENT_Cr, "wrong combination of compID and jointCbCr" );
   bool jointCbCr = tu.jointCbCr && compID == COMPONENT_Cb;
@@ -4494,12 +4587,14 @@ void IntraSearch::xIntraCodingTUBlock(TransformUnit &tu, const ComponentID &comp
       }
 
       //===== get prediction signal =====
+#if !SELF_AWARE_FILTERING_ESTIMATION
       if(compID != COMPONENT_Y && !tu.cu->bdpcmModeChroma && PU::isLMCMode(uiChFinalMode))
       {
         xGetLumaRecPixels( pu, area );
         predIntraChromaLM( compID, piPred, pu, area, uiChFinalMode );
       }
       else
+#endif
       {
 #if JVET_V0130_INTRA_TMP
         if( PU::isTmp( pu, chType ) )
@@ -6940,7 +7035,21 @@ ChromaCbfs IntraSearch::xRecurIntraChromaCodingQT( CodingStructure &cs, Partitio
 #endif
     if( PU::isLMCMode( predMode ) )
     {
+#if SELF_AWARE_FILTERING_ESTIMATION
+      if (PU::isSafeCCLM(pu))
+      {
+        //cs.getPU(currArea.chromaPos(), CHANNEL_TYPE_CHROMA)->filterIdxForCCLM[0] = xDeriveCCLMFilter(COMPONENT_Cb, pu, pu.Cb());
+        //cs.getPU(currArea.chromaPos(), CHANNEL_TYPE_CHROMA)->filterIdxForCCLM[1] = xDeriveCCLMFilter(COMPONENT_Cr, pu, pu.Cr());
+        //xGetLumaRecPixels(pu, pu.Cb(), (int)COMPONENT_Cb, pu.filterIdxForCCLM[0]);
+        //xGetLumaRecPixels(pu, pu.Cr(), (int)COMPONENT_Cr, pu.filterIdxForCCLM[1]);
+      }
+      else
+      {
+#endif
       xGetLumaRecPixels( pu, cbArea );
+#if SELF_AWARE_FILTERING_ESTIMATION
+      }
+#endif
       predIntraChromaLM( COMPONENT_Cb, piPredCb, pu, cbArea, predMode );
 #if JVET_AA0126_GLM
       xGetLumaRecPixels( pu, crArea ); // generate GLM luma samples for Cr prediction
diff --git a/source/Lib/EncoderLib/VLCWriter.cpp b/source/Lib/EncoderLib/VLCWriter.cpp
index 6f5d798b..d8a9a2c2 100644
--- a/source/Lib/EncoderLib/VLCWriter.cpp
+++ b/source/Lib/EncoderLib/VLCWriter.cpp
@@ -1442,6 +1442,10 @@ void HLSWriter::codeSPS( const SPS* pcSPS )
 #if TM_AMVP || TM_MRG || JVET_Z0084_IBC_TM || MULTI_PASS_DMVR
   WRITE_FLAG( pcSPS->getUseDMVDMode() ? 1 : 0,                                                 "sps_dmvd_enabled_flag" );
 #endif
+
+#if SELF_AWARE_FILTERING_ESTIMATION
+  WRITE_FLAG(pcSPS->getSafeCCLMFlag() ? 1 : 0,                                                "sps_safe_cclm_enabled_flag");
+#endif
 #if JVET_AA0132_CONFIGURABLE_TM_TOOLS
   if (pcSPS->getTMToolsEnableFlag())
   {
-- 
2.37.1.windows.1

