From 1532d56b07d63e9c0f8b34995866b0effe845182 Mon Sep 17 00:00:00 2001
From: Ted <jhuhong-jheng@kwai.com>
Date: Fri, 20 Jan 2023 10:03:07 -0800
Subject: [PATCH] JVET-AC0147: Subsampling is not applied to CCCM

---
 source/App/EncoderApp/EncApp.cpp         |   3 +
 source/App/EncoderApp/EncAppCfg.cpp      |   6 +
 source/App/EncoderApp/EncAppCfg.h        |   3 +
 source/Lib/CommonLib/CommonDef.h         |   4 +
 source/Lib/CommonLib/Contexts.cpp        |  30 ++
 source/Lib/CommonLib/IntraPrediction.cpp | 482 +++++++++++++++++++++++
 source/Lib/CommonLib/IntraPrediction.h   |  12 +
 source/Lib/CommonLib/Slice.h             |   7 +
 source/Lib/CommonLib/TypeDef.h           |   1 +
 source/Lib/CommonLib/Unit.cpp            |   9 +
 source/Lib/CommonLib/Unit.h              |   3 +
 source/Lib/CommonLib/UnitTools.cpp       |  40 ++
 source/Lib/DecoderLib/CABACReader.cpp    |  13 +
 source/Lib/DecoderLib/VLCReader.cpp      |   3 +
 source/Lib/EncoderLib/CABACWriter.cpp    |  12 +
 source/Lib/EncoderLib/EncCfg.h           |   7 +
 source/Lib/EncoderLib/EncCu.cpp          |   3 +
 source/Lib/EncoderLib/EncLib.cpp         |   3 +
 source/Lib/EncoderLib/IntraSearch.cpp    | 313 ++++++++++++++-
 source/Lib/EncoderLib/IntraSearch.h      |   8 +
 source/Lib/EncoderLib/VLCWriter.cpp      |   3 +
 21 files changed, 963 insertions(+), 2 deletions(-)

diff --git a/source/App/EncoderApp/EncApp.cpp b/source/App/EncoderApp/EncApp.cpp
index 96c08dd6a..3406715f5 100644
--- a/source/App/EncoderApp/EncApp.cpp
+++ b/source/App/EncoderApp/EncApp.cpp
@@ -863,6 +863,9 @@ void EncApp::xInitLibCfg()
 #if JVET_AB0155_SGPM
   m_cEncLib.setUseSgpm                                           ( m_sgpm );
 #endif
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  m_cEncLib.setUseCccm                                           ( m_cccm );
+#endif
 #if ENABLE_OBMC
   m_cEncLib.setUseObmc                                           ( m_OBMC );
 #endif
diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp
index 331e19ee1..81d29ecbe 100644
--- a/source/App/EncoderApp/EncAppCfg.cpp
+++ b/source/App/EncoderApp/EncAppCfg.cpp
@@ -1102,6 +1102,9 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
 #if JVET_AB0155_SGPM
   ( "SGPM",                                           m_sgpm,                                            true,  "Enable spatial geometric partitioning mode\n" )
 #endif
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  ( "CCCM",                                           m_cccm,                                               2,  "CCCM mode (0:off, 1:on, 2:on subsampling and no subsampling)  [default: 2]")
+#endif
 #if ENABLE_OBMC
   ("OBMC",                                            m_OBMC,                                           true, "Overlapping Block Motion Compensation")
 #endif
@@ -5235,6 +5238,9 @@ void EncAppCfg::xPrintParameter()
 #if JVET_AB0155_SGPM
   msg(VERBOSE, "SGPM:%d ", m_sgpm);
 #endif
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  msg(VERBOSE, "CCCM:%d ", m_cccm);
+#endif
 #if JVET_V0130_INTRA_TMP
   msg( VERBOSE, "IntraTMP:%d ", m_intraTMP );
   msg( VERBOSE, "IntraTmpMaxSize:%d ", m_intraTmpMaxSize );
diff --git a/source/App/EncoderApp/EncAppCfg.h b/source/App/EncoderApp/EncAppCfg.h
index 6ef67b368..3763e9fbb 100644
--- a/source/App/EncoderApp/EncAppCfg.h
+++ b/source/App/EncoderApp/EncAppCfg.h
@@ -463,6 +463,9 @@ protected:
 #if JVET_AB0155_SGPM
   bool      m_sgpm;
 #endif
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  int       m_cccm;
+#endif
 #if ENABLE_OBMC
   bool      m_OBMC;
 #endif
diff --git a/source/Lib/CommonLib/CommonDef.h b/source/Lib/CommonLib/CommonDef.h
index 474172b0d..20c247d8b 100644
--- a/source/Lib/CommonLib/CommonDef.h
+++ b/source/Lib/CommonLib/CommonDef.h
@@ -965,6 +965,10 @@ static const int CCCM_NUM_MODES           = 6;
 static const int CCCM_NUM_MODES           = 3;
 #endif
 #endif
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+static const int CCCM_NO_SUB_NUM_PARAMS   = 11;
+static const double CCCM_NO_SUB_WEIGHT    = 1.1; 
+#endif
 #endif
 
 #if JVET_AA0126_GLM
diff --git a/source/Lib/CommonLib/Contexts.cpp b/source/Lib/CommonLib/Contexts.cpp
index 4323ce25f..945e98cb6 100644
--- a/source/Lib/CommonLib/Contexts.cpp
+++ b/source/Lib/CommonLib/Contexts.cpp
@@ -3053,6 +3053,19 @@ const CtxSet ContextSetCfg::GlmFlags = ContextSetCfg::addCtxSet
 #if JVET_AA0057_CCCM
 const CtxSet ContextSetCfg::CccmFlag = ContextSetCfg::addCtxSet
 ({
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING  
+  { CNU, CNU, },
+  { CNU, CNU, },
+  { CNU, CNU, },
+  { DWS, DWS, },
+  { DWS, DWS, },
+  { DWS, DWS, },
+  { DWE, DWE, },
+  { DWE, DWE, },
+  { DWE, DWE, },
+  { DWO, DWO, },
+  { DWO, DWO, },
+#else
   { CNU, },
   { CNU, },
   { CNU, },
@@ -3064,6 +3077,7 @@ const CtxSet ContextSetCfg::CccmFlag = ContextSetCfg::addCtxSet
   { DWE, },
   { DWO, },
   { DWO, },
+#endif
 });
 #endif
 
@@ -4682,12 +4696,21 @@ const CtxSet ContextSetCfg::GlmFlags = ContextSetCfg::addCtxSet
 #if JVET_AA0057_CCCM
 const CtxSet ContextSetCfg::CccmFlag = ContextSetCfg::addCtxSet
 ({
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  { CNU, CNU, },
+  { CNU, CNU, },
+  { CNU, CNU, },
+  { DWS, DWS, },
+  { DWS, DWS, },
+  { DWS, DWS, },   
+#else
   { CNU, },
   { CNU, },
   { CNU, },
   { DWS, },
   { DWS, },
   { DWS, },
+#endif
 });
 #endif
 
@@ -5841,10 +5864,17 @@ const CtxSet ContextSetCfg::GlmFlags = ContextSetCfg::addCtxSet
 #if JVET_AA0057_CCCM
 const CtxSet ContextSetCfg::CccmFlag = ContextSetCfg::addCtxSet
 ({
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  { CNU, CNU, },
+  { CNU, CNU, },
+  { CNU, CNU, },
+  { DWS, DWS, },
+#else
   { CNU, },
   { CNU, },
   { CNU, },
   { DWS, },
+#endif
 });
 #endif
 
diff --git a/source/Lib/CommonLib/IntraPrediction.cpp b/source/Lib/CommonLib/IntraPrediction.cpp
index 73492398d..7f74d2408 100644
--- a/source/Lib/CommonLib/IntraPrediction.cpp
+++ b/source/Lib/CommonLib/IntraPrediction.cpp
@@ -126,8 +126,13 @@ IntraPrediction::IntraPrediction()
   m_pppTarPatch = NULL;
 #endif
 #if JVET_AA0057_CCCM
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  m_cccmLumaBuf[0] = nullptr;
+  m_cccmLumaBuf[1] = nullptr;
+#else
   m_cccmLumaBuf = nullptr;
 #endif
+#endif
 }
 
 IntraPrediction::~IntraPrediction()
@@ -210,9 +215,17 @@ void IntraPrediction::destroy()
 #endif
 
 #if JVET_AA0057_CCCM
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  delete[] m_cccmLumaBuf[0];
+  m_cccmLumaBuf[0] = nullptr;
+
+  delete[] m_cccmLumaBuf[1];
+  m_cccmLumaBuf[1] = nullptr;
+#else
   delete[] m_cccmLumaBuf;
   m_cccmLumaBuf = nullptr;
 #endif
+#endif
 }
 
 void IntraPrediction::init(ChromaFormat chromaFormatIDC, const unsigned bitDepthY)
@@ -358,11 +371,26 @@ void IntraPrediction::init(ChromaFormat chromaFormatIDC, const unsigned bitDepth
 #endif
 
 #if JVET_AA0057_CCCM
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  if( m_cccmLumaBuf[0] == nullptr )
+  {
+    m_cccmLumaBuf[0] = new Pel[(2 * MAX_CU_SIZE + CCCM_WINDOW_SIZE + 2 * CCCM_FILTER_PADDING) * (2 * MAX_CU_SIZE + CCCM_WINDOW_SIZE + 2 * CCCM_FILTER_PADDING)];
+  }
+
+  if( m_cccmLumaBuf[1] == nullptr )
+  {
+    const int chromaScaleX = getChannelTypeScaleX( CHANNEL_TYPE_CHROMA, chromaFormatIDC );
+    const int chromaScaleY = getChannelTypeScaleY( CHANNEL_TYPE_CHROMA, chromaFormatIDC );
+
+    m_cccmLumaBuf[1] = new Pel[(2 * MAX_CU_SIZE + CCCM_WINDOW_SIZE + (CCCM_FILTER_PADDING << chromaScaleX) + (CCCM_FILTER_PADDING << chromaScaleY)) * (2 * MAX_CU_SIZE + CCCM_WINDOW_SIZE + (CCCM_FILTER_PADDING << chromaScaleX) + (CCCM_FILTER_PADDING << chromaScaleY))];
+  }
+#else
   if (m_cccmLumaBuf == nullptr)
   {
     m_cccmLumaBuf = new Pel[(2*MAX_CU_SIZE + CCCM_WINDOW_SIZE + 2*CCCM_FILTER_PADDING) * (2*MAX_CU_SIZE + CCCM_WINDOW_SIZE + 2*CCCM_FILTER_PADDING)];
   }
 #endif
+#endif
 
 #if ENABLE_SIMD_TMP
 #ifdef TARGET_SIMD_X86
@@ -6454,6 +6482,13 @@ void IntraPrediction::xGetLumaRecPixels(const PredictionUnit &pu, CompArea chrom
 #if JVET_AA0057_CCCM
   if ( pu.cccmFlag )
   {
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+    if( pu.cccmNoSubFlag )
+    {
+      xCccmCreateLumaNoSubRef( pu, chromaArea );
+      return;
+    }
+#endif
     xCccmCreateLumaRef(pu, chromaArea);
     return;
   }
@@ -8923,6 +8958,38 @@ void IntraPrediction::xCccmSetLumaRefValue( const PredictionUnit& pu )
 
 void IntraPrediction::predIntraCCCM( const PredictionUnit &pu, PelBuf &predCb, PelBuf &predCr, int intraDir )
 {
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  if( pu.cccmNoSubFlag )
+  {
+    CccmModel<CCCM_NO_SUB_NUM_PARAMS> cccmModelCb( pu.cu->slice->getSPS()->getBitDepth( CHANNEL_TYPE_LUMA ) );
+    CccmModel<CCCM_NO_SUB_NUM_PARAMS> cccmModelCr( pu.cu->slice->getSPS()->getBitDepth( CHANNEL_TYPE_LUMA ) );
+
+#if JVET_AB0143_CCCM_TS
+    if( intraDir == LM_CHROMA_IDX || intraDir == MDLM_L_IDX || intraDir == MDLM_T_IDX )
+#else
+    if( PU::cccmSingleModeAvail( pu, intraDir ) )
+#endif
+    {
+      xCccmCalcModels( pu, cccmModelCb, cccmModelCr, 0, 0 );
+      xCccmApplyModel( pu, COMPONENT_Cb, cccmModelCb, 0, 0, predCb );
+      xCccmApplyModel( pu, COMPONENT_Cr, cccmModelCr, 0, 0, predCr );
+    }
+    else
+    {
+      // Multimode case
+      int modelThr = xCccmCalcRefAver( pu );
+
+      xCccmCalcModels( pu, cccmModelCb, cccmModelCr, 1, modelThr );
+      xCccmApplyModel( pu, COMPONENT_Cb, cccmModelCb, 1, modelThr, predCb );
+      xCccmApplyModel( pu, COMPONENT_Cr, cccmModelCr, 1, modelThr, predCr );
+
+      xCccmCalcModels( pu, cccmModelCb, cccmModelCr, 2, modelThr );
+      xCccmApplyModel( pu, COMPONENT_Cb, cccmModelCb, 2, modelThr, predCb );
+      xCccmApplyModel( pu, COMPONENT_Cr, cccmModelCr, 2, modelThr, predCr );
+    }
+  }
+  else
+#endif
   if ( pu.cccmFlag )
   {
     CccmModel<CCCM_NUM_PARAMS> cccmModelCb( pu.cu->slice->getSPS()->getBitDepth(CHANNEL_TYPE_LUMA) );
@@ -8959,6 +9026,10 @@ void IntraPrediction::xCccmApplyModel(const PredictionUnit& pu, const ComponentI
   const  ClpRng& clpRng(pu.cu->cs->slice->clpRng(compId));
   static Pel     samples[CCCM_NUM_PARAMS];
 
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  CHECK( pu.cccmNoSubFlag, "cccmNoSubFlag shall be disabled" );
+#endif
+
   CPelBuf refLumaBlk = xCccmGetLumaPuBuf(pu);
 
   for (int y = 0; y < refLumaBlk.height; y++)
@@ -8992,6 +9063,10 @@ void IntraPrediction::xCccmCalcModels(const PredictionUnit& pu, CccmModel<CCCM_N
 {
   int areaWidth, areaHeight, refSizeX, refSizeY, refPosPicX, refPosPicY;
 
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  CHECK( pu.cccmNoSubFlag, "cccmNoSubFlag shall be disabled" );
+#endif
+
   const CPelBuf recoCb  = pu.cs->picture->getRecoBuf(COMPONENT_Cb);
   const CPelBuf recoCr  = pu.cs->picture->getRecoBuf(COMPONENT_Cr);
   PelBuf        refLuma = xCccmGetLumaRefBuf(pu, areaWidth, areaHeight, refSizeX, refSizeY, refPosPicX, refPosPicY);
@@ -9076,13 +9151,376 @@ void IntraPrediction::xCccmCalcModels(const PredictionUnit& pu, CccmModel<CCCM_N
   }
 }
 
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+void IntraPrediction::xCccmCalcModels( const PredictionUnit& pu, CccmModel<CCCM_NO_SUB_NUM_PARAMS> &cccmModelCb, CccmModel<CCCM_NO_SUB_NUM_PARAMS> &cccmModelCr, int modelId, int modelThr )
+{
+  int areaWidth, areaHeight, refSizeX, refSizeY, refPosPicX, refPosPicY;
+
+  const CPelBuf recoCb = pu.cs->picture->getRecoBuf( COMPONENT_Cb );
+  const CPelBuf recoCr = pu.cs->picture->getRecoBuf( COMPONENT_Cr );
+
+  CHECK( !pu.cccmNoSubFlag, "cccmNoSubFlag shall be enabled" );
+
+  PelBuf refLuma = xCccmGetLumaRefBuf( pu, areaWidth, areaHeight, refSizeX, refSizeY, refPosPicX, refPosPicY );
+
+  int sampleNum = 0;
+  const int chromaScaleX = getChannelTypeScaleX( CHANNEL_TYPE_CHROMA, pu.cu->slice->getSPS()->getChromaFormatIdc() );
+  const int chromaScaleY = getChannelTypeScaleY( CHANNEL_TYPE_CHROMA, pu.cu->slice->getSPS()->getChromaFormatIdc() );
+  const int stepX = 1 << chromaScaleX;
+  const int stepY = 1 << chromaScaleY;
+
+#if JVET_AB0174_CCCM_DIV_FREE
+  int chromaOffsetCb = 1 << ( pu.cu->slice->getSPS()->getBitDepth( CHANNEL_TYPE_CHROMA ) - 1 );
+  int chromaOffsetCr = 1 << ( pu.cu->slice->getSPS()->getBitDepth( CHANNEL_TYPE_CHROMA ) - 1 );
+
+  if( refSizeX || refSizeY )
+  {
+    int refPosX = refSizeX > 0 ? refSizeX - 1 : 0;
+    int refPosY = refSizeY > 0 ? refSizeY - 1 : 0;
+
+    refPosX = (refPosX + refPosPicX) >> chromaScaleX;
+    refPosY = (refPosY + refPosPicY) >> chromaScaleY;
+
+    chromaOffsetCb = recoCb.at( refPosX, refPosY );
+    chromaOffsetCr = recoCr.at( refPosX, refPosY );
+  }
+#endif
+
+  // Collect reference data to input matrix A and target vector Y
+  static Pel A[CCCM_NO_SUB_NUM_PARAMS][CCCM_MAX_REF_SAMPLES];
+  static Pel YCb[CCCM_MAX_REF_SAMPLES];
+  static Pel YCr[CCCM_MAX_REF_SAMPLES];
+
+#if JVET_AB0143_CCCM_TS
+  int yStart = pu.cccmFlag == 2 ? refSizeY : 0;
+  int yEnd = pu.cccmFlag == 3 ? refSizeY : areaHeight;
+  int xStart = pu.cccmFlag == 3 ? refSizeX : 0;
+  int xEnd = pu.cccmFlag == 2 ? refSizeX : areaWidth;
+#else
+  int yStart = 0;
+  int yEnd = areaHeight;
+  int xStart = 0;
+  int xEnd = areaWidth;
+#endif
+
+  for( int y = yStart; y < yEnd; y += stepY )
+  {
+    for( int x = xStart; x < xEnd; x += stepX )
+    {
+      if( x >= refSizeX && y >= refSizeY )
+      {
+        continue;
+      }
+
+      const Pel* src0 = refLuma.bufAt( x, y );
+      const Pel* src1 = refLuma.bufAt( x, y + 1 );
+
+      if( modelId == 1 && src0[0] > modelThr )   // Model 1: Include only samples below or equal to the threshold
+      {
+        continue;
+      }
+      if( modelId == 2 && src0[0] <= modelThr )   // Model 2: Include only samples above the threshold
+      {
+        continue;
+      }
+
+      A[0][sampleNum] = src0[0];
+      A[1][sampleNum] = src0[-1];
+      A[2][sampleNum] = src0[1];
+      A[3][sampleNum] = src1[0];
+      A[4][sampleNum] = src1[-1];
+      A[5][sampleNum] = src1[1];
+      A[6][sampleNum] = cccmModelCb.nonlinear( src0[0] );
+      A[7][sampleNum] = cccmModelCb.nonlinear( src1[0] );
+      A[8][sampleNum] = cccmModelCb.nonlinear( src0[1] );
+      A[9][sampleNum] = cccmModelCb.nonlinear( src0[-1] );
+      A[10][sampleNum] = cccmModelCb.bias();
+
+      const int refPosX = (refPosPicX + x) >> chromaScaleX;
+      const int refPosY = (refPosPicY + y) >> chromaScaleY;
+
+      YCb[sampleNum] = recoCb.at( refPosX, refPosY );
+      YCr[sampleNum++] = recoCr.at( refPosX, refPosY );
+    }
+  }
+
+  if( !sampleNum ) // Number of samples can go to zero in the multimode case
+  {
+    cccmModelCb.clearModel();
+    cccmModelCr.clearModel();
+  }
+  else
+  {
+#if JVET_AB0174_CCCM_DIV_FREE
+    m_cccmNoSubSolver.solve2( A, YCb, YCr, sampleNum, chromaOffsetCb, chromaOffsetCr, cccmModelCb, cccmModelCr );
+#else
+    m_cccmNoSubSolver.solve2( A, YCb, YCr, sampleNum, cccmModelCb, cccmModelCr );
+#endif
+  }
+}
+
+void IntraPrediction::xCccmApplyModel( const PredictionUnit& pu, const ComponentID compId, CccmModel<CCCM_NO_SUB_NUM_PARAMS> &cccmModel, int modelId, int modelThr, PelBuf &piPred ) const
+{
+  const  ClpRng& clpRng( pu.cu->cs->slice->clpRng( compId ) );
+  static Pel     samples[CCCM_NO_SUB_NUM_PARAMS];
+
+  CHECK( !pu.cccmNoSubFlag, "cccmNoSubFlag shall be enabled" );
+
+  CPelBuf refLumaBlk = xCccmGetLumaPuBuf( pu );
+  const int chromaScaleX = getChannelTypeScaleX( CHANNEL_TYPE_CHROMA, pu.cu->slice->getSPS()->getChromaFormatIdc() );
+  const int chromaScaleY = getChannelTypeScaleY( CHANNEL_TYPE_CHROMA, pu.cu->slice->getSPS()->getChromaFormatIdc() );
+  const int stepX = 1 << chromaScaleX;
+  const int stepY = 1 << chromaScaleY;
+
+  for( int y = 0; y < refLumaBlk.height; y += stepY )
+  {
+    for( int x = 0; x < refLumaBlk.width; x += stepX )
+    {
+      const Pel* src0 = refLumaBlk.bufAt( x, y );
+      const Pel* src1 = refLumaBlk.bufAt( x, y + 1 );
+
+      if( modelId == 1 && src0[0] > modelThr )   // Model 1: Include only samples below or equal to the threshold
+      {
+        continue;
+      }
+      if( modelId == 2 && src0[0] <= modelThr )   // Model 2: Include only samples above the threshold
+      {
+        continue;
+      }
+
+      samples[0] = src0[0];
+      samples[1] = src0[-1];
+      samples[2] = src0[1];
+      samples[3] = src1[0];
+      samples[4] = src1[-1];
+      samples[5] = src1[1];
+      samples[6] = cccmModel.nonlinear( src0[0] );
+      samples[7] = cccmModel.nonlinear( src1[0] );
+      samples[8] = cccmModel.nonlinear( src0[1] );
+      samples[9] = cccmModel.nonlinear( src0[-1] );
+      samples[10] = cccmModel.bias();
+
+      piPred.at( x >> chromaScaleX, y >> chromaScaleY ) = ClipPel<Pel>( cccmModel.convolve( samples ), clpRng );
+    }
+  }
+}
+
+void IntraPrediction::xCccmCreateLumaNoSubRef( const PredictionUnit& pu, CompArea chromaArea )
+{
+  const CPelBuf recoLuma = pu.cs->picture->getRecoBuf( COMPONENT_Y );
+  const int  maxPosPicX = pu.cs->picture->lumaSize().width - 1;
+  const int  maxPosPicY = pu.cs->picture->lumaSize().height - 1;
+
+  xCccmCalcRefArea( pu, chromaArea ); // Find the reference area
+
+  int areaWidth, areaHeight, refSizeX, refSizeY, refPosPicX, refPosPicY;
+
+  CHECK( !pu.cccmNoSubFlag, "cccmNoSubFlag shall be enabled" );
+
+  PelBuf refLuma = xCccmGetLumaRefBuf( pu, areaWidth, areaHeight, refSizeX, refSizeY, refPosPicX, refPosPicY );
+
+  const int chromaScaleX = getChannelTypeScaleX( CHANNEL_TYPE_CHROMA, pu.cu->slice->getSPS()->getChromaFormatIdc() );
+  const int chromaScaleY = getChannelTypeScaleY( CHANNEL_TYPE_CHROMA, pu.cu->slice->getSPS()->getChromaFormatIdc() );
+
+  int puBorderX = refSizeX + (m_cccmBlkArea.width << chromaScaleX);
+  int puBorderY = refSizeY + (m_cccmBlkArea.height << chromaScaleY);
+
+#if JVET_AB0174_CCCM_DIV_FREE
+  xCccmSetLumaRefValue( pu );
+#endif
+
+  const int filterPaddingX = CCCM_FILTER_PADDING << chromaScaleX;
+  const int filterPaddingY = CCCM_FILTER_PADDING << chromaScaleY;
+
+  // luma for the area covering both the PU and the top/left reference areas (+ top and left paddings)
+  for( int y = -filterPaddingY; y < areaHeight; y++ )
+  {
+    for( int x = -filterPaddingX; x < areaWidth; x++ )
+    {
+      if( (x >= puBorderX && y >= refSizeY) || (y >= puBorderY && x >= refSizeX) )
+      {
+        continue;
+      }
+
+      int chromaPosPicX = refPosPicX + x;
+      int chromaPosPicY = refPosPicY + y;
+
+      chromaPosPicX = chromaPosPicX < 0 ? 0 : chromaPosPicX > maxPosPicX ? maxPosPicX : chromaPosPicX;
+      chromaPosPicY = chromaPosPicY < 0 ? 0 : chromaPosPicY > maxPosPicY ? maxPosPicY : chromaPosPicY;
+
+      refLuma.at( x, y ) = xCccmGetLumaVal( pu, recoLuma, chromaPosPicX, chromaPosPicY );
+    }
+  }
+
+  // Pad right of top reference area
+  for( int x = 0; x < filterPaddingX; x++ )
+  {
+    for( int y = -filterPaddingY; y < refSizeY; y++ )
+    {
+      refLuma.at( areaWidth + x, y ) = refLuma.at( areaWidth - 1, y );
+    }
+
+    // Pad right of PU
+    for( int y = refSizeY; y < puBorderY; y++ )
+    {
+      refLuma.at( puBorderX + x, y ) = refLuma.at( puBorderX - 1, y );
+    }
+
+    // Pad right of left reference area
+    for( int y = puBorderY; y < areaHeight; y++ )
+    {
+      refLuma.at( refSizeX + x, y ) = refLuma.at( refSizeX - 1, y );
+    }
+  }
+
+  for( int y = 0; y < filterPaddingY; y++ )
+  {
+    // Pad below left reference area
+    for( int x = -filterPaddingX; x < refSizeX + filterPaddingX; x++ )
+    {
+      refLuma.at( x, areaHeight + y ) = refLuma.at( x, areaHeight - 1 );
+    }
+
+    // Pad below PU
+    for( int x = refSizeX; x < puBorderX + filterPaddingX; x++ )
+    {
+      refLuma.at( x, puBorderY + y ) = refLuma.at( x, puBorderY - 1 );
+    }
+
+    // Pad below right reference area
+    for( int x = puBorderX + filterPaddingX; x < areaWidth + filterPaddingX; x++ )
+    {
+      refLuma.at( x, refSizeY + y ) = refLuma.at( x, refSizeY - 1 );
+    }
+  }
+
+  // In dualtree we can also use luma from the right and below (if not on CTU/picture boundary)
+  if( CS::isDualITree( *pu.cs ) )
+  {
+    int ctuWidth = pu.cs->sps->getMaxCUWidth() >> getComponentScaleX( COMPONENT_Cb, pu.chromaFormat );
+    int ctuHeight = pu.cs->sps->getMaxCUHeight() >> getComponentScaleY( COMPONENT_Cb, pu.chromaFormat );
+
+    // Samples right of top reference area
+    int padPosPicX = refPosPicX + areaWidth;
+
+    if( padPosPicX <= maxPosPicX && (padPosPicX % ctuWidth) )
+    {
+      for( int y = -filterPaddingY; y < refSizeY; y++ )
+      {
+        int chromaPosPicY = refPosPicY + y;
+        chromaPosPicY = chromaPosPicY < 0 ? 0 : chromaPosPicY > maxPosPicY ? maxPosPicY : chromaPosPicY;
+
+        for( int x = 0; x < filterPaddingX; x++ )
+        {
+          refLuma.at( areaWidth + x, y ) = xCccmGetLumaVal( pu, recoLuma, padPosPicX, chromaPosPicY );
+        }
+      }
+    }
+
+    // Samples right of PU
+    padPosPicX = refPosPicX + puBorderX;
+
+    if( padPosPicX <= maxPosPicX && (padPosPicX % ctuWidth) )
+    {
+      for( int y = refSizeY; y < puBorderY; y++ )
+      {
+        int chromaPosPicY = refPosPicY + y;
+        chromaPosPicY = chromaPosPicY < 0 ? 0 : chromaPosPicY > maxPosPicY ? maxPosPicY : chromaPosPicY;
+
+        for( int x = 0; x < filterPaddingX; x++ )
+        {
+          refLuma.at( puBorderX + x, y ) = xCccmGetLumaVal( pu, recoLuma, padPosPicX, chromaPosPicY );
+        }
+      }
+    }
+
+    // Samples right of left reference area
+    padPosPicX = refPosPicX + refSizeX;
+
+    if( padPosPicX <= maxPosPicX )
+    {
+      for( int y = puBorderY; y < areaHeight; y++ )
+      {
+        int chromaPosPicY = refPosPicY + y;
+        chromaPosPicY = chromaPosPicY < 0 ? 0 : chromaPosPicY > maxPosPicY ? maxPosPicY : chromaPosPicY;
+
+        for( int x = 0; x < filterPaddingX; x++ )
+        {
+          refLuma.at( refSizeX + x, y ) = xCccmGetLumaVal( pu, recoLuma, padPosPicX, chromaPosPicY );
+        }
+      }
+    }
+
+    // Samples below left reference area
+    int padPosPicY = refPosPicY + areaHeight;
+
+    if( padPosPicY <= maxPosPicY && (padPosPicY % ctuHeight) )
+    {
+      for( int x = -filterPaddingX; x < refSizeX + filterPaddingX; x++ )
+      {
+        int chromaPosPicX = refPosPicX + x;
+        chromaPosPicX = chromaPosPicX < 0 ? 0 : chromaPosPicX > maxPosPicX ? maxPosPicX : chromaPosPicX;
+
+        for( int y = 0; y < filterPaddingY; y++ )
+        {
+          refLuma.at( x, areaHeight + y ) = xCccmGetLumaVal( pu, recoLuma, chromaPosPicX, padPosPicY );
+        }
+      }
+    }
+
+    // Samples below PU
+    padPosPicY = refPosPicY + puBorderY;
+
+    if( padPosPicY <= maxPosPicY && (padPosPicY % ctuHeight) )
+    {
+      for( int x = refSizeX; x < puBorderX; x++ ) // Just go to PU border as the next sample may be out of CTU (and not needed anyways)
+      {
+        int chromaPosPicX = refPosPicX + x;
+        chromaPosPicX = chromaPosPicX < 0 ? 0 : chromaPosPicX > maxPosPicX ? maxPosPicX : chromaPosPicX;
+
+        for( int y = 0; y < filterPaddingY; y++ )
+        {
+          refLuma.at( x, puBorderY + y ) = xCccmGetLumaVal( pu, recoLuma, chromaPosPicX, padPosPicY );
+        }
+      }
+    }
+
+    // Samples below right reference area
+    padPosPicY = refPosPicY + refSizeY;
+
+    if( padPosPicY <= maxPosPicY )
+    {
+      // Avoid going outside of right CTU border where these samples are not yet available
+      int puPosPicX = pu.blocks[COMPONENT_Cb].x;
+      int ctuRightEdgeDist = ctuWidth - (puPosPicX % ctuWidth) + refSizeX;
+      int lastPosX = ctuRightEdgeDist < areaWidth ? ctuRightEdgeDist : areaWidth;
+
+      for( int x = puBorderX + 1; x < lastPosX; x++ ) // Just go to ref area border as the next sample may be out of CTU (and not needed anyways)
+      {
+        int chromaPosPicX = refPosPicX + x;
+        chromaPosPicX = chromaPosPicX < 0 ? 0 : chromaPosPicX > maxPosPicX ? maxPosPicX : chromaPosPicX;
+
+        for( int y = 0; y < filterPaddingY; y++ )
+        {
+          refLuma.at( x, refSizeY + y ) = xCccmGetLumaVal( pu, recoLuma, chromaPosPicX, padPosPicY );
+        }
+      }
+    }
+  }
+}
+#endif
+
 // Calculate a single downsampled luma reference value (copied from IntraPrediction::xGetLumaRecPixels)
 Pel IntraPrediction::xCccmGetLumaVal(const PredictionUnit& pu, const CPelBuf pi, const int x, const int y) const
 {
   const Pel* piSrc = pi.buf;
   const int iRecStride = pi.stride;
   Pel ypval = 0;
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  if( pu.cccmNoSubFlag || pu.chromaFormat == CHROMA_444 )
+#else
   if (pu.chromaFormat == CHROMA_444)
+#endif
   {
     ypval = piSrc[x + iRecStride * y];
   }
@@ -9177,6 +9615,25 @@ void IntraPrediction::xCccmCalcRefArea(const PredictionUnit& pu, CompArea chroma
 // Return downsampled luma buffer that contains PU and the reference areas above and left of the PU
 PelBuf IntraPrediction::xCccmGetLumaRefBuf(const PredictionUnit& pu, int &areaWidth, int &areaHeight, int &refSizeX, int &refSizeY, int &refPosPicX, int &refPosPicY ) const
 {
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  if( pu.cccmNoSubFlag )
+  {
+    const int chromaScaleX = getChannelTypeScaleX( CHANNEL_TYPE_CHROMA, pu.cu->slice->getSPS()->getChromaFormatIdc() );
+    const int chromaScaleY = getChannelTypeScaleY( CHANNEL_TYPE_CHROMA, pu.cu->slice->getSPS()->getChromaFormatIdc() );
+
+    refPosPicX = m_cccmRefArea.x << chromaScaleX;                          // Position of the reference area in picture coordinates
+    refPosPicY = m_cccmRefArea.y << chromaScaleY;
+    refSizeX   = ((m_cccmBlkArea.x - m_cccmRefArea.x) << chromaScaleX);    // Reference lines available left and above
+    refSizeY   = ((m_cccmBlkArea.y - m_cccmRefArea.y) << chromaScaleY);
+    areaWidth  = m_cccmRefArea.width << chromaScaleX;                      // Reference buffer size excluding paddings
+    areaHeight = m_cccmRefArea.height << chromaScaleY;
+
+    int refStride = areaWidth + 2 * (CCCM_FILTER_PADDING << chromaScaleX); // Including paddings required for the 2D filter
+    int refOrigin = refStride * (CCCM_FILTER_PADDING << chromaScaleY) + (CCCM_FILTER_PADDING << chromaScaleX);
+    return PelBuf( m_cccmLumaBuf[1] + refOrigin, refStride, areaWidth, areaHeight ); // Points to the top-left corner of the reference area 
+  }
+#endif
+
   refSizeX   = m_cccmBlkArea.x - m_cccmRefArea.x; // Reference lines available left and above
   refSizeY   = m_cccmBlkArea.y - m_cccmRefArea.y;
   areaWidth  = m_cccmRefArea.width;               // Reference buffer size excluding paddings
@@ -9187,12 +9644,33 @@ PelBuf IntraPrediction::xCccmGetLumaRefBuf(const PredictionUnit& pu, int &areaWi
   int refStride = areaWidth + 2 * CCCM_FILTER_PADDING; // Including paddings required for the 2D filter
   int refOrigin = refStride * CCCM_FILTER_PADDING + CCCM_FILTER_PADDING;
 
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  return PelBuf( m_cccmLumaBuf[0] + refOrigin, refStride, areaWidth, areaHeight ); // Points to the top-left corner of the reference area
+#else
   return PelBuf(m_cccmLumaBuf + refOrigin, refStride, areaWidth, areaHeight); // Points to the top-left corner of the reference area
+#endif
 }
 
 // Return downsampled luma buffer for a PU
 PelBuf IntraPrediction::xCccmGetLumaPuBuf(const PredictionUnit& pu) const
 {
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  if( pu.cccmNoSubFlag )
+  {
+    const int chromaScaleX = getChannelTypeScaleX( CHANNEL_TYPE_CHROMA, pu.cu->slice->getSPS()->getChromaFormatIdc() );
+    const int chromaScaleY = getChannelTypeScaleY( CHANNEL_TYPE_CHROMA, pu.cu->slice->getSPS()->getChromaFormatIdc() );
+
+    int refSizeX = ((m_cccmBlkArea.x - m_cccmRefArea.x) << chromaScaleX); // Reference lines available left and above
+    int refSizeY = ((m_cccmBlkArea.y - m_cccmRefArea.y) << chromaScaleY);
+    int tuWidth  = m_cccmBlkArea.width << chromaScaleX;
+    int tuHeight = m_cccmBlkArea.height << chromaScaleY;
+
+    int refStride = (m_cccmRefArea.width + 2 * CCCM_FILTER_PADDING) << chromaScaleX; // Including paddings required for the 2D filter
+    int refOrigin = refStride * (refSizeY + (CCCM_FILTER_PADDING << chromaScaleY)) + refSizeX + (CCCM_FILTER_PADDING << chromaScaleX);
+    return PelBuf( m_cccmLumaBuf[1] + refOrigin, refStride, tuWidth, tuHeight );  // Points to the top-left corner of the block
+  }
+#endif
+
   int refSizeX  = m_cccmBlkArea.x - m_cccmRefArea.x; // Reference lines available left and above
   int refSizeY  = m_cccmBlkArea.y - m_cccmRefArea.y;
   int tuWidth   = m_cccmBlkArea.width;
@@ -9200,7 +9678,11 @@ PelBuf IntraPrediction::xCccmGetLumaPuBuf(const PredictionUnit& pu) const
   int refStride = m_cccmRefArea.width + 2 * CCCM_FILTER_PADDING; // Including paddings required for the 2D filter
   int refOrigin = refStride * (refSizeY + CCCM_FILTER_PADDING) + refSizeX + CCCM_FILTER_PADDING;
 
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  return PelBuf( m_cccmLumaBuf[0] + refOrigin, refStride, tuWidth, tuHeight );  // Points to the top-left corner of the block
+#else
   return PelBuf(m_cccmLumaBuf + refOrigin, refStride, tuWidth, tuHeight);  // Points to the top-left corner of the block
+#endif
 }
 
 int IntraPrediction::xCccmCalcRefAver(const PredictionUnit& pu) const
diff --git a/source/Lib/CommonLib/IntraPrediction.h b/source/Lib/CommonLib/IntraPrediction.h
index 96c332be4..ef56518da 100644
--- a/source/Lib/CommonLib/IntraPrediction.h
+++ b/source/Lib/CommonLib/IntraPrediction.h
@@ -197,7 +197,11 @@ private:
 #if JVET_AA0057_CCCM
   Area m_cccmBlkArea;
   Area m_cccmRefArea;
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  Pel* m_cccmLumaBuf[2];
+#else
   Pel* m_cccmLumaBuf;
+#endif
 #if JVET_AB0174_CCCM_DIV_FREE
   int  m_cccmLumaOffset;
 #endif
@@ -300,6 +304,9 @@ protected:
 #if JVET_AB0092_GLM_WITH_LUMA
   CccmCovariance<GLM_NUM_PARAMS, GLM_MAX_REF_SAMPLES> m_glmSolver;
 #endif
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  CccmCovariance<CCCM_NO_SUB_NUM_PARAMS, GLM_MAX_REF_SAMPLES> m_cccmNoSubSolver;
+#endif
 
   // prediction
   void xPredIntraPlanar           ( const CPelBuf &pSrc, PelBuf &pDst
@@ -390,6 +397,11 @@ public:
 #if JVET_AB0174_CCCM_DIV_FREE
   void   xCccmSetLumaRefValue     (const PredictionUnit& pu);
 #endif
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  void   xCccmCreateLumaNoSubRef  ( const PredictionUnit& pu, CompArea chromaArea );
+  void   xCccmApplyModel          ( const PredictionUnit& pu, const ComponentID compId, CccmModel<CCCM_NO_SUB_NUM_PARAMS> &cccmModel, int modelId, int modelThr, PelBuf &piPred ) const;
+  void   xCccmCalcModels          ( const PredictionUnit& pu, CccmModel<CCCM_NO_SUB_NUM_PARAMS> &cccmModelCb, CccmModel<CCCM_NO_SUB_NUM_PARAMS> &cccmModelCr, int modelId, int modelThr );
+#endif
 #endif
 #if JVET_AB0092_GLM_WITH_LUMA
   void   xGlmCalcModel            (const PredictionUnit& pu, const ComponentID compId, const CompArea& chromaArea, CccmModel<GLM_NUM_PARAMS> &glmModel);
diff --git a/source/Lib/CommonLib/Slice.h b/source/Lib/CommonLib/Slice.h
index 0d24575d7..2555c41fb 100644
--- a/source/Lib/CommonLib/Slice.h
+++ b/source/Lib/CommonLib/Slice.h
@@ -1727,6 +1727,9 @@ private:
 #if JVET_AB0155_SGPM
   bool              m_sgpm;
 #endif
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  int               m_cccm;
+#endif
 #if JVET_V0130_INTRA_TMP
   bool              m_intraTMP;                                       ///< intra Template Matching 
   unsigned          m_intraTmpMaxSize;                               ///< max CU size for which intra TMP is allowed
@@ -2272,6 +2275,10 @@ void                    setCCALFEnabledFlag( bool b )
   void      setUseSgpm         (bool b)                                          { m_sgpm = b; }
   bool      getUseSgpm         ()                                      const     { return m_sgpm; }
 #endif
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  void      setUseCccm( int i )                                                  { m_cccm = i; }
+  int       getUseCccm()                                               const     { return m_cccm; }
+#endif
 
 #if ENABLE_OBMC
   void      setUseOBMC         ( bool b )                                        { m_OBMC = b; }
diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index c6960dd4a..9cdc466cd 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -147,6 +147,7 @@
 #define JVET_AA0057_CCCM                                  1 // JVET-AA0057: Convolutional cross-component model (CCCM)
 #if JVET_AA0057_CCCM
 #define JVET_AB0143_CCCM_TS                               1 // JVET-AB0143: CCCM template selection
+#define JVET_AC0147_CCCM_NO_SUBSAMPLING                   1 // JVET-AC0147: Subsampling is not applied to CCCM
 #endif
 #define JVET_AA0126_GLM                                   1 // JVET-AA0126: Gradient linear model
 #if JVET_AA0126_GLM
diff --git a/source/Lib/CommonLib/Unit.cpp b/source/Lib/CommonLib/Unit.cpp
index 3f2d00b06..2781e3a5c 100644
--- a/source/Lib/CommonLib/Unit.cpp
+++ b/source/Lib/CommonLib/Unit.cpp
@@ -739,6 +739,9 @@ void PredictionUnit::initData()
 #endif
 #if JVET_AA0057_CCCM
   cccmFlag    = 0;
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  cccmNoSubFlag = 0;
+#endif
 #endif
   // inter data
   mergeFlag   = false;
@@ -873,6 +876,9 @@ PredictionUnit& PredictionUnit::operator=(const IntraPredictionData& predData)
 #endif
 #if JVET_AA0057_CCCM
   cccmFlag    = predData.cccmFlag;
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  cccmNoSubFlag = predData.cccmNoSubFlag;
+#endif
 #endif
   return *this;
 }
@@ -1004,6 +1010,9 @@ PredictionUnit& PredictionUnit::operator=( const PredictionUnit& other )
 #endif
 #if JVET_AA0057_CCCM
   cccmFlag    = other.cccmFlag;
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  cccmNoSubFlag = other.cccmNoSubFlag;
+#endif
 #endif
 
   mergeFlag   = other.mergeFlag;
diff --git a/source/Lib/CommonLib/Unit.h b/source/Lib/CommonLib/Unit.h
index f953b7329..e0240e9a6 100644
--- a/source/Lib/CommonLib/Unit.h
+++ b/source/Lib/CommonLib/Unit.h
@@ -481,6 +481,9 @@ struct IntraPredictionData
 #endif
 #if JVET_AA0057_CCCM
   int       cccmFlag;
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  int       cccmNoSubFlag;
+#endif
 #endif
 };
 
diff --git a/source/Lib/CommonLib/UnitTools.cpp b/source/Lib/CommonLib/UnitTools.cpp
index c2863ca4b..2e8b98aa3 100644
--- a/source/Lib/CommonLib/UnitTools.cpp
+++ b/source/Lib/CommonLib/UnitTools.cpp
@@ -1845,6 +1845,16 @@ void PU::getCccmRefLineNum(const PredictionUnit& pu, const Area area, int& th, i
 #if JVET_AA0057_CCCM
 bool PU::cccmSingleModeAvail(const PredictionUnit& pu, int intraMode)
 {
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  if ( pu.cs->sps->getUseCccm() == 0 )
+  {    
+    return false;
+  }
+  else if( ( pu.cs->sps->getUseCccm() == 1 ) && ( pu.cccmNoSubFlag == 1 )  )
+  {    
+    return false;
+  }
+#endif
   const Area area = pu.blocks[COMPONENT_Cb];
   bool modeIsOk   = intraMode == LM_CHROMA_IDX;
   modeIsOk        = modeIsOk && ( area.width * area.height >= CCCM_MIN_PU_SIZE );
@@ -1859,6 +1869,16 @@ bool PU::cccmSingleModeAvail(const PredictionUnit& pu, int intraMode)
   
 bool PU::cccmMultiModeAvail(const PredictionUnit& pu, int intraMode)
 {
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  if ( pu.cs->sps->getUseCccm() == 0 )
+  {    
+    return false;
+  }
+  else if( ( pu.cs->sps->getUseCccm() == 1 ) && ( pu.cccmNoSubFlag == 1 )  )
+  {    
+    return false;
+  }
+#endif
 #if MMLM
   const Area area = pu.blocks[COMPONENT_Cb];
 #if JVET_AB0143_CCCM_TS
@@ -1906,6 +1926,16 @@ bool PU::cccmMultiModeAvail(const PredictionUnit& pu, int intraMode)
 #if JVET_AB0143_CCCM_TS
 bool PU::isLeftCccmMode(const PredictionUnit& pu, int intraMode)
 {
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  if ( pu.cs->sps->getUseCccm() == 0 )
+  {    
+    return false;
+  }
+  else if( ( pu.cs->sps->getUseCccm() == 1 ) && ( pu.cccmNoSubFlag == 1 )  )
+  {    
+    return false;
+  }
+#endif
   const Area area = pu.blocks[COMPONENT_Cb];
   bool modeIsOk = (intraMode == MDLM_L_IDX);
   modeIsOk = modeIsOk && (area.width * area.height >= CCCM_MIN_PU_SIZE);
@@ -1925,6 +1955,16 @@ bool PU::isLeftCccmMode(const PredictionUnit& pu, int intraMode)
 
 bool PU::isTopCccmMode(const PredictionUnit& pu, int intraMode)
 {
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  if ( pu.cs->sps->getUseCccm() == 0 )
+  {    
+    return false;
+  }
+  else if( ( pu.cs->sps->getUseCccm() == 1 ) && ( pu.cccmNoSubFlag == 1 )  )
+  {    
+    return false;
+  }
+#endif
   const Area area = pu.blocks[COMPONENT_Cb];
   bool modeIsOk = (intraMode == MDLM_T_IDX);
   modeIsOk = modeIsOk && (area.width * area.height >= CCCM_MIN_PU_SIZE);
diff --git a/source/Lib/DecoderLib/CABACReader.cpp b/source/Lib/DecoderLib/CABACReader.cpp
index 097e40526..e7fbf8aa3 100644
--- a/source/Lib/DecoderLib/CABACReader.cpp
+++ b/source/Lib/DecoderLib/CABACReader.cpp
@@ -2469,6 +2469,12 @@ bool CABACReader::intra_chroma_lmc_mode(PredictionUnit& pu)
 #if JVET_AA0057_CCCM
 void CABACReader::cccmFlag(PredictionUnit& pu)
 {
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  if ( pu.cs->sps->getUseCccm() == 0 )
+  {    
+    return;
+  }
+#endif
   const unsigned intraDir = pu.intraDir[1];
   
 #if JVET_AB0143_CCCM_TS
@@ -2513,6 +2519,13 @@ void CABACReader::cccmFlag(PredictionUnit& pu)
       pu.cccmFlag = (intraDir == MDLM_T_IDX || intraDir == MMLM_T_IDX) ? 3 : (intraDir == MDLM_L_IDX || intraDir == MMLM_L_IDX) ? 2 : 1;
 #else
       pu.cccmFlag = (intraDir == MDLM_T_IDX) ? 3 : (intraDir == MDLM_L_IDX) ? 2 : 1;
+#endif
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+      pu.cccmNoSubFlag = 0;
+      if ( pu.cs->sps->getUseCccm() == 2 )
+      {    
+        pu.cccmNoSubFlag += m_BinDecoder.decodeBin( Ctx::CccmFlag( 1 ) );
+      }         
 #endif
     }
 #endif
diff --git a/source/Lib/DecoderLib/VLCReader.cpp b/source/Lib/DecoderLib/VLCReader.cpp
index c6811834f..bbc6dee5e 100644
--- a/source/Lib/DecoderLib/VLCReader.cpp
+++ b/source/Lib/DecoderLib/VLCReader.cpp
@@ -2502,6 +2502,9 @@ void HLSyntaxReader::parseSPS(SPS* pcSPS)
 #endif
 #if JVET_AB0155_SGPM
   READ_FLAG(uiCode, "sps_sgpm_enabled_flag");                       pcSPS->setUseSgpm(uiCode != 0);
+#endif
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  READ_UVLC(uiCode, "sps_cccm_cand");                               pcSPS->setUseCccm(uiCode);
 #endif
   if( pcSPS->getChromaFormatIdc() != CHROMA_400)
   {
diff --git a/source/Lib/EncoderLib/CABACWriter.cpp b/source/Lib/EncoderLib/CABACWriter.cpp
index bf3e32091..2fdb6f726 100644
--- a/source/Lib/EncoderLib/CABACWriter.cpp
+++ b/source/Lib/EncoderLib/CABACWriter.cpp
@@ -2214,6 +2214,12 @@ void CABACWriter::intra_chroma_lmc_mode(const PredictionUnit& pu)
 #if JVET_AA0057_CCCM
 void CABACWriter::cccmFlag(const PredictionUnit& pu)
 {
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  if ( pu.cs->sps->getUseCccm() == 0 )
+  {    
+    return;
+  }
+#endif
   const unsigned intraDir = pu.intraDir[1];
   
 #if JVET_AB0143_CCCM_TS
@@ -2251,6 +2257,12 @@ void CABACWriter::cccmFlag(const PredictionUnit& pu)
 #endif
   {
     m_BinEncoder.encodeBin( pu.cccmFlag ? 1 : 0, Ctx::CccmFlag( 0 ) );
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+    if ( pu.cccmFlag && ( pu.cs->sps->getUseCccm() == 2 ) ) 
+    {
+      m_BinEncoder.encodeBin( pu.cccmNoSubFlag ? 1 : 0, Ctx::CccmFlag( 1 ) );
+    }
+#endif
   }
 }
 #endif
diff --git a/source/Lib/EncoderLib/EncCfg.h b/source/Lib/EncoderLib/EncCfg.h
index 0e8270d5b..9380a9a7d 100644
--- a/source/Lib/EncoderLib/EncCfg.h
+++ b/source/Lib/EncoderLib/EncCfg.h
@@ -470,6 +470,9 @@ protected:
 #if JVET_AB0155_SGPM
   bool      m_sgpm;
 #endif
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  int       m_cccm;
+#endif
 #if ENABLE_OBMC
   bool      m_OBMC;
 #endif
@@ -1488,6 +1491,10 @@ public:
   void      setUseSgpm                   (bool b)         { m_sgpm = b; }
   bool      getUseSgpm                   () const         { return m_sgpm; }
 #endif
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  void      setUseCccm                   (int i)          { m_cccm = i; }
+  int       getUseCccm                   () const         { return m_cccm; }
+#endif
 #if ENABLE_OBMC
   void      setUseObmc                   ( bool b )       { m_OBMC = b; }
   bool      getUseObmc                   ()         const { return m_OBMC; }
diff --git a/source/Lib/EncoderLib/EncCu.cpp b/source/Lib/EncoderLib/EncCu.cpp
index b7296561c..a52e35046 100644
--- a/source/Lib/EncoderLib/EncCu.cpp
+++ b/source/Lib/EncoderLib/EncCu.cpp
@@ -2403,6 +2403,9 @@ bool EncCu::xCheckRDCostIntra(CodingStructure *&tempCS, CodingStructure *&bestCS
 #if INTRA_TRANS_ENC_OPT
   m_pcIntraSearch->m_skipTimdLfnstMtsPass = false;
   m_modeCtrl->resetLfnstCost();
+#endif
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  m_pcIntraSearch->m_skipCCCMSATD = false;
 #endif
   for( int trGrpIdx = 0; trGrpIdx < grpNumMax; trGrpIdx++ )
   {
diff --git a/source/Lib/EncoderLib/EncLib.cpp b/source/Lib/EncoderLib/EncLib.cpp
index 57f16c82a..769ad8c4a 100644
--- a/source/Lib/EncoderLib/EncLib.cpp
+++ b/source/Lib/EncoderLib/EncLib.cpp
@@ -1617,6 +1617,9 @@ void EncLib::xInitSPS( SPS& sps )
 #if JVET_AB0155_SGPM
   sps.setUseSgpm            ( m_sgpm );
 #endif
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  sps.setUseCccm            ( m_cccm );
+#endif
 #if ENABLE_OBMC
   sps.setUseOBMC            ( m_OBMC );
 #endif
diff --git a/source/Lib/EncoderLib/IntraSearch.cpp b/source/Lib/EncoderLib/IntraSearch.cpp
index 7160deb4e..5200d238a 100644
--- a/source/Lib/EncoderLib/IntraSearch.cpp
+++ b/source/Lib/EncoderLib/IntraSearch.cpp
@@ -180,7 +180,12 @@ void IntraSearch::destroy()
 #if JVET_AB0143_CCCM_TS
   for (uint32_t cccmIdx = 0; cccmIdx < CCCM_NUM_MODES; cccmIdx++)
   {
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+    m_cccmStorage[0][cccmIdx].destroy();
+    m_cccmStorage[1][cccmIdx].destroy();
+#else
     m_cccmStorage[cccmIdx].destroy();
+#endif
   }
 #endif
 
@@ -283,7 +288,12 @@ void IntraSearch::init( EncCfg*        pcEncCfg,
 #if JVET_AB0143_CCCM_TS
   for (uint32_t cccmIdx = 0; cccmIdx < CCCM_NUM_MODES; cccmIdx++)
   {
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+    m_cccmStorage[0][cccmIdx].create(UnitArea(cform, Area(0, 0, MAX_CU_SIZE, MAX_CU_SIZE)));
+    m_cccmStorage[1][cccmIdx].create(UnitArea(cform, Area(0, 0, MAX_CU_SIZE, MAX_CU_SIZE)));
+#else
     m_cccmStorage[cccmIdx].create(UnitArea(cform, Area(0, 0, MAX_CU_SIZE, MAX_CU_SIZE)));
+#endif
   }
 #endif
 #if JVET_AB0155_SGPM
@@ -2540,6 +2550,9 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
     int32_t bestBDPCMMode = 0;
 #if JVET_AA0057_CCCM
     int      cccmModeBest = 0;
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+    int      cccmNoSubBest = 0;
+#endif
 #endif
 #if JVET_Z0050_CCLM_SLOPE
     CclmOffsets bestCclmOffsets = {};
@@ -2848,6 +2861,247 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
 #else
       uint32_t chromaCandCccmModes[CCCM_NUM_MODES] = { LM_CHROMA_IDX, MDLM_L_IDX, MDLM_T_IDX };
 #endif
+
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+      int64_t satdCccmSortedCost[2][CCCM_NUM_MODES];
+      int satdCccmModeList[2][CCCM_NUM_MODES];
+
+      for (int i = 0; i < CCCM_NUM_MODES; i++)
+      {
+        satdCccmSortedCost[0][i] = LLONG_MAX; // for the mode not pre-select by SATD, do RDO by default, so set the initial value 0.
+        satdCccmSortedCost[1][i] = LLONG_MAX; // for the mode not pre-select by SATD, do RDO by default, so set the initial value 0.
+        satdCccmModeList[0][i] = chromaCandCccmModes[i];
+        satdCccmModeList[1][i] = chromaCandCccmModes[i];
+      }
+      int64_t bestCccmCost[2] = { LLONG_MAX, LLONG_MAX};
+
+      bool isCccmFullEnabled = PU::cccmSingleModeAvail(pu, LM_CHROMA_IDX);
+      bool isCccmLeftEnabled = PU::isLeftCccmMode(pu, MDLM_L_IDX);
+      bool isCccmTopEnabled = PU::isTopCccmMode(pu, MDLM_T_IDX);
+#if MMLM
+      bool isMultiCccmFullEnabled = PU::cccmMultiModeAvail(pu, MMLM_CHROMA_IDX);
+      bool isMultiCccmLeftEnabled = PU::cccmMultiModeAvail(pu, MMLM_L_IDX);
+      bool isMultiCccmTopEnabled = PU::cccmMultiModeAvail(pu, MMLM_T_IDX);
+#endif
+
+      const UnitArea localUnitArea(cs.area.chromaFormat, Area(0, 0, (pu.Cb().width) << 1, (pu.Cb().height) << 1));
+      PelUnitBuf cccmStorage[2][CCCM_NUM_MODES];
+
+      pu.cccmFlag = 1;
+
+      for (int sub = 0; sub < pu.cu->slice->getSPS()->getUseCccm(); sub++)
+      {
+        pu.cccmNoSubFlag = sub;
+
+        xGetLumaRecPixels(pu, pu.Cb());
+
+        bool isCCCMEnabled = false;
+
+        for (int idx = 0; idx < CCCM_NUM_MODES; idx++)
+        {
+          int mode = chromaCandCccmModes[idx];
+          if (idx == 0)
+          {
+            isCCCMEnabled = isCccmFullEnabled;
+            pu.cccmFlag = 1;
+          }
+          else if (idx == 1)
+          {
+            isCCCMEnabled = isCccmLeftEnabled;
+            pu.cccmFlag = 2;
+          }
+          else if (idx == 2)
+          {
+            isCCCMEnabled = isCccmTopEnabled;
+            pu.cccmFlag = 3;
+          }
+#if MMLM
+          else if (idx == 3)
+          {
+            isCCCMEnabled = isMultiCccmFullEnabled;
+            pu.cccmFlag = 1;
+          }
+          else if (idx == 4)
+          {
+            isCCCMEnabled = isMultiCccmLeftEnabled;
+            pu.cccmFlag = 2;
+          }
+          else if (idx == 5)
+          {
+            isCCCMEnabled = isMultiCccmTopEnabled;
+            pu.cccmFlag = 3;
+          }
+#endif
+
+          if (isCCCMEnabled)
+          {
+            pu.intraDir[1] = mode; // temporary assigned, for SATD checking.
+
+            if ( ( sub == 1 ) && m_skipCCCMSATD )
+            {
+              if (m_isCccmNoSubModeEnabledInRdo[mode] == 0)
+              {
+                continue;
+              }
+            }
+
+            int64_t sad = 0;
+            int64_t sadCb = 0;
+            int64_t satdCb = 0;
+            int64_t sadCr = 0;
+            int64_t satdCr = 0;
+            CodingStructure& cs = *(pu.cs);
+
+            DistParam distParamSadCb;
+            DistParam distParamSatdCb;
+            DistParam distParamSadCr;
+            DistParam distParamSatdCr;
+
+            cccmStorage[sub][idx] = m_cccmStorage[sub][idx].getBuf(localUnitArea);
+
+            CompArea areaCb = pu.Cb();
+            PelBuf orgCb = cs.getOrgBuf(areaCb);
+            CompArea areaCr = pu.Cr();
+            PelBuf orgCr = cs.getOrgBuf(areaCr);
+
+            m_pcRdCost->setDistParam(distParamSadCb, orgCb, cccmStorage[sub][idx].Cb(), pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA), COMPONENT_Cb, false);
+            m_pcRdCost->setDistParam(distParamSatdCb, orgCb, cccmStorage[sub][idx].Cb(), pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA), COMPONENT_Cb, true);
+            distParamSadCb.applyWeight = false;
+            distParamSatdCb.applyWeight = false;
+            m_pcRdCost->setDistParam(distParamSadCr, orgCr, cccmStorage[sub][idx].Cr(), pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA), COMPONENT_Cr, false);
+            m_pcRdCost->setDistParam(distParamSatdCr, orgCr, cccmStorage[sub][idx].Cr(), pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA), COMPONENT_Cr, true);
+
+            distParamSadCr.applyWeight = false;
+            distParamSatdCr.applyWeight = false;
+
+            predIntraCCCM(pu, cccmStorage[sub][idx].Cb(), cccmStorage[sub][idx].Cr(), mode);
+
+            sadCb = distParamSadCb.distFunc(distParamSadCb) * 2;
+            satdCb = distParamSatdCb.distFunc(distParamSatdCb);
+            sad += std::min(sadCb, satdCb);
+            sadCr = distParamSadCr.distFunc(distParamSadCr) * 2;
+            satdCr = distParamSatdCr.distFunc(distParamSatdCr);
+            sad += std::min(sadCr, satdCr);
+
+            satdCccmSortedCost[sub][idx] = sad;
+
+            if (sad < bestCccmCost[sub])
+            {
+              bestCccmCost[sub] = sad;
+            }
+          }
+        }
+      }
+      int tempCccmIdx = 0;
+      int64_t tempCccmCost = 0;
+#if MMLM
+      for (int i = 1; i < 4; i++)
+#else
+      for (int i = 1; i < 3; i++)
+#endif
+      {
+        for (int j = i + 1; j < CCCM_NUM_MODES; j++)
+        {
+          if (satdCccmSortedCost[0][j] < satdCccmSortedCost[0][i])
+          {
+            tempCccmIdx = satdCccmModeList[0][i];
+            satdCccmModeList[0][i] = satdCccmModeList[0][j];
+            satdCccmModeList[0][j] = tempCccmIdx;
+
+            tempCccmCost = satdCccmSortedCost[0][i];
+            satdCccmSortedCost[0][i] = satdCccmSortedCost[0][j];
+            satdCccmSortedCost[0][j] = tempCccmCost;
+          }
+        }
+      }
+
+#if MMLM
+      bool isCccmModeEnabledInRdo[2][MMLM_T_IDX + 1] = { false };
+      isCccmModeEnabledInRdo[0][satdCccmModeList[0][0]] = true;
+      for (int i = 1; i < 4; i++)
+#else
+      bool isCccmModeEnabledInRdo[MDLM_T_IDX + 1] = { false };
+      isCccmModeEnabledInRdo[satdCccmModeList[0]] = true;
+      for (int i = 1; i < 3; i++)
+#endif
+      {
+        if (satdCccmSortedCost[0][i] >= 1.15 * bestCccmCost[0])
+        {
+          break;
+        }
+        isCccmModeEnabledInRdo[0][satdCccmModeList[0][i]] = true;
+      }
+
+      if (pu.cu->slice->getSPS()->getUseCccm() == 2)
+      {
+        if (bestCccmCost[1] < bestCccmCost[0])
+        {
+#if MMLM
+          for (int i = 0; i < 4; i++)
+#else
+          for (int i = 0; i < 3; i++)
+#endif
+          {
+            if (isCccmModeEnabledInRdo[0][satdCccmModeList[0][i]] && (satdCccmSortedCost[0][i] >= 1.2 * bestCccmCost[1]))
+            {
+              isCccmModeEnabledInRdo[0][satdCccmModeList[0][i]] = false;
+            }
+            else
+            {
+              bestCccmCost[0] = satdCccmSortedCost[0][i];
+            }
+          }
+        }
+        else
+        {
+          bestCccmCost[1] = bestCccmCost[0];
+        }
+
+        tempCccmIdx = 0;
+        tempCccmCost = 0;
+        for (int i = 1; i < 2; i++)
+        {
+          for (int j = i + 1; j < 6; j++)
+          {
+            if (satdCccmSortedCost[1][j] < satdCccmSortedCost[1][i])
+            {
+              tempCccmIdx = satdCccmModeList[1][i];
+              satdCccmModeList[1][i] = satdCccmModeList[1][j];
+              satdCccmModeList[1][j] = tempCccmIdx;
+
+              tempCccmCost = satdCccmSortedCost[1][i];
+              satdCccmSortedCost[1][i] = satdCccmSortedCost[1][j];
+              satdCccmSortedCost[1][j] = tempCccmCost;
+            }
+          }
+        }
+
+        isCccmModeEnabledInRdo[1][satdCccmModeList[1][0]] = true;
+        for (int i = 1; i < 2; i++)
+        {
+          if (satdCccmSortedCost[1][i] >= CCCM_NO_SUB_WEIGHT * bestCccmCost[1])
+          {
+            if (satdCccmSortedCost[1][i - 1] > bestCccmCost[0])
+            {
+              bestCccmCost[0] = satdCccmSortedCost[1][i - 1];
+            }
+            bestCccmCost[1] = satdCccmSortedCost[1][i - 1];
+            break;
+          }
+          isCccmModeEnabledInRdo[1][satdCccmModeList[1][i]] = true;
+        }
+        if (m_skipCCCMSATD == false)
+        {
+          m_skipCCCMSATD = true;
+          for (int i = 0; i < (MMLM_T_IDX + 1); i++)
+          {
+            m_isCccmNoSubModeEnabledInRdo[i] = isCccmModeEnabledInRdo[1][i];
+          }
+        }
+      }
+      pu.cccmFlag = 0;
+      pu.cccmNoSubFlag = 0;
+#else
       int64_t satdCccmSortedCost[CCCM_NUM_MODES];
       int satdCccmModeList[CCCM_NUM_MODES];
 
@@ -3004,6 +3258,7 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
 
       pu.cccmFlag = 0;
 #endif
+#endif
 
 #if MMLM
       m_encPreRDRun = false;
@@ -3036,6 +3291,33 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
       {
         modeIsEnable[satdModeList[uiMaxMode - 1 - i]] = 0; // disable the last reducedModeNumber modes
       }
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+      if (pu.cu->slice->getSPS()->getUseCccm() == 2)
+      {
+        if (satdSortedCost[uiMaxMode - 1 - reducedModeNumber] > bestCccmCost[0])
+        {
+          modeIsEnable[satdModeList[uiMaxMode - 1 - reducedModeNumber]] = 0; // disable the last reducedModeNumber modes
+        }
+        else if (satdSortedCost[uiMaxMode - 1 - reducedModeNumber] < bestCccmCost[0])
+        {
+          for (int i = 3; i > 0; i--)
+          {
+            if ((satdCccmSortedCost[0][i] > satdSortedCost[uiMaxMode - 1 - reducedModeNumber]) && (isCccmModeEnabledInRdo[0][satdCccmModeList[0][i]]))
+            {
+              isCccmModeEnabledInRdo[0][satdCccmModeList[0][i]] = false;
+              break;
+            }
+          }
+        }
+        if (satdSortedCost[uiMaxMode - 1 - reducedModeNumber] < bestCccmCost[1])
+        {
+          if ((satdCccmSortedCost[1][1] > satdSortedCost[uiMaxMode - 1 - reducedModeNumber]) && (isCccmModeEnabledInRdo[1][satdCccmModeList[1][1]]))
+          {
+            isCccmModeEnabledInRdo[1][satdCccmModeList[1][1]] = false;
+          }
+        }
+      }
+#endif
 
       // save the dist
       Distortion baseDist = cs.dist;
@@ -3380,8 +3662,11 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
 #if JVET_AA0057_CCCM
 #if JVET_AB0143_CCCM_TS
       int chromaIntraModeInCCCM = LM_CHROMA_IDX;
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+      bool isCCCMEnabled = isCccmFullEnabled;
+#else
       isCCCMEnabled = isCccmFullEnabled;
-
+#endif
       pu.cccmFlag = 1;
 
       for (int32_t uiMode = 0; uiMode < CCCM_NUM_MODES; uiMode++)
@@ -3418,8 +3703,11 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
           pu.cccmFlag = 3;
         }
 #endif
-
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+        if (!isCccmModeEnabledInRdo[0][chromaIntraModeInCCCM] && !isCccmModeEnabledInRdo[1][chromaIntraModeInCCCM])
+#else
         if (!isCccmModeEnabledInRdo[chromaIntraModeInCCCM])
+#endif
         {
           continue;
         }
@@ -3442,6 +3730,16 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
           pu.cccmFlag = 1;
 #endif
 
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+          for (int sub = 0; sub < pu.cu->slice->getSPS()->getUseCccm(); sub++)
+          {
+            pu.cccmNoSubFlag = sub;
+            if (!isCccmModeEnabledInRdo[sub][chromaIntraModeInCCCM])
+            {
+              continue;
+            }
+#endif
+
           // Original RD check code replicated from above
           cs.setDecomp( pu.Cb(), false );
           cs.dist = baseDist;
@@ -3452,7 +3750,11 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
 #if JVET_AB0143_CCCM_TS
           pu.intraDir[1] = chromaIntraModeInCCCM;
 
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+          xRecurIntraChromaCodingQT(cs, partitioner, bestCostSoFar, ispType, cccmStorage[sub][uiMode]);
+#else
           xRecurIntraChromaCodingQT(cs, partitioner, bestCostSoFar, ispType, cccmStorage[uiMode]);
+#endif
 #else
           pu.intraDir[1] = chromaIntraMode;
 
@@ -3518,6 +3820,10 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
 #if JVET_AA0126_GLM
             bestGlmIdc      = pu.glmIdc;
 #endif 
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING 
+            cccmNoSubBest  = pu.cccmNoSubFlag;
+            }
+#endif
           }
         }
       }
@@ -3557,6 +3863,9 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
 #endif
 #if JVET_AA0057_CCCM
     pu.cccmFlag        = cccmModeBest;
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING 
+    pu.cccmNoSubFlag   = cccmNoSubBest;
+#endif
 #endif
 #if JVET_Z0050_DIMD_CHROMA_FUSION
     pu.isChromaFusion = isChromaFusion;
diff --git a/source/Lib/EncoderLib/IntraSearch.h b/source/Lib/EncoderLib/IntraSearch.h
index eb6607246..ec04a6de3 100644
--- a/source/Lib/EncoderLib/IntraSearch.h
+++ b/source/Lib/EncoderLib/IntraSearch.h
@@ -489,8 +489,12 @@ private:
   PelStorage      m_tmpStorageLCU;
   PelStorage      m_colorTransResiBuf;
 #if JVET_AB0143_CCCM_TS
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  PelStorage      m_cccmStorage[2][CCCM_NUM_MODES];
+#else
   PelStorage      m_cccmStorage[CCCM_NUM_MODES];
 #endif
+#endif
 protected:
   // interface to option
   EncCfg*         m_pcEncCfg;
@@ -524,6 +528,10 @@ public:
 #if INTRA_TRANS_ENC_OPT
   bool            m_skipTimdLfnstMtsPass;
 #endif
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  bool            m_skipCCCMSATD;
+  int             m_isCccmNoSubModeEnabledInRdo[MMLM_T_IDX + 1];
+#endif 
   IntraSearch();
   ~IntraSearch();
 
diff --git a/source/Lib/EncoderLib/VLCWriter.cpp b/source/Lib/EncoderLib/VLCWriter.cpp
index f33f57216..336324a9d 100644
--- a/source/Lib/EncoderLib/VLCWriter.cpp
+++ b/source/Lib/EncoderLib/VLCWriter.cpp
@@ -1573,6 +1573,9 @@ void HLSWriter::codeSPS( const SPS* pcSPS )
 #endif
 #if JVET_AB0155_SGPM
   WRITE_FLAG(pcSPS->getUseSgpm() ? 1 : 0, "sps_sgpm_enabled_flag");
+#endif
+#if JVET_AC0147_CCCM_NO_SUBSAMPLING
+  WRITE_UVLC(pcSPS->getUseCccm() , "sps_cccm_cand");
 #endif
   if( pcSPS->getChromaFormatIdc() != CHROMA_400)
   {
-- 
GitLab