diff --git a/source/App/EncoderApp/EncApp.cpp b/source/App/EncoderApp/EncApp.cpp
index 2a7209a9fc1f45f96a6708024d81bcb7bee4c55c..9e6bfb2080662d270dc587e2c5dbc4a870578761 100644
--- a/source/App/EncoderApp/EncApp.cpp
+++ b/source/App/EncoderApp/EncApp.cpp
@@ -299,7 +299,10 @@ void EncApp::xInitLibCfg()
   m_cEncLib.setDMVR                                              ( m_DMVR );
   m_cEncLib.setMMVD                                              ( m_MMVD );
   m_cEncLib.setMmvdDisNum                                        (m_MmvdDisNum);
-  m_cEncLib.setRDPCM                                              ( m_RdpcmMode );
+  m_cEncLib.setRDPCM                                             ( m_RdpcmMode );
+#if JVET_O0119_BASE_PALETTE_444
+  m_cEncLib.setPLTMode                                           ( m_PLTMode);
+#endif
 #if JVET_O0376_SPS_JOINTCBCR_FLAG
   m_cEncLib.setJointCbCr                                         ( m_JointCbCrMode );
 #endif
diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp
index 18029650b91a96a3eb6b6476480c840e708bd1d8..3294ed5253356d84a9d0718de870fb3ab3cfeabc 100644
--- a/source/App/EncoderApp/EncAppCfg.cpp
+++ b/source/App/EncoderApp/EncAppCfg.cpp
@@ -916,7 +916,10 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
   ("AffineAmvrEncOpt",                                m_AffineAmvrEncOpt,                               false, "Enable encoder optimization of affine AMVR")
   ("DMVR",                                            m_DMVR,                                           false, "Decoder-side Motion Vector Refinement")
   ("MmvdDisNum",                                      m_MmvdDisNum,                                     8,     "Number of MMVD Distance Entries")
-  ( "RDPCM",                                         m_RdpcmMode,                                       false, "RDPCM")
+  ( "RDPCM",                                          m_RdpcmMode,                                       false, "RDPCM")
+#if JVET_O0119_BASE_PALETTE_444
+  ("PLT",                                             m_PLTMode,                                           0u, "PLTMode (0x1:enabled, 0x0:disabled)  [default: disabled]")
+#endif
 #if JVET_O0376_SPS_JOINTCBCR_FLAG
   ("JointCbCr",                                       m_JointCbCrMode,                                  false, "Enable joint coding of chroma residuals (JointCbCr, 0:off, 1:on)")
 #endif
@@ -2273,6 +2276,9 @@ bool EncAppCfg::xCheckParameter()
 #endif
     xConfirmPara( m_LMChroma, "LMChroma only allowed with NEXT profile" );
     xConfirmPara( m_ImvMode, "IMV is only allowed with NEXT profile" );
+#if JVET_O0119_BASE_PALETTE_444
+    xConfirmPara( m_PLTMode, "PLT Mode only allowed with NEXT profile");
+#endif
     xConfirmPara(m_IBCMode, "IBC Mode only allowed with NEXT profile");
     xConfirmPara( m_HashME, "Hash motion estimation only allowed with NEXT profile" );
     xConfirmPara( m_useFastLCTU, "Fast large CTU can only be applied when encoding with NEXT profile" );
@@ -3503,6 +3509,10 @@ void EncAppCfg::xPrintParameter()
     msg(VERBOSE, "JointCbCr:%d ", m_JointCbCrMode);
 #endif
   }
+#if JVET_O0119_BASE_PALETTE_444
+    m_PLTMode = ( m_chromaFormatIDC == CHROMA_444) ? m_PLTMode : 0u;
+    msg(VERBOSE, "PLT:%d ", m_PLTMode);
+#endif
     msg(VERBOSE, "IBC:%d ", m_IBCMode);
   msg( VERBOSE, "HashME:%d ", m_HashME );
   msg( VERBOSE, "WrapAround:%d ", m_wrapAround);
diff --git a/source/App/EncoderApp/EncAppCfg.h b/source/App/EncoderApp/EncAppCfg.h
index 3607774397de63d901792cd21af28c803c20fb39..ebfc9e723a501e0d4ab3d4a79f75449e885a2f7e 100644
--- a/source/App/EncoderApp/EncAppCfg.h
+++ b/source/App/EncoderApp/EncAppCfg.h
@@ -293,6 +293,9 @@ protected:
   bool      m_MMVD;
   int       m_MmvdDisNum;
   bool      m_RdpcmMode;
+#if JVET_O0119_BASE_PALETTE_444
+  unsigned  m_PLTMode;
+#endif
 #if JVET_O0376_SPS_JOINTCBCR_FLAG
   bool      m_JointCbCrMode;
 #endif
diff --git a/source/Lib/CommonLib/Buffer.h b/source/Lib/CommonLib/Buffer.h
index ba0486194797f3106fdee651ff9bd6bc28b7892e..d04861d65f7c10eb9dab65bdbec515c30f745963 100644
--- a/source/Lib/CommonLib/Buffer.h
+++ b/source/Lib/CommonLib/Buffer.h
@@ -173,6 +173,13 @@ typedef AreaBuf<const TCoeff> CCoeffBuf;
 typedef AreaBuf<      MotionInfo>  MotionBuf;
 typedef AreaBuf<const MotionInfo> CMotionBuf;
 
+#if JVET_O0119_BASE_PALETTE_444
+typedef AreaBuf<      TCoeff> PLTescapeBuf;
+typedef AreaBuf<const TCoeff> CPLTescapeBuf;
+
+typedef AreaBuf<      bool> PLTtypeBuf;
+typedef AreaBuf<const bool> CPLTtypeBuf;
+#endif
 
 #define SIZE_AWARE_PER_EL_OP( OP, INC )                     \
 if( ( width & 7 ) == 0 )                                    \
diff --git a/source/Lib/CommonLib/CodingStructure.cpp b/source/Lib/CommonLib/CodingStructure.cpp
index 71b0c4ce17c710cd2907479ca59f0995b40815ae..c7f0b3458ab97d674a17ce33ed9eab6e47f0be1f 100644
--- a/source/Lib/CommonLib/CodingStructure.cpp
+++ b/source/Lib/CommonLib/CodingStructure.cpp
@@ -78,6 +78,10 @@ CodingStructure::CodingStructure(CUCache& cuCache, PUCache& puCache, TUCache& tu
   {
     m_coeffs[ i ] = nullptr;
     m_pcmbuf[ i ] = nullptr;
+#if JVET_O0119_BASE_PALETTE_444
+    m_runType[i] = nullptr;
+    m_runLength[i] = nullptr;
+#endif
 
     m_offsets[ i ] = 0;
   }
@@ -507,6 +511,10 @@ TransformUnit& CodingStructure::addTU( const UnitArea &unit, const ChannelType c
 
   TCoeff *coeffs[5] = { nullptr, nullptr, nullptr, nullptr, nullptr };
   Pel    *pcmbuf[5] = { nullptr, nullptr, nullptr, nullptr, nullptr };
+#if JVET_O0119_BASE_PALETTE_444 
+  bool   *runType[5]   = { nullptr, nullptr, nullptr, nullptr, nullptr };
+  Pel    *runLength[5] = { nullptr, nullptr, nullptr, nullptr, nullptr };
+#endif
 
   uint32_t numCh = ::getNumberValidComponents( area.chromaFormat );
 
@@ -543,12 +551,20 @@ TransformUnit& CodingStructure::addTU( const UnitArea &unit, const ChannelType c
 
     coeffs[i] = m_coeffs[i] + m_offsets[i];
     pcmbuf[i] = m_pcmbuf[i] + m_offsets[i];
+#if JVET_O0119_BASE_PALETTE_444
+    runType[i]   = m_runType[i]   + m_offsets[i];
+    runLength[i] = m_runLength[i] + m_offsets[i];
+#endif
 
     unsigned areaSize = tu->blocks[i].area();
     m_offsets[i] += areaSize;
   }
 
+#if JVET_O0119_BASE_PALETTE_444
+  tu->init( coeffs, pcmbuf, runLength, runType);
+#else
   tu->init( coeffs, pcmbuf );
+#endif
 
   return *tu;
 }
@@ -711,6 +727,64 @@ void CodingStructure::addMiToLut(static_vector<MotionInfo, MAX_NUM_HMVP_CANDS> &
   lut.push_back(mi);
 }
 
+#if JVET_O0119_BASE_PALETTE_444
+void CodingStructure::resetPrevPLT(PLTBuf& prevPLT)
+{
+  for (int comp = 0; comp < MAX_NUM_COMPONENT; comp++)
+  {
+    prevPLT.curPLTSize[comp] = 0;
+    memset(prevPLT.curPLT[comp], 0, MAXPLTPREDSIZE * sizeof(Pel));
+  }
+}
+
+void CodingStructure::reorderPrevPLT(PLTBuf& prevPLT, uint32_t curPLTSize[MAX_NUM_COMPONENT], Pel curPLT[MAX_NUM_COMPONENT][MAXPLTSIZE], bool reuseflag[MAX_NUM_COMPONENT][MAXPLTPREDSIZE], uint32_t compBegin, uint32_t numComp, bool jointPLT)
+{
+  Pel stuffedPLT[MAX_NUM_COMPONENT][MAXPLTPREDSIZE];
+  uint32_t tempCurPLTsize[MAX_NUM_COMPONENT];
+  uint32_t stuffPLTsize[MAX_NUM_COMPONENT];
+
+  for (int i = compBegin; i < (compBegin + numComp); i++)
+  {
+    ComponentID comID = jointPLT ? (ComponentID)compBegin : ((i > 0) ? COMPONENT_Cb : COMPONENT_Y);
+    tempCurPLTsize[comID] = curPLTSize[comID];
+    stuffPLTsize[i] = 0;
+    memcpy(stuffedPLT[i], curPLT[i], curPLTSize[comID] * sizeof(Pel));
+  }
+
+  for (int ch = compBegin; ch < (compBegin + numComp); ch++)
+  {
+    ComponentID comID = jointPLT ? (ComponentID)compBegin : ((ch > 0) ? COMPONENT_Cb : COMPONENT_Y);
+    if (ch > 1) break;
+    for (int i = 0; i < prevPLT.curPLTSize[comID]; i++)
+    {
+      if (tempCurPLTsize[comID] + stuffPLTsize[ch] >= MAXPLTPREDSIZE)
+        break;
+
+      if (!reuseflag[comID][i])
+      {
+        if (ch == COMPONENT_Y)
+        {
+          stuffedPLT[0][tempCurPLTsize[comID] + stuffPLTsize[ch]] = prevPLT.curPLT[0][i];
+        }
+        else
+        {
+          stuffedPLT[1][tempCurPLTsize[comID] + stuffPLTsize[ch]] = prevPLT.curPLT[1][i];
+          stuffedPLT[2][tempCurPLTsize[comID] + stuffPLTsize[ch]] = prevPLT.curPLT[2][i];
+        }
+        stuffPLTsize[ch]++;
+      }
+    }
+  }
+
+  for (int i = compBegin; i < (compBegin + numComp); i++)
+  {
+    ComponentID comID = jointPLT ? (ComponentID)compBegin : ((i > 0) ? COMPONENT_Cb : COMPONENT_Y);
+    prevPLT.curPLTSize[comID] = curPLTSize[comID] + stuffPLTsize[comID];
+    memcpy(prevPLT.curPLT[i], stuffedPLT[i], prevPLT.curPLTSize[comID] * sizeof(Pel));
+  }
+}
+#endif
+
 void CodingStructure::rebindPicBufs()
 {
   CHECK( parent, "rebindPicBufs can only be used for the top level CodingStructure" );
@@ -738,6 +812,10 @@ void CodingStructure::createCoeffs()
 
     m_coeffs[i] = _area > 0 ? ( TCoeff* ) xMalloc( TCoeff, _area ) : nullptr;
     m_pcmbuf[i] = _area > 0 ? ( Pel*    ) xMalloc( Pel,    _area ) : nullptr;
+#if JVET_O0119_BASE_PALETTE_444
+    m_runType[i]   = _area > 0 ? (bool*)xMalloc( bool, _area) : nullptr;
+    m_runLength[i] = _area > 0 ? (Pel*) xMalloc( Pel,  _area) : nullptr;
+#endif
   }
 }
 
@@ -747,6 +825,10 @@ void CodingStructure::destroyCoeffs()
   {
     if( m_coeffs[i] ) { xFree( m_coeffs[i] ); m_coeffs[i] = nullptr; }
     if( m_pcmbuf[i] ) { xFree( m_pcmbuf[i] ); m_pcmbuf[i] = nullptr; }
+#if JVET_O0119_BASE_PALETTE_444
+    if (m_runType[i])   { xFree(m_runType[i]);   m_runType[i]   = nullptr; }
+    if (m_runLength[i]) { xFree(m_runLength[i]); m_runLength[i] = nullptr; }
+#endif
   }
 }
 
@@ -790,6 +872,10 @@ void CodingStructure::initSubStructure( CodingStructure& subStruct, const Channe
 
   subStruct.motionLut = motionLut;
 
+#if JVET_O0119_BASE_PALETTE_444
+  subStruct.prevPLT = prevPLT;
+#endif
+
   subStruct.initStructData( currQP[_chType], isLossless );
 
   if( isTuEnc )
@@ -851,6 +937,10 @@ void CodingStructure::useSubStructure( const CodingStructure& subStruct, const C
 
     motionLut = subStruct.motionLut;
   }
+#if JVET_O0119_BASE_PALETTE_444
+  prevPLT = subStruct.prevPLT;
+#endif
+
 #if ENABLE_WPP_PARALLELISM
 
   if( nullptr == parent )
@@ -1033,6 +1123,9 @@ void CodingStructure::copyStructure( const CodingStructure& other, const Channel
 
     motionLut = other.motionLut;
   }
+#if JVET_O0119_BASE_PALETTE_444
+  prevPLT = other.prevPLT;
+#endif
 
   if( copyTUs )
   {
diff --git a/source/Lib/CommonLib/CodingStructure.h b/source/Lib/CommonLib/CodingStructure.h
index 8589a75a78e47024d8e7fdadc2982458059a6700..f641a2ba230b4de66e34caceb67199e6c895f3a6 100644
--- a/source/Lib/CommonLib/CodingStructure.h
+++ b/source/Lib/CommonLib/CodingStructure.h
@@ -201,6 +201,11 @@ public:
 
   void addMiToLut(static_vector<MotionInfo, MAX_NUM_HMVP_CANDS>& lut, const MotionInfo &mi);
 
+#if JVET_O0119_BASE_PALETTE_444
+  PLTBuf prevPLT;
+  void resetPrevPLT(PLTBuf& prevPLT);
+  void reorderPrevPLT(PLTBuf& prevPLT, uint32_t curPLTSize[MAX_NUM_COMPONENT], Pel curPLT[MAX_NUM_COMPONENT][MAXPLTSIZE], bool reuseflag[MAX_NUM_COMPONENT][MAXPLTPREDSIZE], uint32_t compBegin, uint32_t numComp, bool jointPLT);
+#endif
 private:
 
   // needed for TU encoding
@@ -228,7 +233,10 @@ private:
 
   TCoeff *m_coeffs [ MAX_NUM_COMPONENT ];
   Pel    *m_pcmbuf [ MAX_NUM_COMPONENT ];
-
+#if JVET_O0119_BASE_PALETTE_444
+  bool   *m_runType  [MAX_NUM_COMPONENT];
+  Pel    *m_runLength[MAX_NUM_COMPONENT];
+#endif
   int     m_offsets[ MAX_NUM_COMPONENT ];
 
   MotionInfo *m_motionBuf;
diff --git a/source/Lib/CommonLib/CommonDef.h b/source/Lib/CommonLib/CommonDef.h
index bf963649cbaa536890f3cf8e7d88d98a1d40f6fd..daf78d6ff822b7d7dd250fba507518018256bba9 100644
--- a/source/Lib/CommonLib/CommonDef.h
+++ b/source/Lib/CommonLib/CommonDef.h
@@ -507,6 +507,15 @@ static const int CSCALE_FP_PREC =                                11;
 static const int  NEIG_NUM_LOG  =                                 6;
 static const int  NEIG_NUM =                      1 << NEIG_NUM_LOG;
 #endif
+#if JVET_O0119_BASE_PALETTE_444
+static const int MAXPLTPREDSIZE = 63;
+static const int MAXPLTSIZE = 31;
+static const int PLT_ENCBITDEPTH = 8;
+static const int PLT_RUN_MSB_IDX_CABAC_BYPASS_THRE = 4;
+static const int PLT_RUN_MSB_IDX_CTX_T1 = 1;
+static const int PLT_RUN_MSB_IDX_CTX_T2 = 3;
+static const int PLT_FAST_RATIO = 100;
+#endif
 #if RExt__DECODER_DEBUG_TOOL_MAX_FRAME_STATS
 static const int  EPBIN_WEIGHT_FACTOR =                           4;
 #endif
diff --git a/source/Lib/CommonLib/Contexts.cpp b/source/Lib/CommonLib/Contexts.cpp
index 8903da8fae4c4653fdab9fbff1ff9bb953a7b9ad..3b4a41b797ffb60b605e4956638a855b614729ce 100755
--- a/source/Lib/CommonLib/Contexts.cpp
+++ b/source/Lib/CommonLib/Contexts.cpp
@@ -761,6 +761,48 @@ const CtxSet ContextSetCfg::LFNSTIdx = ContextSetCfg::addCtxSet
   {   8,   8, },
 } );
 
+#if JVET_O0119_BASE_PALETTE_444
+const CtxSet ContextSetCfg::PLTFlag = ContextSetCfg::addCtxSet
+({
+  { 146 },
+  { 146 },
+  { 147 },
+  { 1 }
+});
+
+const CtxSet ContextSetCfg::RotationFlag = ContextSetCfg::addCtxSet
+({
+  { 153 },
+  { 138 },
+  { 168 },
+  { 5 }
+});
+
+const CtxSet ContextSetCfg::RunTypeFlag = ContextSetCfg::addCtxSet
+({
+  { 167 },
+  { 167 },
+  { 167 },
+  { 8 }
+});
+
+const CtxSet ContextSetCfg::IdxRunModel = ContextSetCfg::addCtxSet
+({
+  { 157, 169, 138, 170, 155 },
+  { 186, 169, 166, 186, 185 },
+  { 155, 168, 167, 155, 154 },
+  { 5,   6,   5,   9,  10 }
+});
+
+const CtxSet ContextSetCfg::CopyRunModel = ContextSetCfg::addCtxSet
+({
+  { 187, 172, 156 },
+  { 187, 187, 185 },
+  { 201, 171, 155 },
+  { 0,   5,   5 }
+});
+#endif
+
 const CtxSet ContextSetCfg::RdpcmFlag = ContextSetCfg::addCtxSet
 ({
   { CNU, CNU, },
@@ -1014,6 +1056,9 @@ const unsigned ContextSetCfg::NumberOfContexts = (unsigned)ContextSetCfg::sm_Ini
 
 
 // combined sets
+#if JVET_O0119_BASE_PALETTE_444 
+const CtxSet ContextSetCfg::Palette = { ContextSetCfg::RotationFlag, ContextSetCfg::RunTypeFlag, ContextSetCfg::IdxRunModel, ContextSetCfg::CopyRunModel };
+#endif
 const CtxSet ContextSetCfg::Sao = { ContextSetCfg::SaoMergeFlag, ContextSetCfg::SaoTypeIdx };
 
 #if JVET_O0090_ALF_CHROMA_FILTER_ALTERNATIVES_CTB
diff --git a/source/Lib/CommonLib/Contexts.h b/source/Lib/CommonLib/Contexts.h
old mode 100755
new mode 100644
index 7ed83c7b74622ea55cb004876696035b10a7bd18..529d4a7a08644d6e2d29eece642f5d38ca0f705a
--- a/source/Lib/CommonLib/Contexts.h
+++ b/source/Lib/CommonLib/Contexts.h
@@ -251,6 +251,13 @@ public:
   static const CtxSet   MTSIndex;
   static const CtxSet   TransquantBypassFlag;
   static const CtxSet   LFNSTIdx;
+#if JVET_O0119_BASE_PALETTE_444
+  static const CtxSet   PLTFlag;
+  static const CtxSet   RotationFlag;
+  static const CtxSet   RunTypeFlag;
+  static const CtxSet   IdxRunModel;    
+  static const CtxSet   CopyRunModel;    
+#endif
   static const CtxSet   RdpcmFlag;
   static const CtxSet   RdpcmDir;
   static const CtxSet   SbtFlag;
@@ -285,6 +292,9 @@ public:
 #if JVET_O0090_ALF_CHROMA_FILTER_ALTERNATIVES_CTB
   static const CtxSet   Alf;
 #endif
+#if JVET_O0119_BASE_PALETTE_444
+  static const CtxSet   Palette;
+#endif
 
 public:
   static const std::vector<uint8_t>&  getInitTable( unsigned initId );
diff --git a/source/Lib/CommonLib/IntraPrediction.cpp b/source/Lib/CommonLib/IntraPrediction.cpp
index 337dcac80c107ac2eb928b4057c7a1e378e63f34..3ae05f29d10c46ac7552891769e16920b175b3ce 100644
--- a/source/Lib/CommonLib/IntraPrediction.cpp
+++ b/source/Lib/CommonLib/IntraPrediction.cpp
@@ -175,6 +175,10 @@ void IntraPrediction::destroy()
   m_piTemp = nullptr;
   delete[] m_pMdlmTemp;
   m_pMdlmTemp = nullptr;
+#if JVET_O0119_BASE_PALETTE_444
+  if (m_runTypeRD)   { xFree(m_runTypeRD);   m_runTypeRD = NULL; }
+  if (m_runLengthRD) { xFree(m_runLengthRD); m_runLengthRD = NULL; }
+#endif
 }
 
 void IntraPrediction::init(ChromaFormat chromaFormatIDC, const unsigned bitDepthY)
@@ -230,6 +234,10 @@ 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.
   }
+#if JVET_O0119_BASE_PALETTE_444
+  m_runTypeRD = (bool*)xMalloc(bool, MAX_CU_SIZE*MAX_CU_SIZE);
+  m_runLengthRD = (Pel*)xMalloc(Pel, MAX_CU_SIZE*MAX_CU_SIZE);
+#endif
 }
 
 // ====================================================================================================================
@@ -2169,4 +2177,150 @@ void IntraPrediction::predIntraMip( const ComponentID compId, PelBuf &piPred, co
   m_matrixIntraPred.predBlock( pu.Y(), pu.intraDir[CHANNEL_TYPE_LUMA], piPred, bitDepth );
 }
 
+#if JVET_O0119_BASE_PALETTE_444
+bool IntraPrediction::calCopyRun(CodingStructure &cs, Partitioner& partitioner, uint32_t startPos, uint32_t total, uint32_t &run, ComponentID compBegin)
+{
+  CodingUnit    &cu = *cs.getCU(partitioner.chType);
+  TransformUnit &tu = *cs.getTU(partitioner.chType);
+  PelBuf     curPLTIdx = tu.getcurPLTIdx(compBegin);
+  PLTtypeBuf runType   = tu.getrunType(compBegin);
+
+  uint32_t idx = startPos;
+  uint32_t xPos;
+  uint32_t yPos;
+  bool valid = false;
+  run = 0;
+  while (idx < total)
+  {
+    xPos = m_scanOrder[idx].x;
+    yPos = m_scanOrder[idx].y;
+    runType.at(xPos, yPos) = PLT_RUN_COPY;
+
+    if (yPos == 0 && !cu.useRotation[compBegin])
+    {
+      return false;
+    }
+    if (xPos == 0 && cu.useRotation[compBegin])
+    {
+      return false;
+    }
+    if (!cu.useRotation[compBegin] && curPLTIdx.at(xPos, yPos) == curPLTIdx.at(xPos, yPos - 1))
+    {
+      run++;
+      valid = true;
+    }
+    else if (cu.useRotation[compBegin] && curPLTIdx.at(xPos, yPos) == curPLTIdx.at(xPos - 1, yPos))
+    {
+      run++;
+      valid = true;
+    }
+    else
+    {
+      break;
+    }
+    idx++;
+  }
+  return valid;
+}
+bool IntraPrediction::calIndexRun(CodingStructure &cs, Partitioner& partitioner, uint32_t startPos, uint32_t total, uint32_t &run, ComponentID compBegin)
+{
+  TransformUnit &tu = *cs.getTU(partitioner.chType);
+  PelBuf     curPLTIdx = tu.getcurPLTIdx(compBegin);
+  PLTtypeBuf runType   = tu.getrunType(compBegin);
+
+  run = 1;
+  uint32_t idx = startPos;
+  while (idx < total)
+  {
+    uint32_t xPos = m_scanOrder[idx].x;
+    uint32_t yPos = m_scanOrder[idx].y;
+    runType.at(xPos, yPos) = PLT_RUN_INDEX;
+
+    uint32_t xPrev = idx == 0 ? 0 : m_scanOrder[idx - 1].x;
+    uint32_t yPrev = idx == 0 ? 0 : m_scanOrder[idx - 1].y;
+    if (idx > startPos && curPLTIdx.at(xPos, yPos) == curPLTIdx.at(xPrev, yPrev))
+    {
+      run++;
+    }
+    else if (idx > startPos)
+    {
+      break;
+    }
+    idx++;
+  }
+  return true;
+}
+void IntraPrediction::reorderPLT(CodingStructure& cs, Partitioner& partitioner, ComponentID compBegin, uint32_t numComp)
+{
+  CodingUnit &cu = *cs.getCU(partitioner.chType);
+
+  uint32_t       reusePLTSizetmp = 0;
+  uint32_t       pltSizetmp = 0;
+  Pel            curPLTtmp[MAX_NUM_COMPONENT][MAXPLTSIZE];
+  bool           curPLTpred[MAXPLTPREDSIZE];
+
+  for (int idx = 0; idx < MAXPLTPREDSIZE; idx++)
+  {
+    curPLTpred[idx] = false;
+    cu.reuseflag[compBegin][idx] = false;
+  }
+  for (int idx = 0; idx < MAXPLTSIZE; idx++)
+  {
+    curPLTpred[idx] = false;
+  }
+
+  for (int predidx = 0; predidx < cs.prevPLT.curPLTSize[compBegin]; predidx++)
+  {
+    bool match = false;
+    int curidx = 0;
+
+    for (curidx = 0; curidx < cu.curPLTSize[compBegin]; curidx++)
+    {
+      bool matchTmp = true;
+      for (int comp = compBegin; comp < (compBegin + numComp); comp++)
+      {
+        matchTmp = matchTmp && (cu.curPLT[comp][curidx] == cs.prevPLT.curPLT[comp][predidx]);
+      }
+      if (matchTmp)
+      {
+        match = true;
+        break;
+      }
+    }
+
+    if (match)
+    {
+      cu.reuseflag[compBegin][predidx] = true;
+      curPLTpred[curidx] = true;
+      for (int comp = compBegin; comp < (compBegin + numComp); comp++)
+      {
+        curPLTtmp[comp][reusePLTSizetmp] = cs.prevPLT.curPLT[comp][predidx];
+      }
+      reusePLTSizetmp++;
+      pltSizetmp++;
+    }
+  }
+  cu.reusePLTSize[compBegin] = reusePLTSizetmp;
+  for (int curidx = 0; curidx < cu.curPLTSize[compBegin]; curidx++)
+  {
+    if (!curPLTpred[curidx])
+    {
+      for (int comp = compBegin; comp < (compBegin + numComp); comp++)
+      {
+        curPLTtmp[comp][pltSizetmp] = cu.curPLT[comp][curidx];
+      }
+      pltSizetmp++;
+    }
+  }
+  assert(pltSizetmp == cu.curPLTSize[compBegin]);
+  for (int curidx = 0; curidx < cu.curPLTSize[compBegin]; curidx++)
+  {
+    for (int comp = compBegin; comp < (compBegin + numComp); comp++)
+    {
+      cu.curPLT[comp][curidx] = curPLTtmp[comp][curidx];
+    }
+  }
+}
+#endif
+
 //! \}
diff --git a/source/Lib/CommonLib/IntraPrediction.h b/source/Lib/CommonLib/IntraPrediction.h
index a942a13d58471a82528f3fc45bd6a0f191275d25..29ccc472f74c33e190f19c10ad4e397a2e2ad66a 100644
--- a/source/Lib/CommonLib/IntraPrediction.h
+++ b/source/Lib/CommonLib/IntraPrediction.h
@@ -123,12 +123,23 @@ private:
   Pel* m_pMdlmTemp; // for MDLM mode
   MatrixIntraPrediction m_matrixIntraPred;
 
-protected:
 
+
+protected:
   ChromaFormat  m_currChromaFormat;
 
   int m_topRefLength;
   int m_leftRefLength;
+#if JVET_O0119_BASE_PALETTE_444
+  ScanElement* m_scanOrder;
+  bool         m_bestScanRotationMode;
+  Ctx          m_storeCtxRun;
+  Ctx          m_storeCtxRunIndex;
+  Ctx          m_storeCtxRunCopy; 
+  Ctx          m_orgCtxRD;
+  bool         *m_runTypeRD;
+  Pel          *m_runLengthRD;
+#endif
   // prediction
   void xPredIntraPlanar           ( const CPelBuf &pSrc, PelBuf &pDst );
   void xPredIntraDc               ( const CPelBuf &pSrc, PelBuf &pDst, const ChannelType channelType, const bool enableBoundaryFilter = true );
@@ -185,6 +196,11 @@ public:
   Pel* getPredictorPtr2           (const ComponentID compID, uint32_t idx) { return m_yuvExt2[compID][idx]; }
   void switchBuffer               (const PredictionUnit &pu, ComponentID compID, PelBuf srcBuff, Pel *dst);
   void geneIntrainterPred         (const CodingUnit &cu);
+#if JVET_O0119_BASE_PALETTE_444
+  void reorderPLT(CodingStructure& cs, Partitioner& partitioner, ComponentID compBegin, uint32_t numComp);
+  bool calCopyRun(CodingStructure &cs, Partitioner& partitioner, uint32_t startPos, uint32_t total, uint32_t &run, ComponentID compBegin);
+  bool calIndexRun(CodingStructure &cs, Partitioner& partitioner, uint32_t startPos, uint32_t total, uint32_t &run, ComponentID compBegin);
+#endif
 };
 
 //! \}
diff --git a/source/Lib/CommonLib/LoopFilter.cpp b/source/Lib/CommonLib/LoopFilter.cpp
index eb210178dbeb5763759fc5f288dbdbb95fbef93a..8ce43b547e7458e737df6e740bc0aaee895a5e2b 100644
--- a/source/Lib/CommonLib/LoopFilter.cpp
+++ b/source/Lib/CommonLib/LoopFilter.cpp
@@ -878,6 +878,9 @@ void LoopFilter::xEdgeFilterLuma( const CodingUnit& cu, const DeblockEdgeDir edg
   const SPS     &sps      = *(cu.cs->sps);
   const Slice   &slice    = *(cu.slice);
   const bool    ppsTransquantBypassEnabledFlag = pps.getTransquantBypassEnabledFlag();
+#if JVET_O0119_BASE_PALETTE_444
+  const bool    spsPaletteEnabledFlag = sps.getPLTMode();
+#endif
   const int     bitDepthLuma                   = sps.getBitDepth(CHANNEL_TYPE_LUMA);
   const ClpRng& clpRng( cu.cs->slice->clpRng(COMPONENT_Y) );
 
@@ -1056,6 +1059,14 @@ void LoopFilter::xEdgeFilterLuma( const CodingUnit& cu, const DeblockEdgeDir edg
             bPartPNoFilter = bPartPNoFilter || cuP.transQuantBypass;
             bPartQNoFilter = bPartQNoFilter || cuQ.transQuantBypass;
           }
+#if JVET_O0119_BASE_PALETTE_444
+          if (spsPaletteEnabledFlag)
+          {
+            // check if each of PUs is palette coded
+            bPartPNoFilter = bPartPNoFilter || CU::isPLT(cuP);
+            bPartQNoFilter = bPartQNoFilter || CU::isPLT(cuQ);
+          }
+#endif
 
           if (dL < iBeta)
           {
@@ -1101,6 +1112,14 @@ void LoopFilter::xEdgeFilterLuma( const CodingUnit& cu, const DeblockEdgeDir edg
           bPartPNoFilter = bPartPNoFilter || cuP.transQuantBypass;
           bPartQNoFilter = bPartQNoFilter || cuQ.transQuantBypass;
         }
+#if JVET_O0119_BASE_PALETTE_444
+        if( spsPaletteEnabledFlag)
+        {
+          // check if each of PUs is palette coded
+          bPartPNoFilter = bPartPNoFilter || CU::isPLT(cuP);
+          bPartQNoFilter = bPartQNoFilter || CU::isPLT(cuQ);
+        }
+#endif
 
         if( d < iBeta )
         {
@@ -1251,6 +1270,14 @@ void LoopFilter::xEdgeFilterChroma(const CodingUnit& cu, const DeblockEdgeDir ed
         bPartPNoFilter = bPartPNoFilter || cuP.transQuantBypass;
         bPartQNoFilter = bPartQNoFilter || cuQ.transQuantBypass;
       }
+#if JVET_O0119_BASE_PALETTE_444
+      if ( sps.getPLTMode())
+      {
+        // check if each of PUs is palette coded
+        bPartPNoFilter = bPartPNoFilter || CU::isPLT(cuP);
+        bPartQNoFilter = bPartQNoFilter || CU::isPLT(cuQ);
+      }
+#endif
 
       const int maxFilterLengthP = m_maxFilterLengthP[COMPONENT_Cb][(pos.x-m_ctuXLumaSamples)>>m_shiftHor][(pos.y-m_ctuYLumaSamples)>>m_shiftVer];
       const int maxFilterLengthQ = m_maxFilterLengthQ[COMPONENT_Cb][(pos.x-m_ctuXLumaSamples)>>m_shiftHor][(pos.y-m_ctuYLumaSamples)>>m_shiftVer];
diff --git a/source/Lib/CommonLib/Rom.cpp b/source/Lib/CommonLib/Rom.cpp
index 721e683b24a53970d6854862703be200a83a6fca..c6895b6e2d611312566f195431651cd9b980147e 100644
--- a/source/Lib/CommonLib/Rom.cpp
+++ b/source/Lib/CommonLib/Rom.cpp
@@ -53,7 +53,6 @@ CDTrace *g_trace_ctx = NULL;
 #endif
 bool g_mctsDecCheckEnabled = false;
 
-
 //! \ingroup CommonLib
 //! \{
 
@@ -130,6 +129,53 @@ public:
         }
         break;
 
+#if JVET_O0119_BASE_PALETTE_444
+      case SCAN_TRAV_HOR:
+      {
+        if (m_line % 2 == 0)
+        {
+          if (m_column == (m_blockWidth - 1))
+          {
+            m_line++;
+            m_column = m_blockWidth - 1;
+        }
+        else m_column++;
+        }
+        else
+        {
+          if (m_column == 0)
+          {
+            m_line++;
+            m_column = 0;
+          }
+          else m_column--;
+        }
+      }
+      break;
+
+      case SCAN_TRAV_VER:
+      {
+        if (m_column % 2 == 0)
+        {
+          if (m_line == (m_blockHeight - 1))
+          {
+            m_column++;
+            m_line = m_blockHeight - 1;
+          }
+          else m_line++;
+        }
+        else
+        {
+          if (m_line == 0)
+          {
+            m_column++;
+            m_line = 0;
+          }
+          else m_line--;
+        }
+      }
+      break;
+#endif
       //------------------------------------------------
 
       default:
@@ -762,4 +808,9 @@ Mv   g_reusedUniMVs[32][32][8][8][2][33];
 bool g_isReusedUniMVsFilled[32][32][8][8];
 #endif
 
+#if JVET_O0119_BASE_PALETTE_444
+const uint8_t g_paletteQuant[52] = { 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 7, 7, 8, 9, 9, 10, 11, 12, 13, 14, 15, 16, 17, 19, 21, 22, 24, 23, 25, 26, 28, 29, 31, 32, 34, 36, 37, 39, 41, 42, 45 };
+uint8_t g_paletteRunTopLut [5] = { 0, 1, 1, 2, 2 };
+uint8_t g_paletteRunLeftLut[5] = { 0, 3, 3, 4, 4 };
+#endif
 //! \}
diff --git a/source/Lib/CommonLib/Rom.h b/source/Lib/CommonLib/Rom.h
index 3bfe26e204d0077aabff6b31ca36553d5a40f5dd..1dc86ce08a62f22a60a4ed7e612b547a6e415ddb 100644
--- a/source/Lib/CommonLib/Rom.h
+++ b/source/Lib/CommonLib/Rom.h
@@ -59,6 +59,7 @@ void         destroyROM();
 // Data structure related table & variable
 // ====================================================================================================================
 
+
 // flexible conversion from relative to absolute index
 struct ScanElement
 {
@@ -232,5 +233,11 @@ extern Mv   g_reusedUniMVs[32][32][8][8][2][33];
 extern bool g_isReusedUniMVsFilled[32][32][8][8];
 #endif
 
+#if JVET_O0119_BASE_PALETTE_444
+extern const uint8_t g_paletteQuant[52];
+extern uint8_t g_paletteRunTopLut[5];
+extern uint8_t g_paletteRunLeftLut[5];
+#endif
+
 #endif  //__TCOMROM__
 
diff --git a/source/Lib/CommonLib/SampleAdaptiveOffset.cpp b/source/Lib/CommonLib/SampleAdaptiveOffset.cpp
index febb1dde94239ecd9d12f3f8493eda293b852e62..e007ff97ff6e0400ee938a76d9ab2ed58eb4ac19 100644
--- a/source/Lib/CommonLib/SampleAdaptiveOffset.cpp
+++ b/source/Lib/CommonLib/SampleAdaptiveOffset.cpp
@@ -729,7 +729,6 @@ void SampleAdaptiveOffset::xPCMCURestoration(CodingStructure& cs, const UnitArea
 void SampleAdaptiveOffset::xPCMSampleRestoration(CodingUnit& cu, const ComponentID compID)
 {
   const CompArea& ca = cu.block(compID);
-
   if( CU::isLosslessCoded( cu ) && !cu.ipcm )
   {
     for( auto &currTU : CU::traverseTUs( cu ) )
diff --git a/source/Lib/CommonLib/Slice.cpp b/source/Lib/CommonLib/Slice.cpp
index 7e22942a87dc1c00776106d21e2dccc264109cbb..2404f16821df58912232b775e2dd1a19a4a8b6dc 100644
--- a/source/Lib/CommonLib/Slice.cpp
+++ b/source/Lib/CommonLib/Slice.cpp
@@ -1447,6 +1447,9 @@ SPS::SPS()
 , m_wrapAroundEnabledFlag     (false)
 , m_wrapAroundOffset          (  0)
 , m_IBCFlag                   (  0)
+#if JVET_O0119_BASE_PALETTE_444
+, m_PLTMode                   (  0)
+#endif
 , m_lumaReshapeEnable         (false)
 , m_AMVREnabledFlag                       ( false )
 , m_LMChroma                  ( false )
diff --git a/source/Lib/CommonLib/Slice.h b/source/Lib/CommonLib/Slice.h
index 9f1206a5d46d3bfc160158f66e2d1f2437a0f2ac..bf8fa59fd884f8ffdcff9dffe16b26f7b4e71862 100644
--- a/source/Lib/CommonLib/Slice.h
+++ b/source/Lib/CommonLib/Slice.h
@@ -47,6 +47,9 @@
 #include "ChromaFormat.h"
 #include "Common.h"
 #include "HRD.h"
+#if JVET_O0119_BASE_PALETTE_444
+#include <unordered_map>
+#endif
 #include "AlfParameters.h"
 
 //! \ingroup CommonLib
@@ -835,6 +838,9 @@ private:
   bool              m_wrapAroundEnabledFlag;
   unsigned          m_wrapAroundOffset;
   unsigned          m_IBCFlag;
+#if JVET_O0119_BASE_PALETTE_444
+  unsigned          m_PLTMode;
+#endif
 
   bool              m_lumaReshapeEnable;
   bool              m_AMVREnabledFlag;
@@ -1066,6 +1072,10 @@ public:
   bool                    getUseReshaper() const                                                          { return m_lumaReshapeEnable;                                                }
   void                    setIBCFlag(unsigned IBCFlag)                                                    { m_IBCFlag = IBCFlag; }
   unsigned                getIBCFlag() const                                                              { return m_IBCFlag; }
+#if JVET_O0119_BASE_PALETTE_444
+  void                    setPLTMode(unsigned PLTMode)                                                    { m_PLTMode = PLTMode; }
+  unsigned                getPLTMode() const                                                              { return m_PLTMode; }
+#endif
   void                    setUseSBT( bool b )                                                             { m_SBT = b; }
   bool                    getUseSBT() const                                                               { return m_SBT; }
   void                    setUseISP( bool b )                                                             { m_ISP = b; }
@@ -1945,6 +1955,11 @@ public:
 protected:
   Picture*              xGetRefPic        (PicList& rcListPic, int poc);
   Picture*              xGetLongTermRefPic(PicList& rcListPic, int poc, bool pocHasMsb);
+#if JVET_O0119_BASE_PALETTE_444
+public:
+  std::unordered_map< Position, std::unordered_map< Size, double> > m_mapPltCost;
+private:
+#endif
 };// END CLASS DEFINITION Slice
 
 void calculateParameterSetChangedFlag(bool &bChanged, const std::vector<uint8_t> *pOldData, const std::vector<uint8_t> *pNewData);
diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index c84ded510d3345fd7c48085b7b210602236b0157..2f47b2e91688c2b3651f58d05c0d5cf89e1fc96b 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -50,6 +50,9 @@
 #include <assert.h>
 #include <cassert>
 
+
+#define JVET_O0119_BASE_PALETTE_444                       1 // JVET-O0119: Palette mode in HEVC and palette mode signaling in JVET-N0258. Only enabled for YUV444.    
+
 #define JVET_O0304_SIMPLIFIED_BDOF                        1 // JVET-O0304: Reduction of number of multiplications in BDOF
 
 #define JVET_O0455_IBC_MAX_MERGE_NUM                      1 // JVET-O0455: Control the max number of IBC merge candidates independently from regular merge candidates
@@ -629,7 +632,12 @@ enum PredMode
   MODE_INTER                 = 0,     ///< inter-prediction mode
   MODE_INTRA                 = 1,     ///< intra-prediction mode
   MODE_IBC                   = 2,     ///< ibc-prediction mode
+#if JVET_O0119_BASE_PALETTE_444
+  MODE_PLT = 3,     ///< plt-prediction mode
+  NUMBER_OF_PREDICTION_MODES = 4,
+#else
   NUMBER_OF_PREDICTION_MODES = 3,
+#endif
 };
 
 /// reference list index
@@ -755,6 +763,10 @@ enum MESearchMethod
 enum CoeffScanType
 {
   SCAN_DIAG = 0,        ///< up-right diagonal scan
+#if JVET_O0119_BASE_PALETTE_444
+  SCAN_TRAV_HOR = 1,
+  SCAN_TRAV_VER = 2,
+#endif
   SCAN_NUMBER_OF_TYPES
 };
 
@@ -1082,6 +1094,14 @@ struct BitDepths
   int recon[MAX_NUM_CHANNEL_TYPE]; ///< the bit depth as indicated in the SPS
 };
 
+#if JVET_O0119_BASE_PALETTE_444
+enum PLTRunMode
+{
+  PLT_RUN_INDEX = 0,
+  PLT_RUN_COPY = 1,
+  NUM_PLT_RUN = 2
+};
+#endif
 /// parameters for deblocking filter
 struct LFCUParam
 {
diff --git a/source/Lib/CommonLib/Unit.cpp b/source/Lib/CommonLib/Unit.cpp
index 74a3cc0ec6952f1ad9fffc7a7190dcd94d78d326..b3c80a04e638f066b2346c92249318f7117d9704 100644
--- a/source/Lib/CommonLib/Unit.cpp
+++ b/source/Lib/CommonLib/Unit.cpp
@@ -287,7 +287,18 @@ CodingUnit& CodingUnit::operator=( const CodingUnit& other )
   smvdMode        = other.smvdMode;
   ispMode           = other.ispMode;
   mipFlag           = other.mipFlag;
-
+#if JVET_O0119_BASE_PALETTE_444
+  for (int idx = 0; idx < MAX_NUM_COMPONENT; idx++)
+  {
+    curPLTSize[idx] = other.curPLTSize[idx];
+    useEscape[idx] = other.useEscape[idx];
+    useRotation[idx] = other.useRotation[idx];
+    reusePLTSize[idx] = other.reusePLTSize[idx];
+    lastPLTSize[idx] = other.lastPLTSize[idx];
+    memcpy(curPLT[idx], other.curPLT[idx], MAXPLTSIZE * sizeof(Pel));
+    memcpy(reuseflag[idx], other.reuseflag[idx], MAXPLTPREDSIZE * sizeof(bool));
+  }
+#endif
   return *this;
 }
 
@@ -325,6 +336,18 @@ void CodingUnit::initData()
   smvdMode        = 0;
   ispMode           = 0;
   mipFlag           = false;
+#if JVET_O0119_BASE_PALETTE_444
+  for (int idx = 0; idx < MAX_NUM_COMPONENT; idx++)
+  {
+    curPLTSize[idx] = 0;
+    reusePLTSize[idx] = 0;
+    lastPLTSize[idx] = 0;
+    useEscape[idx] = false;
+    useRotation[idx] = false;
+    memset(curPLT[idx], 0, MAXPLTSIZE * sizeof(Pel));
+    memset(reuseflag[idx], false, MAXPLTPREDSIZE * sizeof(bool));
+  }
+#endif
 }
 
 #if JVET_O1124_ALLOW_CCLM_COND
@@ -409,7 +432,11 @@ const uint8_t CodingUnit::checkAllowedSbt() const
   }
 
   //check on prediction mode
+#if JVET_O0119_BASE_PALETTE_444
+  if (predMode == MODE_INTRA || predMode == MODE_IBC || predMode == MODE_PLT) //intra, palette or IBC
+#else
   if( predMode == MODE_INTRA || predMode == MODE_IBC ) //intra or IBC
+#endif
   {
     return 0;
   }
@@ -667,6 +694,10 @@ TransformUnit::TransformUnit(const UnitArea& unit) : UnitArea(unit), cu(nullptr)
   {
     m_coeffs[i] = nullptr;
     m_pcmbuf[i] = nullptr;
+#if JVET_O0119_BASE_PALETTE_444
+    m_runType[i] = nullptr;
+    m_runLength[i] = nullptr;
+#endif
   }
 
   initData();
@@ -678,6 +709,10 @@ TransformUnit::TransformUnit(const ChromaFormat _chromaFormat, const Area &_area
   {
     m_coeffs[i] = nullptr;
     m_pcmbuf[i] = nullptr;
+#if JVET_O0119_BASE_PALETTE_444
+    m_runType[i] = nullptr;
+    m_runLength[i] = nullptr;
+#endif
   }
 
   initData();
@@ -698,7 +733,11 @@ void TransformUnit::initData()
   m_chromaResScaleInv = 0;
 }
 
+#if JVET_O0119_BASE_PALETTE_444
+void TransformUnit::init(TCoeff **coeffs, Pel **pcmbuf, Pel **runLength, bool **runType)
+#else
 void TransformUnit::init(TCoeff **coeffs, Pel **pcmbuf)
+#endif
 {
   uint32_t numBlocks = getNumberValidTBlocks(*cs->pcv);
 
@@ -706,6 +745,10 @@ void TransformUnit::init(TCoeff **coeffs, Pel **pcmbuf)
   {
     m_coeffs[i] = coeffs[i];
     m_pcmbuf[i] = pcmbuf[i];
+#if JVET_O0119_BASE_PALETTE_444
+    m_runType[i] = runType[i];
+    m_runLength[i] = runLength[i];
+#endif
   }
 }
 
@@ -722,6 +765,10 @@ TransformUnit& TransformUnit::operator=(const TransformUnit& other)
 
     if (m_coeffs[i] && other.m_coeffs[i] && m_coeffs[i] != other.m_coeffs[i]) memcpy(m_coeffs[i], other.m_coeffs[i], sizeof(TCoeff) * area);
     if (m_pcmbuf[i] && other.m_pcmbuf[i] && m_pcmbuf[i] != other.m_pcmbuf[i]) memcpy(m_pcmbuf[i], other.m_pcmbuf[i], sizeof(Pel   ) * area);
+#if JVET_O0119_BASE_PALETTE_444
+    if (m_runType[i] && other.m_runType[i] && m_runType[i] != other.m_runType[i]) memcpy(m_runType[i], other.m_runType[i], sizeof(bool) * area);
+    if (m_runLength[i] && other.m_runLength[i] && m_runLength[i] != other.m_runLength[i]) memcpy(m_runLength[i], other.m_runLength[i], sizeof(Pel) * area);
+#endif
 
     cbf[i]           = other.cbf[i];
     rdpcm[i]         = other.rdpcm[i];
@@ -744,6 +791,10 @@ void TransformUnit::copyComponentFrom(const TransformUnit& other, const Componen
 
   if (m_coeffs[i] && other.m_coeffs[i] && m_coeffs[i] != other.m_coeffs[i]) memcpy(m_coeffs[i], other.m_coeffs[i], sizeof(TCoeff) * area);
   if (m_pcmbuf[i] && other.m_pcmbuf[i] && m_pcmbuf[i] != other.m_pcmbuf[i]) memcpy(m_pcmbuf[i], other.m_pcmbuf[i], sizeof(Pel   ) * area);
+#if JVET_O0119_BASE_PALETTE_444
+  if (m_runType[i] && other.m_runType[i] && m_runType[i] != other.m_runType[i]) memcpy(m_runType[i], other.m_runType[i], sizeof(bool) * area);
+  if (m_runLength[i] && other.m_runLength[i] && m_runLength[i] != other.m_runLength[i]) memcpy(m_runLength[i], other.m_runLength[i], sizeof(Pel) * area);
+#endif
 
   cbf[i]           = other.cbf[i];
   rdpcm[i]         = other.rdpcm[i];
@@ -761,6 +812,24 @@ const CCoeffBuf TransformUnit::getCoeffs(const ComponentID id) const { return CC
        PelBuf   TransformUnit::getPcmbuf(const ComponentID id)       { return  PelBuf  (m_pcmbuf[id], blocks[id]); }
 const CPelBuf   TransformUnit::getPcmbuf(const ComponentID id) const { return CPelBuf  (m_pcmbuf[id], blocks[id]); }
 
+#if JVET_O0119_BASE_PALETTE_444
+       PelBuf TransformUnit::getcurPLTIdx(const ComponentID id) { return  PelBuf(m_pcmbuf[id], blocks[id]); }
+const CPelBuf TransformUnit::getcurPLTIdx(const ComponentID id) const { return CPelBuf(m_pcmbuf[id], blocks[id]); }
+
+       PelBuf TransformUnit::getrunLength(const ComponentID id) { return  PelBuf(m_runLength[id], blocks[id]); }
+const CPelBuf TransformUnit::getrunLength(const ComponentID id) const { return CPelBuf(m_runLength[id], blocks[id]); }
+
+       PLTtypeBuf TransformUnit::getrunType(const ComponentID id) { return  PLTtypeBuf(m_runType[id], blocks[id]); }
+const CPLTtypeBuf TransformUnit::getrunType(const ComponentID id)   const { return CPLTtypeBuf(m_runType[id], blocks[id]); }
+
+       PLTescapeBuf TransformUnit::getescapeValue(const ComponentID id) { return  PLTescapeBuf(m_coeffs[id], blocks[id]); }
+const CPLTescapeBuf TransformUnit::getescapeValue(const ComponentID id)  const { return CPLTescapeBuf(m_coeffs[id], blocks[id]); }
+
+Pel*  TransformUnit::getPLTIndex(const ComponentID id) { return  m_pcmbuf[id]; }
+Pel*  TransformUnit::getRunLens (const ComponentID id) { return  m_runLength[id]; }
+bool* TransformUnit::getRunTypes(const ComponentID id) { return  m_runType[id]; }
+#endif
+
 void TransformUnit::checkTuNoResidual( unsigned idx )
 {
   if( CU::getSbtIdx( cu->sbtInfo ) == SBT_OFF_DCT )
diff --git a/source/Lib/CommonLib/Unit.h b/source/Lib/CommonLib/Unit.h
index 16645400555f2678b53607cf743124a64b2d5c1a..df7b398a98d270ff2360f58013e5f313f0825306 100644
--- a/source/Lib/CommonLib/Unit.h
+++ b/source/Lib/CommonLib/Unit.h
@@ -48,7 +48,12 @@
 // ---------------------------------------------------------------------------
 // tools
 // ---------------------------------------------------------------------------
-
+#if JVET_O0119_BASE_PALETTE_444
+struct PLTBuf {
+  uint32_t       curPLTSize[MAX_NUM_COMPONENT];
+  Pel            curPLT[MAX_NUM_COMPONENT][MAXPLTPREDSIZE];
+};
+#endif
 inline Position recalcPosition(const ChromaFormat _cf, const ComponentID srcCId, const ComponentID dstCId, const Position &pos)
 {
   if( toChannelType( srcCId ) == toChannelType( dstCId ) )
@@ -322,6 +327,15 @@ struct CodingUnit : public UnitArea
   Size           shareParentSize;
   uint8_t          smvdMode;
   uint8_t        ispMode;
+#if JVET_O0119_BASE_PALETTE_444
+  bool           useEscape[MAX_NUM_COMPONENT];
+  bool           useRotation[MAX_NUM_COMPONENT];
+  bool           reuseflag[MAX_NUM_COMPONENT][MAXPLTPREDSIZE];
+  uint32_t       lastPLTSize[MAX_NUM_COMPONENT];
+  uint32_t       reusePLTSize[MAX_NUM_COMPONENT];
+  uint32_t       curPLTSize[MAX_NUM_COMPONENT];
+  Pel            curPLT[MAX_NUM_COMPONENT][MAXPLTSIZE];
+#endif
 
   CodingUnit() : chType( CH_L ) { }
   CodingUnit(const UnitArea &unit);
@@ -461,7 +475,11 @@ struct TransformUnit : public UnitArea
   TransformUnit *next;
   TransformUnit *prev;
 
+#if JVET_O0119_BASE_PALETTE_444
+  void init(TCoeff **coeffs, Pel **pcmbuf, Pel **runLength, bool **runType);
+#else
   void init(TCoeff **coeffs, Pel **pcmbuf);
+#endif
 
   TransformUnit& operator=(const TransformUnit& other);
   void copyComponentFrom  (const TransformUnit& other, const ComponentID compID);
@@ -476,6 +494,19 @@ struct TransformUnit : public UnitArea
   const CPelBuf   getPcmbuf(const ComponentID id) const;
         int       getChromaAdj( )                 const;
         void      setChromaAdj(int i);
+#if JVET_O0119_BASE_PALETTE_444
+         PelBuf   getcurPLTIdx(const ComponentID id);
+  const CPelBuf   getcurPLTIdx(const ComponentID id) const;
+         PelBuf   getrunLength(const ComponentID id);
+  const CPelBuf   getrunLength(const ComponentID id) const;
+         PLTtypeBuf   getrunType(const ComponentID id);
+  const CPLTtypeBuf   getrunType(const ComponentID id) const;
+         PLTescapeBuf getescapeValue(const ComponentID id);
+  const CPLTescapeBuf getescapeValue(const ComponentID id) const;
+        Pel*      getPLTIndex(const ComponentID id);
+        Pel*      getRunLens(const ComponentID id);
+        bool*     getRunTypes(const ComponentID id);
+#endif
 
 #if ENABLE_SPLIT_PARALLELISM || ENABLE_WPP_PARALLELISM
   int64_t cacheId;
@@ -485,6 +516,10 @@ struct TransformUnit : public UnitArea
 private:
   TCoeff *m_coeffs[ MAX_NUM_TBLOCKS ];
   Pel    *m_pcmbuf[ MAX_NUM_TBLOCKS ];
+#if JVET_O0119_BASE_PALETTE_444
+  bool   *m_runType[MAX_NUM_TBLOCKS];
+  Pel    *m_runLength[MAX_NUM_TBLOCKS];
+#endif
 };
 
 // ---------------------------------------------------------------------------
diff --git a/source/Lib/CommonLib/UnitTools.cpp b/source/Lib/CommonLib/UnitTools.cpp
index ec14d617025c822dce3cf110722fa740f184d5c1..55da1ffc2535c31f9fa55ffab3d6bbe13b58da8e 100755
--- a/source/Lib/CommonLib/UnitTools.cpp
+++ b/source/Lib/CommonLib/UnitTools.cpp
@@ -115,6 +115,13 @@ bool CU::isIBC(const CodingUnit &cu)
   return cu.predMode == MODE_IBC;
 }
 
+#if JVET_O0119_BASE_PALETTE_444
+bool CU::isPLT(const CodingUnit &cu)
+{
+  return cu.predMode == MODE_PLT;
+}
+#endif
+
 bool CU::isRDPCMEnabled(const CodingUnit& cu)
 {
   return cu.cs->sps->getSpsRangeExtension().getRdpcmEnabledFlag(cu.predMode == MODE_INTRA ? RDPCM_SIGNAL_IMPLICIT : RDPCM_SIGNAL_EXPLICIT);
diff --git a/source/Lib/CommonLib/UnitTools.h b/source/Lib/CommonLib/UnitTools.h
index db9e6a99ccc29aa389522771c215557157ce0766..0530ae2dbb4e6348bb7e98e7cd58140e62fe1e72 100644
--- a/source/Lib/CommonLib/UnitTools.h
+++ b/source/Lib/CommonLib/UnitTools.h
@@ -59,6 +59,9 @@ namespace CU
   bool isIntra                        (const CodingUnit &cu);
   bool isInter                        (const CodingUnit &cu);
   bool isIBC                          (const CodingUnit &cu);
+#if JVET_O0119_BASE_PALETTE_444
+  bool isPLT                          (const CodingUnit &cu);
+#endif
   bool isRDPCMEnabled                 (const CodingUnit &cu);
   bool isLosslessCoded                (const CodingUnit &cu);
   uint32_t getIntraSizeIdx                (const CodingUnit &cu);
diff --git a/source/Lib/DecoderLib/CABACReader.cpp b/source/Lib/DecoderLib/CABACReader.cpp
index 4b10f5c300de874f78e67f61ac7a392a5478dc36..ca63508c569648ad3d13229120c5022a75c68af0 100755
--- a/source/Lib/DecoderLib/CABACReader.cpp
+++ b/source/Lib/DecoderLib/CABACReader.cpp
@@ -620,6 +620,34 @@ bool CABACReader::coding_tree( CodingStructure& cs, Partitioner& partitioner, CU
 
   bool isLastCtu = coding_unit( cu, partitioner, cuCtx );
 
+#if JVET_O0119_BASE_PALETTE_444
+  uint32_t compBegin;
+  uint32_t numComp;
+  bool jointPLT = false;
+  if (CS::isDualITree(*cu.cs))
+  {
+    if (isLuma(partitioner.chType))
+    {
+      compBegin = COMPONENT_Y;
+      numComp = 1;
+    }
+    else
+    {
+      compBegin = COMPONENT_Cb;
+      numComp = 2;
+    }
+  }
+  else
+  {
+    compBegin = COMPONENT_Y;
+    numComp = 3;
+    jointPLT = true;
+  }
+  if (CU::isPLT(cu))
+  {
+    cs.reorderPrevPLT(cs.prevPLT, cu.curPLTSize, cu.curPLT, cu.reuseflag, compBegin, numComp, jointPLT);
+  }
+#endif
   DTRACE( g_trace_ctx, D_QP, "x=%d, y=%d, w=%d, h=%d, qp=%d\n", cu.Y().x, cu.Y().y, cu.Y().width, cu.Y().height, cu.qp );
   if (startShareThisLevel == 1)
     shareStateDec = NO_SHARE;
@@ -742,6 +770,28 @@ bool CABACReader::coding_unit( CodingUnit &cu, Partitioner &partitioner, CUCtx&
 
   // prediction mode and partitioning data
   pred_mode ( cu );
+#if JVET_O0119_BASE_PALETTE_444
+  if (CU::isPLT(cu))
+  {
+    cs.addTU(cu, partitioner.chType);
+    if (CS::isDualITree(*cu.cs))
+    {
+      if (isLuma(partitioner.chType))
+      {
+        cu_palette_info(cu, COMPONENT_Y, 1, cuCtx);
+      }
+      if (cu.chromaFormat != CHROMA_400 && (partitioner.chType == CHANNEL_TYPE_CHROMA))
+      {
+        cu_palette_info(cu, COMPONENT_Cb, 2, cuCtx);
+      }
+    }
+    else
+    {
+      cu_palette_info(cu, COMPONENT_Y, 3, cuCtx);
+    }
+    return end_of_ctu(cu, cuCtx);
+  }
+#endif
   bdpcm_mode( cu, ComponentID( partitioner.chType ) );
 
   // --> create PUs
@@ -1015,12 +1065,30 @@ void CABACReader::pred_mode( CodingUnit& cu )
         cu.predMode = MODE_IBC;
       }
       }
+#if JVET_O0119_BASE_PALETTE_444
+    if (!CU::isIBC(cu) && cu.cs->slice->getSPS()->getPLTMode() && cu.lwidth() <= 64 && cu.lheight() <= 64)
+    {
+      if (m_BinDecoder.decodeBin(Ctx::PLTFlag(0)))
+      {
+        cu.predMode = MODE_PLT;
+      }
+    }
+#endif
     }
     else
     {
       if (m_BinDecoder.decodeBin(Ctx::PredMode(DeriveCtx::CtxPredModeFlag(cu))))
       {
         cu.predMode = MODE_INTRA;
+#if JVET_O0119_BASE_PALETTE_444
+    if (cu.cs->slice->getSPS()->getPLTMode() && cu.lwidth() <= 64 && cu.lheight() <= 64)
+    {
+      if (m_BinDecoder.decodeBin(Ctx::PLTFlag(0)))
+      {
+        cu.predMode = MODE_PLT;
+      }
+    }
+#endif
       }
       else
       {
@@ -1042,6 +1110,30 @@ void CABACReader::pred_mode( CodingUnit& cu )
   }
   else
   {
+#if JVET_O0119_BASE_PALETTE_444
+    if ( cu.cs->slice->isIntra() || (cu.lwidth() == 4 && cu.lheight() == 4) )
+    {
+    cu.predMode = MODE_INTRA;
+    if (cu.cs->slice->getSPS()->getPLTMode() && cu.lwidth() <= 64 && cu.lheight() <= 64)
+    {
+      if (m_BinDecoder.decodeBin(Ctx::PLTFlag(0)))
+      {
+          cu.predMode = MODE_PLT;
+      }
+    }
+    }
+    else
+    {
+    cu.predMode = m_BinDecoder.decodeBin(Ctx::PredMode(DeriveCtx::CtxPredModeFlag(cu))) ? MODE_INTRA : MODE_INTER;
+    if (!CU::isIntra(cu) && cu.cs->slice->getSPS()->getPLTMode() && cu.lwidth() <= 64 && cu.lheight() <= 64)
+    {
+      if (m_BinDecoder.decodeBin(Ctx::PLTFlag(0)))
+      {
+          cu.predMode = MODE_PLT;
+      }
+    }
+    }
+#else
     if ( cu.cs->slice->isIntra() || ( cu.lwidth() == 4 && cu.lheight() == 4 ) || m_BinDecoder.decodeBin( Ctx::PredMode( DeriveCtx::CtxPredModeFlag( cu ) ) ) )
     {
       cu.predMode = MODE_INTRA;
@@ -1050,6 +1142,7 @@ void CABACReader::pred_mode( CodingUnit& cu )
     {
       cu.predMode = MODE_INTER;
     }
+#endif
   }
 }
 void CABACReader::bdpcm_mode( CodingUnit& cu, const ComponentID compID )
@@ -1607,6 +1700,408 @@ bool CABACReader::end_of_ctu( CodingUnit& cu, CUCtx& cuCtx )
   return false;
 }
 
+#if JVET_O0119_BASE_PALETTE_444
+void CABACReader::cu_palette_info(CodingUnit& cu, ComponentID compBegin, uint32_t numComp, CUCtx& cuCtx)
+{
+  const SPS&      sps = *(cu.cs->sps);
+  TransformUnit&   tu = *cu.firstTU;
+  int curPLTidx = 0;
+
+  cu.lastPLTSize[compBegin] = cu.cs->prevPLT.curPLTSize[compBegin];
+
+  if (cu.lastPLTSize[compBegin])
+  {
+    xDecodePLTPredIndicator(cu, MAXPLTSIZE, compBegin);
+  }
+
+  for (int idx = 0; idx < cu.lastPLTSize[compBegin]; idx++)
+  {
+    if (cu.reuseflag[compBegin][idx])
+    {
+      for (int comp = compBegin; comp < (compBegin + numComp); comp++)
+      {
+        cu.curPLT[comp][curPLTidx] = cu.cs->prevPLT.curPLT[comp][idx];
+      }
+      curPLTidx++;
+    }
+  }
+
+  uint32_t recievedPLTnum = 0;
+
+  if (curPLTidx < MAXPLTSIZE)
+  {
+    recievedPLTnum = exp_golomb_eqprob(0);
+  }
+
+  cu.curPLTSize[compBegin] = curPLTidx + recievedPLTnum;
+  for (int comp = compBegin; comp < (compBegin + numComp); comp++)
+  {
+    for (int idx = curPLTidx; idx < cu.curPLTSize[compBegin]; idx++)
+    {
+      ComponentID compID = (ComponentID)comp;
+      const int  channelBitDepth = sps.getBitDepth(toChannelType(compID));
+      cu.curPLT[compID][idx] = m_BinDecoder.decodeBinsEP(channelBitDepth);
+    }
+  }
+  cu.useEscape[compBegin] = true; // JC 
+  if (cu.curPLTSize[compBegin] > 0)
+  {
+    uint32_t escCode = 0;
+    escCode = m_BinDecoder.decodeBinEP();
+    cu.useEscape[compBegin] = (escCode != 0);
+  }
+  uint32_t    indexMaxSize = cu.useEscape[compBegin] ? (cu.curPLTSize[compBegin] + 1) : cu.curPLTSize[compBegin];
+  //encode index map
+  PLTtypeBuf  runType = tu.getrunType(compBegin);
+  PelBuf      runLength = tu.getrunLength(compBegin);
+  PelBuf      curPLTIdx = tu.getcurPLTIdx(compBegin);
+  uint32_t    height = cu.block(compBegin).height;
+  uint32_t    width = cu.block(compBegin).width;
+
+  int       numCopyIndexRuns = -1;
+  bool      lastRunType = 0;
+  uint32_t  numIndices = 0;
+  uint32_t  adjust = 0;
+  uint32_t  symbol = 0;
+  std::list<int> parsedIdxList;
+  if (indexMaxSize > 1)
+  {
+    uint32_t currParam = 3 + ((indexMaxSize) >> 3);
+    numIndices = m_BinDecoder.decodeRemAbsEP(currParam, false, MAX_NUM_CHANNEL_TYPE); // JC: number of indices (INDEX RUN)
+    numIndices++;
+    numCopyIndexRuns = numIndices;
+    while (numIndices--)
+    {
+      xReadTruncBinCode(symbol, indexMaxSize - adjust);
+      adjust = 1;
+      parsedIdxList.push_back(symbol);
+    }
+    lastRunType = m_BinDecoder.decodeBin(Ctx::RunTypeFlag());
+    parseScanRotationModeFlag(cu, compBegin);
+    adjust = 0;
+  }
+  else
+  {
+    cu.useRotation[compBegin] = false;
+  }
+
+  if (cu.useEscape[compBegin] && cu.cs->pps->getUseDQP() && !cuCtx.isDQPCoded)
+  {
+    if (!CS::isDualITree(*tu.cs) || isLuma(tu.chType))
+    {
+      cu_qp_delta(cu, cuCtx.qp, cu.qp);
+      cuCtx.qp = cu.qp;
+      cuCtx.isDQPCoded = true;
+    }
+  }
+#if JVET_O1168_CU_CHROMA_QP_OFFSET
+  if (cu.useEscape[compBegin] && cu.cs->slice->getUseChromaQpAdj() && !cuCtx.isChromaQpAdjCoded)
+#else
+  if (cu.cs->slice->getUseChromaQpAdj() && !cu.transQuantBypass && !cuCtx.isChromaQpAdjCoded)
+#endif
+  {
+    if (!CS::isDualITree(*tu.cs) || isChroma(tu.chType))
+    {
+      cu_chroma_qp_offset(cu);
+      cuCtx.isChromaQpAdjCoded = true;
+    }
+  }
+
+
+  m_scanOrder = g_scanOrder[SCAN_UNGROUPED][(cu.useRotation[compBegin]) ? SCAN_TRAV_VER : SCAN_TRAV_HOR][gp_sizeIdxInfo->idxFrom(width)][gp_sizeIdxInfo->idxFrom(height)];
+  uint32_t strPos = 0;
+  uint32_t endPos = height * width;
+  while (strPos < endPos)
+  {
+    uint32_t posy = m_scanOrder[strPos].y;
+    uint32_t posx = m_scanOrder[strPos].x;
+    uint32_t posyprev = strPos == 0 ? 0 : m_scanOrder[strPos - 1].y;
+    uint32_t posxprev = strPos == 0 ? 0 : m_scanOrder[strPos - 1].x;
+
+    if (indexMaxSize > 1)
+    {
+      if (((posy == 0) && !cu.useRotation[compBegin]) || ((posx == 0) && cu.useRotation[compBegin]))
+      {
+        runType.at(posx, posy) = PLT_RUN_INDEX;
+      }
+      else if (strPos != 0 && runType.at(posxprev, posyprev) == PLT_RUN_COPY)
+      {
+        runType.at(posx, posy) = PLT_RUN_INDEX;
+      }
+      else
+      {
+        if (numCopyIndexRuns && strPos < endPos - 1) // JC: if numIndices (decoder will know this value) == 0 - > only CopyAbove, if strPos == endPos - 1, the last RunType was already coded
+        {
+          runType.at(posx, posy) = (m_BinDecoder.decodeBin(Ctx::RunTypeFlag()));
+        }
+        else
+        {
+          if (strPos == endPos - 1 && numCopyIndexRuns)
+          {
+            runType.at(posx, posy) = PLT_RUN_INDEX;
+          }
+          else
+          {
+            runType.at(posx, posy) = PLT_RUN_COPY;
+          }
+        }
+      }
+    }
+    else
+    {
+      runType.at(posx, posy) = PLT_RUN_INDEX;
+    }
+
+    Pel curLevel = 0;
+    if (runType.at(posx, posy) == PLT_RUN_INDEX)
+    {
+      if (!parsedIdxList.empty())
+      {
+        curLevel = parsedIdxList.front();
+        parsedIdxList.pop_front();
+      }
+      else
+      {
+        curLevel = 0;
+      }
+      xAdjustPLTIndex(cu, curLevel, strPos, curPLTIdx, runType, indexMaxSize, compBegin);
+    }
+
+    if (indexMaxSize > 1)
+    {
+      bool lastRun;
+      numCopyIndexRuns -= (runType.at(posx, posy) == PLT_RUN_INDEX);
+      lastRun = numCopyIndexRuns == 0 && runType.at(posx, posy) == lastRunType;
+      if (!lastRun)
+      {
+        runLength.at(posx, posy) = cu_run_val((PLTRunMode)runType.at(posx, posy), curLevel, endPos - strPos - numCopyIndexRuns - 1 - lastRunType) + 1;
+      }
+      else
+      {
+        runLength.at(posx, posy) = endPos - strPos;
+      }
+
+    }
+    else
+    {
+      runLength.at(posx, posy) = endPos - strPos;
+    }
+
+    //assign run information
+    for (int runidx = 1; runidx < runLength.at(posx, posy); runidx++)
+    {
+      int posYrun, posXrun;
+      posYrun = m_scanOrder[strPos + runidx].y;
+      posXrun = m_scanOrder[strPos + runidx].x;
+      runType.at(posXrun, posYrun) = runType.at(posx, posy);
+      runLength.at(posXrun, posYrun) = runLength.at(posx, posy);
+    }
+
+    uint32_t posYrun;
+    uint32_t posXrun;
+    if (runType.at(posx, posy) == PLT_RUN_INDEX)
+    {
+      for (uint32_t idx = 1; idx < runLength.at(posx, posy); idx++)
+      {
+        posYrun = m_scanOrder[strPos + idx].y;
+        posXrun = m_scanOrder[strPos + idx].x;
+        curPLTIdx.at(posXrun, posYrun) = curPLTIdx.at(posx, posy);
+      }
+    }
+    else if (runType.at(posx, posy) == PLT_RUN_COPY)
+    {
+      for (uint32_t idx = 0; idx < runLength.at(posx, posy); idx++)
+      {
+        posYrun = m_scanOrder[strPos + idx].y;
+        posXrun = m_scanOrder[strPos + idx].x;
+        curPLTIdx.at(posXrun, posYrun) = (cu.useRotation[compBegin]) ? curPLTIdx.at(posXrun - 1, posYrun) : curPLTIdx.at(posXrun, posYrun - 1);
+      }
+    }
+    strPos += (runLength.at(posx, posy));
+  }
+  assert(strPos == endPos);
+
+  uint32_t scaleX = getComponentScaleX(COMPONENT_Cb, sps.getChromaFormatIdc());
+  uint32_t scaleY = getComponentScaleY(COMPONENT_Cb, sps.getChromaFormatIdc());
+  for (int comp = compBegin; comp < (compBegin + numComp); comp++)
+  {
+    ComponentID compID = (ComponentID)comp;
+    for (strPos = 0; strPos < endPos; strPos++)
+    {
+      uint32_t posy = m_scanOrder[strPos].y;
+      uint32_t posx = m_scanOrder[strPos].x;
+      if (curPLTIdx.at(posx, posy) == cu.curPLTSize[compBegin])
+      {
+        {
+          PLTescapeBuf    escapeValue = tu.getescapeValue((ComponentID)comp);
+          if (compID == COMPONENT_Y || compBegin != COMPONENT_Y)
+          {
+            escapeValue.at(posx, posy) = exp_golomb_eqprob(3);
+            assert(escapeValue.at(posx, posy) < (1 << (cu.cs->sps->getBitDepth(toChannelType((ComponentID)comp)) + 1)));
+          }
+          if (compBegin == COMPONENT_Y && compID != COMPONENT_Y && posy % (1 << scaleY) == 0 && posx % (1 << scaleX) == 0)
+          {
+            uint32_t posxC = posx >> scaleX;
+            uint32_t posyC = posy >> scaleY;
+            escapeValue.at(posxC, posyC) = exp_golomb_eqprob(3);
+            assert(escapeValue.at(posxC, posyC) < (1 << (cu.cs->sps->getBitDepth(toChannelType(compID)) + 1)));
+          }
+        }
+      }
+    }
+  }
+
+}
+void CABACReader::parseScanRotationModeFlag(CodingUnit& cu, ComponentID compBegin)
+{
+  cu.useRotation[compBegin] = m_BinDecoder.decodeBin(Ctx::RotationFlag());
+}
+void CABACReader::xDecodePLTPredIndicator(CodingUnit& cu, uint32_t maxPLTSize, ComponentID compBegin)
+{
+  uint32_t symbol, numPltPredicted = 0, idx = 0;
+
+  symbol = exp_golomb_eqprob(0);
+
+  if (symbol != 1)
+  {
+    while (idx < cu.lastPLTSize[compBegin] && numPltPredicted < maxPLTSize)
+    {
+      if (idx > 0)
+      {
+        symbol = exp_golomb_eqprob(0);
+      }
+      if (symbol == 1)
+      {
+        break;
+      }
+
+      if (symbol)
+      {
+        idx += symbol - 1;
+      }
+      cu.reuseflag[compBegin][idx] = 1;
+      numPltPredicted++;
+      idx++;
+    }
+  }
+}
+void CABACReader::xAdjustPLTIndex(CodingUnit& cu, Pel curLevel, uint32_t idx, PelBuf& paletteIdx, PLTtypeBuf& paletteRunType, int maxSymbol, ComponentID compBegin)
+{
+  uint32_t symbol;
+  int refLevel = MAX_INT;
+  uint32_t posy = m_scanOrder[idx].y;
+  uint32_t posx = m_scanOrder[idx].x;
+  if (idx)
+  {
+    uint32_t prevposy = m_scanOrder[idx - 1].y;
+    uint32_t prevposx = m_scanOrder[idx - 1].x;
+    if (paletteRunType.at(prevposx, prevposy) == PLT_RUN_INDEX)
+    {
+      refLevel = paletteIdx.at(prevposx, prevposy);
+      if (paletteIdx.at(prevposx, prevposy) == cu.curPLTSize[compBegin]) // escape
+      {
+        refLevel = maxSymbol - 1;
+      }
+    }
+    else
+    {
+      if (cu.useRotation[compBegin])
+      {
+        assert(prevposx > 0);
+        refLevel = paletteIdx.at(posx - 1, posy);
+        if (paletteIdx.at(posx - 1, posy) == cu.curPLTSize[compBegin]) // escape mode
+        {
+          refLevel = maxSymbol - 1;
+        }
+      }
+      else
+      {
+        assert(prevposy > 0);
+        refLevel = paletteIdx.at(posx, posy - 1);
+        if (paletteIdx.at(posx, posy - 1) == cu.curPLTSize[compBegin]) // escape mode
+        {
+          refLevel = maxSymbol - 1;
+        }
+      }
+    }
+    maxSymbol--;
+  }
+  symbol = curLevel;
+  if (curLevel >= refLevel) // include escape mode 
+  {
+    symbol++;
+  }
+  paletteIdx.at(posx, posy) = symbol;
+}
+uint32_t  CABACReader::cu_run_val(PLTRunMode runtype, const uint32_t paletteIdx, const uint32_t maxRun)
+{
+  uint32_t symbol = 0;
+  if (runtype == PLT_RUN_COPY)
+  {
+  }
+  else
+  {
+    g_paletteRunLeftLut[0] = (paletteIdx < PLT_RUN_MSB_IDX_CTX_T1 ? 0 : (paletteIdx < PLT_RUN_MSB_IDX_CTX_T2 ? 1 : 2));
+  }
+  symbol = xReadTruncMsbP1RefinementBits(runtype, maxRun, PLT_RUN_MSB_IDX_CABAC_BYPASS_THRE);
+  return symbol;
+}
+uint32_t CABACReader::xReadTruncUnarySymbol(PLTRunMode runtype, uint32_t maxVal, uint32_t ctxT)
+{
+  if (maxVal == 0)
+    return 0;
+
+  uint8_t *ctxLut;
+  ctxLut = (runtype == PLT_RUN_INDEX) ? g_paletteRunLeftLut : g_paletteRunTopLut;
+  uint32_t bin, idx = 0;
+  do
+  {
+    if (idx > ctxT)
+      bin = m_BinDecoder.decodeBinEP();
+    else
+    {
+      bin = m_BinDecoder.decodeBin(
+        (idx <= ctxT)
+        ? ((runtype == PLT_RUN_INDEX) ? Ctx::IdxRunModel(ctxLut[idx]) : Ctx::CopyRunModel(ctxLut[idx]))
+        : ((runtype == PLT_RUN_INDEX) ? Ctx::IdxRunModel(ctxLut[ctxT]) : Ctx::CopyRunModel(ctxLut[ctxT])));
+      //        idx <= ctxT? pcSCModel[ctxLut[idx]] : pcSCModel[ctxLut[ctxT]] RExt__DECODER_DEBUG_BIT_STATISTICS_PASS_OPT_ARG(whichStat) );
+    }
+    idx++;
+  } while (bin && idx < maxVal);
+
+  return (bin && idx == maxVal) ? maxVal : idx - 1;
+}
+uint32_t CABACReader::xReadTruncMsbP1RefinementBits(PLTRunMode runtype, uint32_t maxVal, uint32_t ctxT)
+{
+  if (maxVal == 0)
+  {
+    return 0;
+  }
+  uint32_t symbol;
+  uint32_t msbP1 = xReadTruncUnarySymbol(runtype, floorLog2(maxVal) + 1, ctxT);
+  if (msbP1 > 1)
+  {
+    uint32_t numBins = floorLog2(maxVal) + 1;
+    if (msbP1 < numBins)
+    {
+      uint32_t bits = msbP1 - 1;
+      symbol = m_BinDecoder.decodeBinsEP(bits);
+      symbol |= (1 << bits);
+    }
+    else
+    {
+      uint32_t curValue = 1 << (numBins - 1);
+      xReadTruncBinCode(symbol, maxVal + 1 - curValue);
+      symbol += curValue;
+    }
+  }
+  else
+    symbol = msbP1;
+
+  return symbol;
+}
+#endif
+
 //================================================================================
 //  clause 7.3.8.6
 //--------------------------------------------------------------------------------
diff --git a/source/Lib/DecoderLib/CABACReader.h b/source/Lib/DecoderLib/CABACReader.h
index c6e2afa9040b42f2e0081123e512cdfa708cdf53..2908269819ab33a859d18319f17b9157425e9a93 100644
--- a/source/Lib/DecoderLib/CABACReader.h
+++ b/source/Lib/DecoderLib/CABACReader.h
@@ -95,6 +95,9 @@ public:
   void        mip_flag                  ( CodingUnit&                   cu );
   void        mip_pred_modes            ( CodingUnit&                   cu );
   void        mip_pred_mode             ( PredictionUnit&               pu );
+#if JVET_O0119_BASE_PALETTE_444  
+  void        cu_palette_info           ( CodingUnit& cu, ComponentID compBegin, uint32_t numComp, CUCtx& cuCtx);
+#endif
 
   // prediction unit (clause 7.3.8.6)
   void        prediction_unit           ( PredictionUnit&               pu,     MergeCtx&       mrgCtx );
@@ -171,6 +174,14 @@ private:
   unsigned    code_unary_fixed          ( unsigned ctxId, unsigned unary_max, unsigned fixed );
 
   void        xReadTruncBinCode(uint32_t& symbol, uint32_t maxSymbol);
+#if JVET_O0119_BASE_PALETTE_444
+  void        parseScanRotationModeFlag ( CodingUnit& cu, ComponentID compBegin);
+  void        xDecodePLTPredIndicator   ( CodingUnit& cu, uint32_t maxPLTSize, ComponentID compBegin);
+  void        xAdjustPLTIndex           ( CodingUnit& cu, Pel curLevel, uint32_t idx, PelBuf& paletteIdx, PLTtypeBuf& paletteRunType, int maxSymbol, ComponentID compBegin);
+  uint32_t    cu_run_val                ( PLTRunMode runtype, const uint32_t pltIdx, const uint32_t maxRun);
+  uint32_t    xReadTruncUnarySymbol     ( PLTRunMode runtype, uint32_t maxVal, uint32_t ctxT);
+  uint32_t    xReadTruncMsbP1RefinementBits( PLTRunMode runtype, uint32_t maxVal, uint32_t ctxT);
+#endif
 public:
   int         shareStateDec;
   Position    shareParentPos;
@@ -178,6 +189,9 @@ public:
 private:
   BinDecoderBase& m_BinDecoder;
   InputBitstream* m_Bitstream;
+#if JVET_O0119_BASE_PALETTE_444
+  ScanElement*    m_scanOrder;
+#endif
 };
 
 
diff --git a/source/Lib/DecoderLib/DecCu.cpp b/source/Lib/DecoderLib/DecCu.cpp
index 998eb33946df0adef109be77c15c01456c404a1a..5ae1d31d1cf367047c219a18196c530ee65e3b06 100644
--- a/source/Lib/DecoderLib/DecCu.cpp
+++ b/source/Lib/DecoderLib/DecCu.cpp
@@ -150,7 +150,11 @@ void DecCu::decompressCtu( CodingStructure& cs, const UnitArea& ctuArea )
         }
         prevTmpPos = currCU.shareParentPos;
       }
+#if JVET_O0119_BASE_PALETTE_444
+      if (currCU.predMode != MODE_INTRA && currCU.predMode != MODE_PLT && currCU.Y().valid())
+#else
       if (currCU.predMode != MODE_INTRA && currCU.Y().valid())
+#endif
       {
         xDeriveCUMV(currCU);
       }
@@ -160,6 +164,9 @@ void DecCu::decompressCtu( CodingStructure& cs, const UnitArea& ctuArea )
       case MODE_IBC:
         xReconInter( currCU );
         break;
+#if JVET_O0119_BASE_PALETTE_444
+      case MODE_PLT:
+#endif
       case MODE_INTRA:
         xReconIntraQT( currCU );
         break;
@@ -414,6 +421,27 @@ void DecCu::xReconIntraQT( CodingUnit &cu )
     return;
   }
 
+#if JVET_O0119_BASE_PALETTE_444
+  if (CU::isPLT(cu))
+  {
+    if (CS::isDualITree(*cu.cs))
+    {
+      if (cu.chType == CHANNEL_TYPE_LUMA)
+      {
+        xReconPLT(cu, COMPONENT_Y, 1);
+      }
+      if (cu.chromaFormat != CHROMA_400 && (cu.chType == CHANNEL_TYPE_CHROMA))
+      {
+        xReconPLT(cu, COMPONENT_Cb, 2);
+      }
+    }
+    else
+    {
+      xReconPLT(cu, COMPONENT_Y, 3);
+    }
+    return;
+  }
+#endif
   const uint32_t numChType = ::getNumberValidChannels( cu.chromaFormat );
 
   for( uint32_t chType = CHANNEL_TYPE_LUMA; chType < numChType; chType++ )
@@ -425,6 +453,88 @@ void DecCu::xReconIntraQT( CodingUnit &cu )
   }
 }
 
+#if JVET_O0119_BASE_PALETTE_444
+void DecCu::xReconPLT(CodingUnit &cu, ComponentID compBegin, uint32_t numComp)
+{
+  const SPS&       sps = *(cu.cs->sps);
+  TransformUnit&   tu = *cu.firstTU;
+  PelBuf    curPLTIdx = tu.getcurPLTIdx(compBegin);
+
+  uint32_t height = cu.block(compBegin).height;
+  uint32_t width = cu.block(compBegin).width;
+
+  //recon. pixels
+  uint32_t scaleX = getComponentScaleX(COMPONENT_Cb, sps.getChromaFormatIdc());
+  uint32_t scaleY = getComponentScaleY(COMPONENT_Cb, sps.getChromaFormatIdc());
+  for (uint32_t y = 0; y < height; y++)
+  {
+    for (uint32_t x = 0; x < width; x++)
+    {
+      for (uint32_t compID = compBegin; compID < (compBegin + numComp); compID++)
+      {
+        const int  channelBitDepth = cu.cs->sps->getBitDepth(toChannelType((ComponentID)compID));
+        const CompArea &area = cu.blocks[compID];
+
+        PelBuf       picReco   = cu.cs->getRecoBuf(area);
+        PLTescapeBuf escapeValue = tu.getescapeValue((ComponentID)compID);
+        if (curPLTIdx.at(x, y) == cu.curPLTSize[compBegin])
+        {
+          Pel value;
+          QpParam cQP(tu, (ComponentID)compID);
+
+#if JVET_O0919_TS_MIN_QP
+          int qp = cQP.Qp(false);
+#else
+          int qp = cQP.Qp;
+#endif
+          int qpRem = qp % 6;
+          int qpPer = qp / 6;
+          if (compBegin != COMPONENT_Y || compID == COMPONENT_Y)
+          {
+            int invquantiserRightShift = IQUANT_SHIFT;
+            int add = 1 << (invquantiserRightShift - 1);
+            value = ((((escapeValue.at(x, y)*g_invQuantScales[0][qpRem]) << qpPer) + add) >> invquantiserRightShift);
+            value = Pel(ClipBD<int>(value, channelBitDepth));
+            picReco.at(x, y) = value;
+          }
+          else if (compBegin == COMPONENT_Y && compID != COMPONENT_Y && y % (1 << scaleY) == 0 && x % (1 << scaleX) == 0)
+          {
+            uint32_t posYC = y >> scaleY;
+            uint32_t posXC = x >> scaleX;
+            int invquantiserRightShift = IQUANT_SHIFT;
+            int add = 1 << (invquantiserRightShift - 1);
+            value = ((((escapeValue.at(posXC, posYC)*g_invQuantScales[0][qpRem]) << qpPer) + add) >> invquantiserRightShift);
+            value = Pel(ClipBD<int>(value, channelBitDepth));
+            picReco.at(posXC, posYC) = value;
+
+          }
+        }
+        else
+        {
+          uint32_t curIdx = curPLTIdx.at(x, y);
+          if (compBegin != COMPONENT_Y || compID == COMPONENT_Y)
+          {
+            picReco.at(x, y) = cu.curPLT[compID][curIdx];
+          }
+          else if (compBegin == COMPONENT_Y && compID != COMPONENT_Y && y % (1 << scaleY) == 0 && x % (1 << scaleX) == 0)
+          {
+            uint32_t posYC = y >> scaleY;
+            uint32_t posXC = x >> scaleX;
+            picReco.at(posXC, posYC) = cu.curPLT[compID][curIdx];
+          }
+        }
+      }
+    }
+  }
+  for (uint32_t compID = compBegin; compID < (compBegin + numComp); compID++)
+  {
+    const CompArea &area = cu.blocks[compID];
+    PelBuf picReco = cu.cs->getRecoBuf(area);
+    cu.cs->picture->getRecoBuf(area).copyFrom(picReco);
+    cu.cs->setDecomp(area);
+  }
+}
+#endif
 /** Function for deriving reconstructed luma/chroma samples of a PCM mode CU.
 * \param pcCU pointer to current CU
 * \param uiPartIdx part index
diff --git a/source/Lib/DecoderLib/DecCu.h b/source/Lib/DecoderLib/DecCu.h
index 8d70b5f06275284f8d0ba6b91b8726e262c17fbf..06b3aab0b12c9ab7621f5949f775a848ebda1dd1 100644
--- a/source/Lib/DecoderLib/DecCu.h
+++ b/source/Lib/DecoderLib/DecCu.h
@@ -92,6 +92,9 @@ protected:
   void xDecodeInterTU     ( TransformUnit&   tu, const ComponentID compID );
 
   void xDeriveCUMV        ( CodingUnit&      cu );
+#if JVET_O0119_BASE_PALETTE_444
+  void xReconPLT          ( CodingUnit &cu, ComponentID compBegin, uint32_t numComp);
+#endif
   PelStorage        *m_tmpStorageLCU;
 private:
   TrQuant*          m_pcTrQuant;
diff --git a/source/Lib/DecoderLib/DecSlice.cpp b/source/Lib/DecoderLib/DecSlice.cpp
index 2c6b68b2e4e351f5ff0af7ac971636dab5bdd45d..42b66acdaef5f78b5aca6c35bfe210f8a1bd60ce 100644
--- a/source/Lib/DecoderLib/DecSlice.cpp
+++ b/source/Lib/DecoderLib/DecSlice.cpp
@@ -94,6 +94,10 @@ void DecSlice::decompressSlice( Slice* slice, InputBitstream* bitstream, int deb
 
   cs.picture->resizeSAO(cs.pcv->sizeInCtus, 0);
 
+#if JVET_O0119_BASE_PALETTE_444
+  cs.resetPrevPLT(cs.prevPLT);
+#endif
+
   if (slice->getSliceCurStartCtuTsAddr() == 0)
   {
     cs.picture->resizeAlfCtuEnableFlag( cs.pcv->sizeInCtus );
@@ -163,6 +167,9 @@ void DecSlice::decompressSlice( Slice* slice, InputBitstream* bitstream, int deb
       if( ctuTsAddr != startCtuTsAddr ) // if it is the first CTU, then the entropy coder has already been reset
       {
         cabacReader.initCtxModels( *slice );
+#if JVET_O0119_BASE_PALETTE_444
+        cs.resetPrevPLT(cs.prevPLT);
+#endif
       }
       pic->m_prevQP[0] = pic->m_prevQP[1] = slice->getSliceQp();
     }
@@ -172,6 +179,9 @@ void DecSlice::decompressSlice( Slice* slice, InputBitstream* bitstream, int deb
       if( ctuTsAddr != startCtuTsAddr ) // if it is the first CTU, then the entropy coder has already been reset
       {
         cabacReader.initCtxModels( *slice );
+#if JVET_O0119_BASE_PALETTE_444
+        cs.resetPrevPLT(cs.prevPLT);
+#endif
       }
       if( cs.getCURestricted( pos.offset(0, -1), pos, slice->getIndependentSliceIdx(), tileMap.getBrickIdxRsMap( pos ), CH_L ) )
       {
diff --git a/source/Lib/DecoderLib/VLCReader.cpp b/source/Lib/DecoderLib/VLCReader.cpp
index 3cc22c22b1f37e707a0c6c275ce6fda6a79cb6dd..7e5b0c48a79db3abc46c7e3c8786565bc02bb004 100644
--- a/source/Lib/DecoderLib/VLCReader.cpp
+++ b/source/Lib/DecoderLib/VLCReader.cpp
@@ -1353,6 +1353,16 @@ void HLSyntaxReader::parseSPS(SPS* pcSPS)
 #endif
   }
   READ_FLAG( uiCode,    "gbi_flag" );                               pcSPS->setUseGBi                 ( uiCode != 0 );
+#if JVET_O0119_BASE_PALETTE_444
+  if (pcSPS->getChromaFormatIdc() == CHROMA_444)
+  {
+    READ_FLAG( uiCode,  "plt_flag");                                pcSPS->setPLTMode                ( uiCode != 0 );
+  }
+  else
+  {
+    pcSPS->setPLTMode(false);
+  }
+#endif
   READ_FLAG(uiCode, "ibc_flag");                                    pcSPS->setIBCFlag(uiCode);
   // KJS: sps_ciip_enabled_flag
   READ_FLAG( uiCode,     "mhintra_flag" );                           pcSPS->setUseMHIntra             ( uiCode != 0 );
diff --git a/source/Lib/EncoderLib/CABACWriter.cpp b/source/Lib/EncoderLib/CABACWriter.cpp
index ca67320344946cf1f1c15df4ffddb412b81aa9a9..d6b0d3ad2cf356cb0f4350f59dec0012b92f81a9 100755
--- a/source/Lib/EncoderLib/CABACWriter.cpp
+++ b/source/Lib/EncoderLib/CABACWriter.cpp
@@ -612,6 +612,28 @@ void CABACWriter::coding_unit( const CodingUnit& cu, Partitioner& partitioner, C
 
   // prediction mode and partitioning data
   pred_mode ( cu );
+#if JVET_O0119_BASE_PALETTE_444
+  if (CU::isPLT(cu))
+  {
+    if (CS::isDualITree(*cu.cs))
+    {
+      if (isLuma(partitioner.chType))
+      {
+        cu_palette_info(cu, COMPONENT_Y, 1, cuCtx);
+      }
+      if (cu.chromaFormat != CHROMA_400 && (partitioner.chType == CHANNEL_TYPE_CHROMA))
+      {
+        cu_palette_info(cu, COMPONENT_Cb, 2, cuCtx);
+      }
+    }
+    else
+    {
+      cu_palette_info(cu, COMPONENT_Y, 3, cuCtx);
+    }
+    end_of_ctu(cu, cuCtx);
+    return;
+  }
+#endif
   bdpcm_mode( cu, ComponentID( partitioner.chType ) );
 
 #if FIX_PCM
@@ -761,12 +783,29 @@ void CABACWriter::pred_mode( const CodingUnit& cu )
       unsigned ctxidx = DeriveCtx::CtxIBCFlag(cu);
       m_BinEncoder.encodeBin(CU::isIBC(cu), Ctx::IBCFlag(ctxidx));
       }
+#if JVET_O0119_BASE_PALETTE_444
+    if (!CU::isIBC(cu) && cu.cs->slice->getSPS()->getPLTMode() && cu.lwidth() <= 64 && cu.lheight() <= 64)
+    {
+      m_BinEncoder.encodeBin(CU::isPLT(cu), Ctx::PLTFlag(0));
+    }
+#endif
+    }
+    else
+    {
+#if JVET_O0119_BASE_PALETTE_444
+    m_BinEncoder.encodeBin((CU::isIntra(cu) || CU::isPLT(cu)), Ctx::PredMode(DeriveCtx::CtxPredModeFlag(cu)));
+    if (CU::isIntra(cu) || CU::isPLT(cu))
+    {
+      if (cu.cs->slice->getSPS()->getPLTMode() && cu.lwidth() <= 64 && cu.lheight() <= 64)
+      m_BinEncoder.encodeBin(CU::isPLT(cu), Ctx::PLTFlag(0));
     }
     else
     {
+#else
       m_BinEncoder.encodeBin((CU::isIntra(cu)), Ctx::PredMode(DeriveCtx::CtxPredModeFlag(cu)));
       if (!CU::isIntra(cu))
       {
+#endif
 #if JVET_O1161_IBC_MAX_SIZE
         if (cu.lwidth() < 128 && cu.lheight() < 128) // disable IBC mode larger than 64x64
 #else
@@ -783,9 +822,19 @@ void CABACWriter::pred_mode( const CodingUnit& cu )
   {
     if ( cu.cs->slice->isIntra() || ( cu.lwidth() == 4 && cu.lheight() == 4 ) )
     {
+#if JVET_O0119_BASE_PALETTE_444
+    if (cu.cs->slice->getSPS()->getPLTMode() && cu.lwidth() <= 64 && cu.lheight() <= 64)
+      m_BinEncoder.encodeBin((CU::isPLT(cu)), Ctx::PLTFlag(0));
+#endif
       return;
     }
     m_BinEncoder.encodeBin((CU::isIntra(cu)), Ctx::PredMode(DeriveCtx::CtxPredModeFlag(cu)));
+#if JVET_O0119_BASE_PALETTE_444
+  if (!CU::isIntra(cu) && cu.cs->slice->getSPS()->getPLTMode() && cu.lwidth() <= 64 && cu.lheight() <= 64)
+  {
+    m_BinEncoder.encodeBin((CU::isPLT(cu)), Ctx::PLTFlag(0));
+  }
+#endif
   }
 }
 void CABACWriter::bdpcm_mode( const CodingUnit& cu, const ComponentID compID )
@@ -1500,9 +1549,400 @@ void CABACWriter::end_of_ctu( const CodingUnit& cu, CUCtx& cuCtx )
   }
 }
 
+#if JVET_O0119_BASE_PALETTE_444
+void CABACWriter::cu_palette_info(const CodingUnit& cu, ComponentID compBegin, uint32_t numComp, CUCtx& cuCtx)
+{
+  const SPS&       sps = *(cu.cs->sps);
+  TransformUnit&   tu = *cu.firstTU;
+  uint32_t indexMaxSize = cu.useEscape[compBegin] ? (cu.curPLTSize[compBegin] + 1) : cu.curPLTSize[compBegin];
 
+  if (cu.lastPLTSize[compBegin])
+  {
+    xEncodePLTPredIndicator(cu, MAXPLTSIZE, compBegin);
+  }
 
+  uint32_t reusedPLTnum = 0;
+  for (int idx = 0; idx < cu.lastPLTSize[compBegin]; idx++)
+  {
+    if (cu.reuseflag[compBegin][idx])
+      reusedPLTnum++;
+  }
 
+  if (reusedPLTnum < MAXPLTSIZE)
+  {
+    exp_golomb_eqprob(cu.curPLTSize[compBegin] - reusedPLTnum, 0);
+  }
+
+  for (int comp = compBegin; comp < (compBegin + numComp); comp++)
+  {
+    for (int idx = cu.reusePLTSize[compBegin]; idx < cu.curPLTSize[compBegin]; idx++)
+    {
+      ComponentID compID = (ComponentID)comp;
+      const int  channelBitDepth = sps.getBitDepth(toChannelType(compID));
+      m_BinEncoder.encodeBinsEP(cu.curPLT[comp][idx], channelBitDepth);
+    }
+  }
+  uint32_t signalEscape = (cu.useEscape[compBegin]) ? 1 : 0;
+  if (cu.curPLTSize[compBegin] > 0)
+  {
+    m_BinEncoder.encodeBinEP(signalEscape);
+  }
+  //encode index map
+  PLTtypeBuf runType = tu.getrunType(compBegin);
+  PelBuf     runLength = tu.getrunLength(compBegin);
+  PelBuf     curPLTIdx = tu.getcurPLTIdx(compBegin);
+  uint32_t   height = cu.block(compBegin).height;
+  uint32_t   width = cu.block(compBegin).width;
+
+  m_scanOrder = g_scanOrder[SCAN_UNGROUPED][(cu.useRotation[compBegin]) ? SCAN_TRAV_VER : SCAN_TRAV_HOR][gp_sizeIdxInfo->idxFrom(width)][gp_sizeIdxInfo->idxFrom(height)];
+  uint32_t total = height * width;
+  int lastRunPos = -1;
+  uint32_t lastRunType = 0;
+  uint32_t numIndices = 0;
+  std::vector<int> idxPos, parsedIdx;
+  idxPos.reserve(total);
+  parsedIdx.reserve(total);
+  if (indexMaxSize > 1)
+  {
+    int idx = 0, run = 0;
+    while (idx < total)
+    {
+      uint32_t posy = m_scanOrder[idx].y;
+      uint32_t posx = m_scanOrder[idx].x;
+      if (runType.at(posx, posy) == PLT_RUN_INDEX)
+      {
+        idxPos.push_back(idx);
+        numIndices++;
+      }
+      lastRunType = runType.at(posx, posy);
+      lastRunPos = idx;
+      run = runLength.at(posx, posy);
+      idx += run;
+    }
+    uint32_t currParam = 3 + ((indexMaxSize) >> 3);
+    uint32_t mappedValue;
+    assert(numIndices);
+    assert(numIndices > 0);
+    mappedValue = numIndices - 1;
+    m_BinEncoder.encodeRemAbsEP(mappedValue, currParam, false, MAX_NUM_CHANNEL_TYPE); // JC: code number of indices (PLT_RUN_INDEX)
+    auto idxPosEnd = idxPos.end();
+    for (auto iter = idxPos.begin(); iter != idxPosEnd; ++iter)
+    {
+      parsedIdx.push_back( writePLTIndex(cu, *iter, curPLTIdx, runType, indexMaxSize, compBegin));
+    }
+    m_BinEncoder.encodeBin(lastRunType, Ctx::RunTypeFlag());
+    codeScanRotationModeFlag(cu, compBegin);
+  }
+  else
+  {
+    assert(!cu.useRotation[compBegin]);
+  }
+
+  if (cu.useEscape[compBegin] && cu.cs->pps->getUseDQP() && !cuCtx.isDQPCoded)
+  {
+    if (!CS::isDualITree(*tu.cs) || isLuma(tu.chType))
+    {
+      cu_qp_delta(cu, cuCtx.qp, cu.qp);
+      cuCtx.qp = cu.qp;
+      cuCtx.isDQPCoded = true;
+    }
+  }
+#if JVET_O1168_CU_CHROMA_QP_OFFSET
+  if ( cu.useEscape[compBegin] && cu.cs->slice->getUseChromaQpAdj() && !cuCtx.isChromaQpAdjCoded)
+#else
+  if (cu.cs->slice->getUseChromaQpAdj() && !cu.transQuantBypass && !cuCtx.isChromaQpAdjCoded)
+#endif
+  {
+    if (!CS::isDualITree(*tu.cs) || isChroma(tu.chType))
+    {
+      cu_chroma_qp_offset(cu);
+      cuCtx.isChromaQpAdjCoded = true;
+    }
+  }
+
+  uint32_t strPos = 0;
+  uint32_t endPos = height * width;
+  auto parsedIdxEnd = parsedIdx.end();
+  auto parsedIdxIter = parsedIdx.begin();
+  while (strPos < endPos)
+  {
+    uint32_t posy = m_scanOrder[strPos].y;
+    uint32_t posx = m_scanOrder[strPos].x;
+    uint32_t posyprev = strPos == 0 ? 0 : m_scanOrder[strPos - 1].y;
+    uint32_t posxprev = strPos == 0 ? 0 : m_scanOrder[strPos - 1].x;
+
+    if (indexMaxSize > 1)
+    {
+      if (((posy == 0) && !cu.useRotation[compBegin]) || ((posx == 0) && cu.useRotation[compBegin]))
+
+      {
+        assert(runType.at(posx, posy) == PLT_RUN_INDEX);
+      }
+      else if (strPos != 0 && runType.at(posxprev, posyprev) == PLT_RUN_COPY)
+      {
+        assert(runType.at(posx, posy) == PLT_RUN_INDEX);
+      }
+      else
+      {
+        if (numIndices && strPos < endPos - 1) // if numIndices (decoder will know this value) == 0 - > only CopyAbove, if strPos == endPos - 1, the last RunType was already coded
+        {
+          m_BinEncoder.encodeBin((runType.at(posx, posy)), Ctx::RunTypeFlag());
+        }
+      }
+    }
+
+    Pel curLevel = 0;
+    if (runType.at(posx, posy) == PLT_RUN_INDEX)
+    {
+      if (parsedIdxIter != parsedIdxEnd)
+      {
+        curLevel = *parsedIdxIter++;
+      }
+      else
+      {
+        curLevel = 0;
+      }
+    }
+
+    if (indexMaxSize > 1)
+    {
+      if (lastRunPos != strPos)
+      {
+        numIndices -= (runType.at(posx, posy) == PLT_RUN_INDEX);
+        cu_run_val(runLength.at(posx, posy) - 1, (PLTRunMode)runType.at(posx, posy), curLevel, endPos - strPos - numIndices - 1 - lastRunType);
+      }
+
+    }
+
+    strPos += (runLength.at(posx, posy));
+  }
+  assert(strPos == endPos);
+
+  uint32_t scaleX = getComponentScaleX(COMPONENT_Cb, sps.getChromaFormatIdc());
+  uint32_t scaleY = getComponentScaleY(COMPONENT_Cb, sps.getChromaFormatIdc());
+  for (int comp = compBegin; comp < (compBegin + numComp); comp++)
+  {
+    ComponentID compID = (ComponentID)comp;
+    for (strPos = 0; strPos < endPos; strPos++)
+    {
+      uint32_t posy = m_scanOrder[strPos].y;
+      uint32_t posx = m_scanOrder[strPos].x;
+      if (curPLTIdx.at(posx, posy) == cu.curPLTSize[compBegin])
+      {
+        {
+          PLTescapeBuf    escapeValue = tu.getescapeValue((ComponentID)comp);
+          if (compID == COMPONENT_Y || compBegin != COMPONENT_Y)
+          {
+            exp_golomb_eqprob((unsigned)escapeValue.at(posx, posy), 3);
+          }
+          if (compBegin == COMPONENT_Y && compID != COMPONENT_Y && posy % (1 << scaleY) == 0 && posx % (1 << scaleX) == 0)
+          {
+            uint32_t posxC = posx >> scaleX;
+            uint32_t posyC = posy >> scaleY;
+            exp_golomb_eqprob((unsigned)escapeValue.at(posxC, posyC), 3);
+          }
+        }
+      }
+    }
+  }
+
+}
+void CABACWriter::codeScanRotationModeFlag(const CodingUnit& cu, ComponentID compBegin)
+{
+  m_BinEncoder.encodeBin((cu.useRotation[compBegin]), Ctx::RotationFlag());
+}
+void CABACWriter::xEncodePLTPredIndicator(const CodingUnit& cu, uint32_t maxPLTSize, ComponentID compBegin)
+{
+  int lastPredIdx = -1;
+  uint32_t run = 0;
+  uint32_t numPLTPredicted = 0;
+  for (uint32_t idx = 0; idx < cu.lastPLTSize[compBegin]; idx++)
+  {
+    if (cu.reuseflag[compBegin][idx])
+    {
+      numPLTPredicted++;
+      lastPredIdx = idx;
+    }
+  }
+
+  int idx = 0;
+  while (idx <= lastPredIdx)
+  {
+    if (cu.reuseflag[compBegin][idx])
+    {
+      exp_golomb_eqprob(run ? run + 1 : run, 0);
+      run = 0;
+    }
+    else
+    {
+      run++;
+    }
+    idx++;
+  }
+  if ((numPLTPredicted < maxPLTSize && lastPredIdx + 1 < cu.lastPLTSize[compBegin]) || !numPLTPredicted)
+  {
+    exp_golomb_eqprob(1, 0);
+  }
+}
+Pel CABACWriter::writePLTIndex(const CodingUnit& cu, uint32_t idx, PelBuf& paletteIdx, PLTtypeBuf& paletteRunType, int maxSymbol, ComponentID compBegin)
+{
+  uint32_t posy = m_scanOrder[idx].y;
+  uint32_t posx = m_scanOrder[idx].x;
+  Pel curLevel = (paletteIdx.at(posx, posy) == cu.curPLTSize[compBegin]) ? (maxSymbol - 1) : paletteIdx.at(posx, posy);
+  if (idx) // R0348: remove index redundancy
+  {
+    uint32_t prevposy = m_scanOrder[idx - 1].y;
+    uint32_t prevposx = m_scanOrder[idx - 1].x;
+    if (paletteRunType.at(prevposx, prevposy) == PLT_RUN_INDEX)
+    {
+      Pel leftLevel = paletteIdx.at(prevposx, prevposy); // left index
+      if (leftLevel == cu.curPLTSize[compBegin]) // escape mode
+      {
+        leftLevel = maxSymbol - 1;
+      }
+      assert(leftLevel != curLevel);
+      if (curLevel > leftLevel)
+      {
+        curLevel--;
+      }
+    }
+    else
+    {
+      Pel aboveLevel;
+      if (cu.useRotation[compBegin])
+      {
+        assert(prevposx > 0);
+        aboveLevel = paletteIdx.at(posx - 1, posy);
+        if (paletteIdx.at(posx - 1, posy) == cu.curPLTSize[compBegin]) // escape mode
+        {
+          aboveLevel = maxSymbol - 1;
+        }
+      }
+      else
+      {
+        assert(prevposy > 0);
+        aboveLevel = paletteIdx.at(posx, posy - 1);
+        if (paletteIdx.at(posx, posy - 1) == cu.curPLTSize[compBegin]) // escape mode
+        {
+          aboveLevel = maxSymbol - 1;
+        }
+      }
+      assert(curLevel != aboveLevel);
+      if (curLevel > aboveLevel)
+      {
+        curLevel--;
+      }
+    }
+    maxSymbol--;
+  }
+  assert(maxSymbol > 0);
+  assert(curLevel >= 0);
+  assert(maxSymbol > curLevel);
+  if (maxSymbol > 1)
+  {
+    xWriteTruncBinCode(curLevel, maxSymbol);
+  }
+  return curLevel;
+}
+
+void CABACWriter::encodeRunType(const CodingUnit&  cu, PLTtypeBuf& runType, uint32_t idx, ScanElement *refScanOrder, ComponentID compBegin)
+{
+  if (refScanOrder)
+  {
+    m_scanOrder = refScanOrder;
+  }
+  uint32_t posy = m_scanOrder[idx].y;
+  uint32_t posx = m_scanOrder[idx].x;
+  uint32_t posyprev = (idx == 0) ? 0 : m_scanOrder[idx - 1].y;
+  uint32_t posxprev = (idx == 0) ? 0 : m_scanOrder[idx - 1].x;
+
+  if (((posy == 0) && !cu.useRotation[compBegin]) || ((posx == 0) && cu.useRotation[compBegin]))
+  {
+    assert(runType.at(posx, posy) == PLT_RUN_INDEX);
+  }
+  else if (idx != 0 && runType.at(posxprev, posyprev) == PLT_RUN_COPY)
+  {
+    assert(runType.at(posx, posy) == PLT_RUN_INDEX);
+  }
+  else
+  {
+    m_BinEncoder.encodeBin((runType.at(posx, posy)), Ctx::RunTypeFlag());
+  }
+}
+
+void CABACWriter::cu_run_val(uint32_t run, PLTRunMode runtype, const uint32_t paletteIdx, const uint32_t maxRun)
+{
+  if (runtype == PLT_RUN_COPY)
+  {
+  }
+  else
+  {
+    g_paletteRunLeftLut[0] = (paletteIdx < PLT_RUN_MSB_IDX_CTX_T1 ? 0 : (paletteIdx < PLT_RUN_MSB_IDX_CTX_T2 ? 1 : 2));
+  }
+  xWriteTruncMsbP1RefinementBits(run, runtype, maxRun, PLT_RUN_MSB_IDX_CABAC_BYPASS_THRE);
+}
+uint32_t CABACWriter::xWriteTruncMsbP1(uint32_t symbol, PLTRunMode runtype, uint32_t uiMax, uint32_t uiCtxT)
+{
+  if (uiMax == 0)
+    return 0;
+
+  uint8_t *ctxLut;
+  ctxLut = (runtype == PLT_RUN_INDEX) ? g_paletteRunLeftLut : g_paletteRunTopLut;
+
+  uint32_t msbP1;
+  for (msbP1 = 0; symbol > 0; msbP1++)
+  {
+    symbol >>= 1;
+    if (msbP1 > uiCtxT)
+    {
+      m_BinEncoder.encodeBinEP(1);
+    }
+    else
+      m_BinEncoder.encodeBin(1, (msbP1 <= uiCtxT)
+        ? ((runtype == PLT_RUN_INDEX) ? Ctx::IdxRunModel(ctxLut[msbP1]) : Ctx::CopyRunModel(ctxLut[msbP1]))
+        : ((runtype == PLT_RUN_INDEX) ? Ctx::IdxRunModel(ctxLut[uiCtxT]) : Ctx::CopyRunModel(ctxLut[uiCtxT])));
+  }
+  assert(msbP1 <= uiMax);
+  if (msbP1 < uiMax)
+  {
+    if (msbP1 > uiCtxT)
+    {
+      m_BinEncoder.encodeBinEP(0);
+    }
+    else
+      m_BinEncoder.encodeBin(0, msbP1 <= uiCtxT
+        ? ((runtype == PLT_RUN_INDEX) ? Ctx::IdxRunModel(ctxLut[msbP1]) : Ctx::CopyRunModel(ctxLut[msbP1]))
+        : ((runtype == PLT_RUN_INDEX) ? Ctx::IdxRunModel(ctxLut[uiCtxT]) : Ctx::CopyRunModel(ctxLut[uiCtxT])));
+
+    //m_pcBinIf->encodeBin(0, msbP1 <= uiCtxT? pcSCModel[ctxLut[msbP1]] : pcSCModel[ctxLut[uiCtxT]]);
+  }
+  return msbP1;
+}
+
+void CABACWriter::xWriteTruncMsbP1RefinementBits(uint32_t symbol, PLTRunMode runtype, uint32_t maxVal, uint32_t uiCtxT)
+{
+  if (maxVal == 0)
+    return;
+
+  uint32_t msbP1 = xWriteTruncMsbP1(symbol, runtype, floorLog2(maxVal) + 1, uiCtxT);
+  if (msbP1 > 1)
+  {
+    uint32_t numBins = floorLog2(maxVal) + 1;
+    if (msbP1 < numBins)
+    {
+
+      uint32_t bits = msbP1 - 1;
+      m_BinEncoder.encodeBinsEP(symbol & ((1 << bits) - 1), bits);
+    }
+    else
+    {
+      uint32_t curValue = 1 << (numBins - 1);
+      xWriteTruncBinCode(symbol - curValue, maxVal + 1 - curValue);
+    }
+  }
+}
+
+#endif
 
 //================================================================================
 //  clause 7.3.8.6
diff --git a/source/Lib/EncoderLib/CABACWriter.h b/source/Lib/EncoderLib/CABACWriter.h
index 32691e0388f9bfb4f166da80fc9d0f0b0238635f..3ac03b4e775f3fcfb64bec79a751e4f734a6cd58 100644
--- a/source/Lib/EncoderLib/CABACWriter.h
+++ b/source/Lib/EncoderLib/CABACWriter.h
@@ -90,6 +90,7 @@ public:
   void        cu_skip_flag              ( const CodingUnit&             cu );
   void        pred_mode                 ( const CodingUnit&             cu );
   void        bdpcm_mode                ( const CodingUnit&             cu,       const ComponentID compID );
+  
   void        pcm_data                  ( const CodingUnit&             cu,       Partitioner&      pm );
   void        pcm_flag                  ( const CodingUnit&             cu,       Partitioner&      pm );
   void        cu_pred_data              ( const CodingUnit&             cu );
@@ -108,7 +109,12 @@ public:
   void        mip_flag                  ( const CodingUnit&             cu );
   void        mip_pred_modes            ( const CodingUnit&             cu );
   void        mip_pred_mode             ( const PredictionUnit&         pu );
-
+#if JVET_O0119_BASE_PALETTE_444
+  void        cu_palette_info           ( const CodingUnit& cu, ComponentID compBegin, uint32_t numComp, CUCtx& cuCtx);
+  void        cu_run_val                ( uint32_t run, PLTRunMode runtype, const uint32_t paletteIdx, const uint32_t maxRun);
+  void        encodeRunType             ( const CodingUnit&  cu, PLTtypeBuf& runType, uint32_t idx, ScanElement *refScanOrder, ComponentID compBegin);
+  Pel         writePLTIndex             ( const CodingUnit& cu, uint32_t idx, PelBuf& paletteIdx, PLTtypeBuf& paletteRunType, int maxSymbol, ComponentID compBegin);
+#endif
   // prediction unit (clause 7.3.8.6)
   void        prediction_unit           ( const PredictionUnit&         pu );
   void        merge_flag                ( const PredictionUnit&         pu );
@@ -195,12 +201,20 @@ private:
   unsigned    get_num_written_bits()    { return m_BinEncoder.getNumWrittenBits(); }
 
   void  xWriteTruncBinCode(uint32_t uiSymbol, uint32_t uiMaxSymbol);
-
+#if JVET_O0119_BASE_PALETTE_444
+  void        codeScanRotationModeFlag  ( const CodingUnit& cu, ComponentID compBegin);
+  void        xEncodePLTPredIndicator   ( const CodingUnit& cu, uint32_t maxPltSize, ComponentID compBegin);
+  uint32_t    xWriteTruncMsbP1          ( uint32_t symbol, PLTRunMode runtype, uint32_t maxVal, uint32_t ctxT);
+  void        xWriteTruncMsbP1RefinementBits  ( uint32_t symbol, PLTRunMode runtype, uint32_t maxVal, uint32_t ctxT);
+#endif
 private:
   BinEncIf&         m_BinEncoder;
   OutputBitstream*  m_Bitstream;
   Ctx               m_TestCtx;
   EncCu*            m_EncCu;
+#if JVET_O0119_BASE_PALETTE_444
+  ScanElement*      m_scanOrder;
+#endif
 };
 
 
diff --git a/source/Lib/EncoderLib/EncCfg.h b/source/Lib/EncoderLib/EncCfg.h
index 49be1d6dc07d90f1a91bd434b5edaa104c89028e..03d0d061258b58838dcdf60b04d67c7932b6bbfa 100644
--- a/source/Lib/EncoderLib/EncCfg.h
+++ b/source/Lib/EncoderLib/EncCfg.h
@@ -308,6 +308,9 @@ protected:
   bool      m_MMVD;
   int       m_MmvdDisNum;
   bool      m_RdpcmMode;
+#if JVET_O0119_BASE_PALETTE_444
+  unsigned  m_PLTMode;
+#endif
 #if JVET_O0376_SPS_JOINTCBCR_FLAG
   bool      m_JointCbCrMode;
 #endif
@@ -900,6 +903,10 @@ public:
   int       getMmvdDisNum                   ()         const { return m_MmvdDisNum; }
   void      setRDPCM                     ( bool b )       { m_RdpcmMode = b; }
   bool      getRDPCM                     ()         const { return m_RdpcmMode; }
+#if JVET_O0119_BASE_PALETTE_444
+  void      setPLTMode                   ( unsigned n)    { m_PLTMode = n; }
+  unsigned  getPLTMode                   ()         const { return m_PLTMode; }
+#endif
 #if JVET_O0376_SPS_JOINTCBCR_FLAG
   void      setJointCbCr                    ( bool b )       { m_JointCbCrMode = b; }
   bool      getJointCbCr                    ()         const { return m_JointCbCrMode; }
diff --git a/source/Lib/EncoderLib/EncCu.cpp b/source/Lib/EncoderLib/EncCu.cpp
index bd6f75647d85c88011e7ec33f1aad21ac98cc85b..65c1dc2377942c42eb720b730644a2b75cba939e 100644
--- a/source/Lib/EncoderLib/EncCu.cpp
+++ b/source/Lib/EncoderLib/EncCu.cpp
@@ -57,8 +57,6 @@
 extern std::recursive_mutex g_cache_mutex;
 #endif
 
-
-
 //! \ingroup EncoderLib
 //! \{
 
@@ -290,7 +288,9 @@ void EncCu::init( EncLib* pcEncLib, const SPS& sps PARL_PARAM( const int tId ) )
 void EncCu::compressCtu( CodingStructure& cs, const UnitArea& area, const unsigned ctuRsAddr, const int prevQP[], const int currQP[] )
 {
   m_modeCtrl->initCTUEncoding( *cs.slice );
-
+#if JVET_O0119_BASE_PALETTE_444
+  cs.slice->m_mapPltCost.clear();
+#endif
 #if ENABLE_SPLIT_PARALLELISM
   if( m_pcEncCfg->getNumSplitThreads() > 1 )
   {
@@ -364,7 +364,9 @@ void EncCu::compressCtu( CodingStructure& cs, const UnitArea& area, const unsign
   tempCS->prevQP[CH_L] = bestCS->prevQP[CH_L] = prevQP[CH_L];
 
   xCompressCU( tempCS, bestCS, *partitioner );
-
+#if JVET_O0119_BASE_PALETTE_444
+  cs.slice->m_mapPltCost.clear();
+#endif
   // all signals were already copied during compression if the CTU was split - at this point only the structures are copied to the top level CS
   const bool copyUnsplitCTUSignals = bestCS->cus.size() == 1;
   cs.useSubStructure( *bestCS, partitioner->chType, CS::getArea( *bestCS, area, partitioner->chType ), copyUnsplitCTUSignals, false, false, copyUnsplitCTUSignals );
@@ -603,6 +605,42 @@ void EncCu::xCompressCU( CodingStructure *&tempCS, CodingStructure *&bestCS, Par
     }
   }
 
+#endif
+#if JVET_O0119_BASE_PALETTE_444
+  uint32_t compBegin;
+  uint32_t numComp;
+  bool jointPLT = false;
+  if (CS::isDualITree(*bestCS))
+  {
+    if (isLuma(partitioner.chType))
+    {
+      compBegin = COMPONENT_Y;
+      numComp = 1;
+    }
+    else
+    {
+      compBegin = COMPONENT_Cb;
+      numComp = 2;
+    }
+  }
+  else
+  {
+    compBegin = COMPONENT_Y;
+    numComp = 3;
+    jointPLT = true;
+  }
+  SplitSeries splitmode = -1;
+  uint32_t  bestLastPLTSize[MAX_NUM_COMPONENT];
+  Pel       bestLastPLT[MAX_NUM_COMPONENT][MAXPLTPREDSIZE]; // store LastPLT for partition
+  uint32_t  curLastPLTSize[MAX_NUM_COMPONENT];
+  Pel       curLastPLT[MAX_NUM_COMPONENT][MAXPLTPREDSIZE]; // store LastPLT if no partition
+  for (int i = compBegin; i < (compBegin + numComp); i++)
+  {
+    ComponentID comID = jointPLT ? (ComponentID)compBegin : ((i > 0) ? COMPONENT_Cb : COMPONENT_Y);
+    bestLastPLTSize[comID] = 0;
+    curLastPLTSize[comID] = tempCS->prevPLT.curPLTSize[comID];
+    memcpy(curLastPLT[i], tempCS->prevPLT.curPLT[i], tempCS->prevPLT.curPLTSize[comID] * sizeof(Pel));
+  }
 #endif
 
   Slice&   slice      = *tempCS->slice;
@@ -666,6 +704,14 @@ void EncCu::xCompressCU( CodingStructure *&tempCS, CodingStructure *&bestCS, Par
 #endif
   do
   {
+#if JVET_O0119_BASE_PALETTE_444
+  for (int i = compBegin; i < (compBegin + numComp); i++)
+  {
+    ComponentID comID = jointPLT ? (ComponentID)compBegin : ((i > 0) ? COMPONENT_Cb : COMPONENT_Y);
+    tempCS->prevPLT.curPLTSize[comID] = curLastPLTSize[comID];
+    memcpy(tempCS->prevPLT.curPLT[i], curLastPLT[i], curLastPLTSize[comID] * sizeof(Pel));
+  }
+#endif
     EncTestMode currTestMode = m_modeCtrl->currTestMode();
 #if JVET_O0502_ISP_CLEANUP
     currTestMode.maxCostAllowed = maxCostAllowed;
@@ -767,6 +813,12 @@ void EncCu::xCompressCU( CodingStructure *&tempCS, CodingStructure *&bestCS, Par
     {
       xCheckIntraPCM( tempCS, bestCS, partitioner, currTestMode );
     }
+#if JVET_O0119_BASE_PALETTE_444
+  else if (currTestMode.type == ETM_PALETTE)
+  {
+    xCheckPLT( tempCS, bestCS, partitioner, currTestMode );
+  }
+#endif
     else if (currTestMode.type == ETM_IBC)
     {
       xCheckRDCostIBCMode(tempCS, bestCS, partitioner, currTestMode);
@@ -777,9 +829,28 @@ void EncCu::xCompressCU( CodingStructure *&tempCS, CodingStructure *&bestCS, Par
     }
     else if( isModeSplit( currTestMode ) )
     {
-
+#if JVET_O0119_BASE_PALETTE_444
+    if (bestCS->cus.size() != 0)
+    {
+      splitmode = bestCS->cus[0]->splitSeries;
+    }
+#endif
       xCheckModeSplit( tempCS, bestCS, partitioner, currTestMode );
+#if JVET_O0119_BASE_PALETTE_444
+    if (splitmode != bestCS->cus[0]->splitSeries)
+    {
+      splitmode = bestCS->cus[0]->splitSeries;
+      const CodingUnit&     cu = *bestCS->cus.front();
+      cu.cs->prevPLT = bestCS->prevPLT;
+      for (int i = compBegin; i < (compBegin + numComp); i++)
+      {
+        ComponentID comID = jointPLT ? (ComponentID)compBegin : ((i > 0) ? COMPONENT_Cb : COMPONENT_Y);
+        bestLastPLTSize[comID] = bestCS->cus[0]->cs->prevPLT.curPLTSize[comID];
+        memcpy(bestLastPLT[i], bestCS->cus[0]->cs->prevPLT.curPLT[i], bestCS->cus[0]->cs->prevPLT.curPLTSize[comID] * sizeof(Pel));
+      }
     }
+#endif
+  }
     else
     {
       THROW( "Don't know how to handle mode: type = " << currTestMode.type << ", options = " << currTestMode.opts );
@@ -842,6 +913,41 @@ void EncCu::xCompressCU( CodingStructure *&tempCS, CodingStructure *&bestCS, Par
     tempCS->picture->finishParallelPart( currCsArea );
   }
 
+#endif
+#if JVET_O0119_BASE_PALETTE_444
+  if (bestCS->cus.size() == 1) // no partition
+  {
+    if (bestCS->cus[0]->predMode == MODE_PLT)
+    {
+      for (int i = compBegin; i < (compBegin + numComp); i++)
+      {
+        ComponentID comID = jointPLT ? (ComponentID)compBegin : ((i > 0) ? COMPONENT_Cb : COMPONENT_Y);
+        bestCS->prevPLT.curPLTSize[comID] = curLastPLTSize[comID];
+        memcpy(bestCS->prevPLT.curPLT[i], curLastPLT[i], curLastPLTSize[comID] * sizeof(Pel));
+      }
+      bestCS->reorderPrevPLT(bestCS->prevPLT, bestCS->cus[0]->curPLTSize, bestCS->cus[0]->curPLT, bestCS->cus[0]->reuseflag, compBegin, numComp, jointPLT);
+    }
+    else
+    {
+      for (int i = compBegin; i<(compBegin + numComp); i++)
+      {
+        ComponentID comID = jointPLT ? (ComponentID)compBegin : ((i > 0) ? COMPONENT_Cb : COMPONENT_Y);
+        bestCS->prevPLT.curPLTSize[comID] = curLastPLTSize[comID];
+        memcpy(bestCS->prevPLT.curPLT[i], curLastPLT[i], bestCS->prevPLT.curPLTSize[comID] * sizeof(Pel));
+      }
+    }
+  }
+  else
+  {
+    for (int i = compBegin; i<(compBegin + numComp); i++)
+    {
+      ComponentID comID = jointPLT ? (ComponentID)compBegin : ((i > 0) ? COMPONENT_Cb : COMPONENT_Y);
+      bestCS->prevPLT.curPLTSize[comID] = bestLastPLTSize[comID];
+      memcpy(bestCS->prevPLT.curPLT[i], bestLastPLT[i], bestCS->prevPLT.curPLTSize[comID] * sizeof(Pel));
+    }
+  }
+  const CodingUnit&     cu = *bestCS->cus.front();
+  cu.cs->prevPLT = bestCS->prevPLT;
 #endif
   // Assert if Best prediction mode is NONE
   // Selected mode's RD-cost must be not MAX_DOUBLE.
@@ -1131,6 +1237,9 @@ void EncCu::xCheckModeSplit(CodingStructure *&tempCS, CodingStructure *&bestCS,
   const PPS &pps              = *tempCS->pps;
   const uint32_t currDepth    = partitioner.currDepth;
 #endif
+#if JVET_O0119_BASE_PALETTE_444
+  const auto oldPLT = tempCS->prevPLT;
+#endif
 
   const PartSplit split = getPartSplit( encTestMode );
 
@@ -1382,6 +1491,10 @@ void EncCu::xCheckModeSplit(CodingStructure *&tempCS, CodingStructure *&bestCS,
 
   tempCS->motionLut = oldMotionLut;
 
+#if JVET_O0119_BASE_PALETTE_444
+  tempCS->prevPLT = oldPLT;
+#endif
+
   tempCS->releaseIntermediateData();
 
   tempCS->prevQP[partitioner.chType] = oldPrevQp;
@@ -1777,6 +1890,97 @@ void EncCu::xCheckIntraPCM(CodingStructure *&tempCS, CodingStructure *&bestCS, P
   xCheckBestMode( tempCS, bestCS, partitioner, encTestMode );
 }
 
+#if JVET_O0119_BASE_PALETTE_444
+void EncCu::xCheckPLT(CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode)
+{
+  tempCS->initStructData(encTestMode.qp, encTestMode.lossless);
+  CodingUnit &cu = tempCS->addCU(CS::getArea(*tempCS, tempCS->area, partitioner.chType), partitioner.chType);
+  partitioner.setCUData(cu);
+  cu.slice = tempCS->slice;
+  cu.tileIdx = tempCS->picture->brickMap->getBrickIdxRsMap(tempCS->area.lumaPos());
+  cu.skip = false;
+  cu.mmvdSkip = false;
+  cu.predMode = MODE_PLT;
+
+  cu.transQuantBypass = encTestMode.lossless;
+  cu.chromaQpAdj = cu.transQuantBypass ? 0 : m_cuChromaQpOffsetIdxPlus1;
+  cu.qp = encTestMode.qp;
+  cu.ipcm = false;
+  cu.bdpcmMode = 0;
+
+  tempCS->addPU(CS::getArea(*tempCS, tempCS->area, partitioner.chType), partitioner.chType);
+  tempCS->addTU(CS::getArea(*tempCS, tempCS->area, partitioner.chType), partitioner.chType);
+  // Search
+  tempCS->dist = 0;  
+  if (CS::isDualITree(*tempCS))
+  {
+    if (isLuma(partitioner.chType))
+    {
+      m_pcIntraSearch->PLTSearch(*tempCS, partitioner, COMPONENT_Y, 1);
+    }
+    if (tempCS->area.chromaFormat != CHROMA_400 && (partitioner.chType == CHANNEL_TYPE_CHROMA))
+    {
+      m_pcIntraSearch->PLTSearch(*tempCS, partitioner, COMPONENT_Cb, 2);
+    }
+  }
+  else
+  {
+    m_pcIntraSearch->PLTSearch(*tempCS, partitioner, COMPONENT_Y, 3);
+  }
+
+
+  m_CABACEstimator->getCtx() = m_CurrCtx->start;
+  m_CABACEstimator->resetBits();
+  if (tempCS->pps->getTransquantBypassEnabledFlag())
+  {
+    m_CABACEstimator->cu_transquant_bypass_flag(cu);
+  }
+
+  if ((!cu.cs->slice->isIntra() || cu.cs->slice->getSPS()->getIBCFlag())
+    && cu.Y().valid())
+  {
+    m_CABACEstimator->cu_skip_flag(cu);
+  }
+  m_CABACEstimator->pred_mode(cu);
+
+  // signaling
+  CUCtx cuCtx;
+  cuCtx.isDQPCoded = true;
+  cuCtx.isChromaQpAdjCoded = true;
+  if (CS::isDualITree(*tempCS))
+  {
+    if (isLuma(partitioner.chType))
+    {
+      m_CABACEstimator->cu_palette_info(cu, COMPONENT_Y, 1, cuCtx);
+    }
+    if (tempCS->area.chromaFormat != CHROMA_400 && (partitioner.chType == CHANNEL_TYPE_CHROMA))
+    {
+      m_CABACEstimator->cu_palette_info(cu, COMPONENT_Cb, 2, cuCtx);
+    }
+  }
+  else
+  {
+    m_CABACEstimator->cu_palette_info(cu, COMPONENT_Y, 3, cuCtx);
+  }
+  tempCS->fracBits = m_CABACEstimator->getEstFracBits();
+  tempCS->cost = m_pcRdCost->calcRdCost(tempCS->fracBits, tempCS->dist);
+
+  xEncodeDontSplit(*tempCS, partitioner);
+  xCheckDQP(*tempCS, partitioner);
+  xCalDebCost(*tempCS, partitioner);
+  tempCS->useDbCost = m_pcEncCfg->getUseEncDbOpt();
+
+  const Area currCuArea = cu.block(getFirstComponentOfChannel(partitioner.chType));
+  cu.slice->m_mapPltCost[currCuArea.pos()][currCuArea.size()] = tempCS->cost;
+#if WCG_EXT
+  DTRACE_MODE_COST(*tempCS, m_pcRdCost->getLambda(true));
+#else
+  DTRACE_MODE_COST(*tempCS, m_pcRdCost->getLambda());
+#endif
+  xCheckBestMode(tempCS, bestCS, partitioner, encTestMode);
+}
+#endif
+
 void EncCu::xCheckDQP( CodingStructure& cs, Partitioner& partitioner, bool bKeepCtx )
 {
   CHECK( bKeepCtx && cs.cus.size() <= 1 && partitioner.getImplicitSplit( cs ) == CU_DONT_SPLIT, "bKeepCtx should only be set in split case" );
@@ -4449,7 +4653,11 @@ void EncCu::xReuseCachedResult( CodingStructure *&tempCS, CodingStructure *&best
     cu.shareParentSize = tempCS->sharedBndSize;
     partitioner.setCUData( cu );
 
-    if( CU::isIntra( cu ) )
+    if( CU::isIntra( cu ) 
+#if JVET_O0119_BASE_PALETTE_444
+    || CU::isPLT(cu)
+#endif
+    )
     {
       xReconIntraQT( cu );
     }
diff --git a/source/Lib/EncoderLib/EncCu.h b/source/Lib/EncoderLib/EncCu.h
index 9ea4376c59f36d45e4adde91438d27200eaf2a42..82bc30422d8ee48587975dc213a98e57757633fd 100644
--- a/source/Lib/EncoderLib/EncCu.h
+++ b/source/Lib/EncoderLib/EncCu.h
@@ -143,10 +143,10 @@ private:
   void    updateLambda      ( Slice* slice, const int dQP, const bool updateRdCostLambda );
 #endif
   double                m_sbtCostSave[2];
-
 public:
   /// copy parameters from encoder class
   void  init                ( EncLib* pcEncLib, const SPS& sps PARL_PARAM( const int jId = 0 ) );
+
   void setDecCuReshaperInEncCU(EncReshape* pcReshape, ChromaFormat chromaFormatIDC) { initDecCuReshaper((Reshape*) pcReshape, chromaFormatIDC); }
   /// create internal buffers
   void  create              ( EncCfg* encCfg );
@@ -235,6 +235,10 @@ protected:
   }
   void xCheckRDCostIBCMode    ( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &pm, const EncTestMode& encTestMode );
   void xCheckRDCostIBCModeMerge2Nx2N( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode );
+
+#if JVET_O0119_BASE_PALETTE_444
+  void xCheckPLT              ( CodingStructure *&tempCS, CodingStructure *&bestCS, Partitioner &partitioner, const EncTestMode& encTestMode);
+#endif
 };
 
 //! \}
diff --git a/source/Lib/EncoderLib/EncLib.cpp b/source/Lib/EncoderLib/EncLib.cpp
index 3504d6fd32235bdad327fab0e444e6544b13fe1c..daca183e0847f7c4fdb8d19e513339d9a697d83f 100644
--- a/source/Lib/EncoderLib/EncLib.cpp
+++ b/source/Lib/EncoderLib/EncLib.cpp
@@ -66,6 +66,9 @@ EncLib::EncLib()
   , m_cacheModel()
 #endif
   , m_lmcsAPS(nullptr)
+#if JVET_O0119_BASE_PALETTE_444
+  , m_doPlt( true )
+#endif
 {
   m_iPOCLast          = -1;
   m_iNumPicRcvd       =  0;
@@ -949,7 +952,9 @@ void EncLib::xInitSPS(SPS &sps)
 #endif
   sps.setAffineAmvrEnabledFlag              ( m_AffineAmvr );
   sps.setUseDMVR                            ( m_DMVR );
-
+#if JVET_O0119_BASE_PALETTE_444
+  sps.setPLTMode                            ( m_PLTMode);
+#endif
   sps.setIBCFlag                            ( m_IBCMode);
   sps.setWrapAroundEnabledFlag                      ( m_wrapAround );
   sps.setWrapAroundOffset                   ( m_wrapAroundOffset );
@@ -1716,6 +1721,39 @@ bool EncLib::SPSNeedsWriting(int spsId)
   return bChanged;
 }
 
+#if JVET_O0119_BASE_PALETTE_444
+void EncLib::checkPltStats( Picture* pic )
+{
+  int totalArea = 0;
+  int pltArea = 0;
+  for (auto apu : pic->cs->pus)
+  {
+    for (int i = 0; i < MAX_NUM_TBLOCKS; ++i)
+    {
+      int puArea = apu->blocks[i].width * apu->blocks[i].height;
+      if (apu->blocks[i].width > 0 && apu->blocks[i].height > 0)
+      {
+        totalArea += puArea;
+        if (CU::isPLT(*apu->cu) || CU::isIBC(*apu->cu))
+        {
+          pltArea += puArea;
+        }
+        break;
+      }
+
+    }
+  }
+  if (pltArea * PLT_FAST_RATIO < totalArea)
+  {
+    m_doPlt = false;
+  }
+  else
+  {
+    m_doPlt = true;
+  }
+}
+#endif
+
 #if X0038_LAMBDA_FROM_QP_CAPABILITY
 int EncCfg::getQPForPicture(const uint32_t gopIndex, const Slice *pSlice) const
 {
@@ -1787,4 +1825,5 @@ int EncCfg::getQPForPicture(const uint32_t gopIndex, const Slice *pSlice) const
 }
 #endif
 
+
 //! \}
diff --git a/source/Lib/EncoderLib/EncLib.h b/source/Lib/EncoderLib/EncLib.h
index 71bcf0d1b5abc5faa62e6359660f179ffc9b0ef7..09eaa50d4d436359042ec64f3002938493a4b37f 100644
--- a/source/Lib/EncoderLib/EncLib.h
+++ b/source/Lib/EncoderLib/EncLib.h
@@ -148,6 +148,10 @@ private:
 
   EncHRD                    m_encHRD;
 
+#if JVET_O0119_BASE_PALETTE_444 
+  bool                      m_doPlt;
+#endif
+
 public:
   Ctx                       m_entropyCodingSyncContextState;      ///< leave in addition to vector for compatibility
 #if ENABLE_WPP_PARALLELISM
@@ -241,6 +245,11 @@ public:
 #endif
 
   ParameterSetMap<APS>*  getApsMap() { return &m_apsMap; }
+
+#if JVET_O0119_BASE_PALETTE_444
+  bool                   getPltEnc()   const { return m_doPlt; }
+  void                   checkPltStats( Picture* pic );
+#endif
   // -------------------------------------------------------------------------------------------------------------------
   // encoder function
   // -------------------------------------------------------------------------------------------------------------------
diff --git a/source/Lib/EncoderLib/EncModeCtrl.cpp b/source/Lib/EncoderLib/EncModeCtrl.cpp
index acfb5f43ad30f8f93bb5d7b29ca9fba51a7aedcc..194b94f624edf803c35d0d7b0d9657de5f49d903 100644
--- a/source/Lib/EncoderLib/EncModeCtrl.cpp
+++ b/source/Lib/EncoderLib/EncModeCtrl.cpp
@@ -768,13 +768,25 @@ void BestEncInfoCache::init( const Slice &slice )
 #if REUSE_CU_RESULTS_WITH_MULTIPLE_TUS
   m_pCoeff  = new TCoeff[numCoeff*MAX_NUM_TUS];
   m_pPcmBuf = new Pel   [numCoeff*MAX_NUM_TUS];
+#if JVET_O0119_BASE_PALETTE_444
+  m_runType   = new bool[numCoeff*MAX_NUM_TUS];
+  m_runLength = new Pel [numCoeff*MAX_NUM_TUS];
+#endif
 #else
   m_pCoeff  = new TCoeff[numCoeff];
   m_pPcmBuf = new Pel   [numCoeff];
+#if JVET_O0119_BASE_PALETTE_444
+  m_runType   = new bool[numCoeff];
+  m_runLength = new Pel [numCoeff];
+#endif
 #endif
 
   TCoeff *coeffPtr = m_pCoeff;
   Pel    *pcmPtr   = m_pPcmBuf;
+#if JVET_O0119_BASE_PALETTE_444
+  bool   *runTypePtr   = m_runType;
+  Pel    *runLengthPtr = m_runLength;
+#endif
 
   m_dummyCS.pcv = m_slice_bencinf->getPPS()->pcv;
 
@@ -790,6 +802,10 @@ void BestEncInfoCache::init( const Slice &slice )
           {
             TCoeff *coeff[MAX_NUM_TBLOCKS] = { 0, };
             Pel    *pcmbf[MAX_NUM_TBLOCKS] = { 0, };
+#if JVET_O0119_BASE_PALETTE_444
+            bool   *runType[MAX_NUM_TBLOCKS]   = { 0, };
+            Pel    *runLength[MAX_NUM_TBLOCKS] = { 0, };
+#endif
 
 #if REUSE_CU_RESULTS_WITH_MULTIPLE_TUS
             for( int i = 0; i < MAX_NUM_TUS; i++ )
@@ -801,10 +817,18 @@ void BestEncInfoCache::init( const Slice &slice )
               {
                 coeff[i] = coeffPtr; coeffPtr += area.blocks[i].area();
                 pcmbf[i] = pcmPtr;   pcmPtr += area.blocks[i].area();
+#if JVET_O0119_BASE_PALETTE_444
+                runType[i]   = runTypePtr;   runTypePtr += area.blocks[i].area();
+                runLength[i] = runLengthPtr; runLengthPtr += area.blocks[i].area();
+#endif
               }
 
               tu.cs = &m_dummyCS;
+#if JVET_O0119_BASE_PALETTE_444
+              tu.init(coeff, pcmbf, runLength, runType);
+#else
               tu.init(coeff, pcmbf);
+#endif
             }
 #else
             const UnitArea &area = m_bestEncInfo[x][y][wIdx][hIdx]->tu;
@@ -813,10 +837,18 @@ void BestEncInfoCache::init( const Slice &slice )
             {
               coeff[i] = coeffPtr; coeffPtr += area.blocks[i].area();
               pcmbf[i] =   pcmPtr;   pcmPtr += area.blocks[i].area();
+#if JVET_O0119_BASE_PALETTE_444
+              runType[i] = runTypePtr;     runTypePtr += area.blocks[i].area();
+              runLength[i] = runLengthPtr; runLengthPtr += area.blocks[i].area();
+#endif
             }
 
             m_bestEncInfo[x][y][wIdx][hIdx]->tu.cs = &m_dummyCS;
+#if JVET_O0119_BASE_PALETTE_444
+            m_bestEncInfo[x][y][wIdx][hIdx]->tu.init(coeff, pcmbf, runLength, runType);
+#else
             m_bestEncInfo[x][y][wIdx][hIdx]->tu.init( coeff, pcmbf );
+#endif
 #endif
           }
         }
@@ -1083,7 +1115,6 @@ void EncModeCtrlMTnoRQT::initCTUEncoding( const Slice &slice )
   }
 }
 
-
 void EncModeCtrlMTnoRQT::initCULevel( Partitioner &partitioner, const CodingStructure& cs )
 {
   // Min/max depth
@@ -1311,8 +1342,20 @@ void EncModeCtrlMTnoRQT::initCULevel( Partitioner &partitioner, const CodingStru
 #endif
     // add intra modes
     m_ComprCUCtxList.back().testModes.push_back( { ETM_IPCM,  ETO_STANDARD, qp, lossless } );
-    m_ComprCUCtxList.back().testModes.push_back( { ETM_INTRA, ETO_STANDARD, qp, lossless } );
-    // add ibc mode to intra path
+#if JVET_O0119_BASE_PALETTE_444
+  if (cs.slice->getSPS()->getPLTMode() && ( cs.slice->isIRAP() || (cs.area.lwidth() == 4 && cs.area.lheight() == 4) ) && getPltEnc() )
+  {
+    m_ComprCUCtxList.back().testModes.push_back({ ETM_PALETTE, ETO_STANDARD, qp, lossless });
+  }
+#endif
+  m_ComprCUCtxList.back().testModes.push_back( { ETM_INTRA, ETO_STANDARD, qp, lossless } );
+#if JVET_O0119_BASE_PALETTE_444
+  if (cs.slice->getSPS()->getPLTMode() && !cs.slice->isIRAP() && !(cs.area.lwidth() == 4 && cs.area.lheight() == 4) && getPltEnc() )
+  {
+    m_ComprCUCtxList.back().testModes.push_back({ ETM_PALETTE,  ETO_STANDARD, qp, lossless });
+  }
+#endif
+  // add ibc mode to intra path
     if (cs.sps->getIBCFlag() && checkIbc)
     {
       m_ComprCUCtxList.back().testModes.push_back({ ETM_IBC,         ETO_STANDARD,  qp, lossless });
@@ -1532,9 +1575,37 @@ bool EncModeCtrlMTnoRQT::tryMode( const EncTestMode& encTestmode, const CodingSt
         }
       }
     }
-
+#if JVET_O0119_BASE_PALETTE_444
+  if (bestMode.type == ETM_PALETTE && !slice.isIRAP() && !( partitioner.currArea().lumaSize().width == 4 && partitioner.currArea().lumaSize().height == 4) ) // inter slice
+  {
+    return false;
+  }
+#endif
+    return true;
+  }
+#if JVET_O0119_BASE_PALETTE_444
+  else if (encTestmode.type == ETM_PALETTE)
+  {
+    if (partitioner.currArea().lumaSize().width > 64 || partitioner.currArea().lumaSize().height > 64)
+    {
+      return false;
+    }
+    const Area curr_cu = CS::getArea(cs, cs.area, partitioner.chType).blocks[getFirstComponentOfChannel(partitioner.chType)];
+    try
+    {
+      double stored_cost = slice.m_mapPltCost.at(curr_cu.pos()).at(curr_cu.size());
+      if (bestMode.type != ETM_INVALID && stored_cost > cuECtx.bestCS->cost)
+      {
+        return false;
+      }
+    }
+    catch (const std::out_of_range &)
+    {
+      // do nothing if no stored cost value was found.
+    }
     return true;
   }
+#endif
   else if( encTestmode.type == ETM_IPCM )
   {
     if( getFastDeltaQp() )
diff --git a/source/Lib/EncoderLib/EncModeCtrl.h b/source/Lib/EncoderLib/EncModeCtrl.h
index a690dc538774af55c66f4bd6df999dbc9f6220ad..9cb8ce809b41cccbdb8eab23ed1bf5733b8ca22a 100644
--- a/source/Lib/EncoderLib/EncModeCtrl.h
+++ b/source/Lib/EncoderLib/EncModeCtrl.h
@@ -64,6 +64,9 @@ enum EncTestModeType
   ETM_MERGE_TRIANGLE,
   ETM_INTRA,
   ETM_IPCM,
+#if JVET_O0119_BASE_PALETTE_444
+  ETM_PALETTE,  
+#endif
   ETM_SPLIT_QT,
   ETM_SPLIT_BT_H,
   ETM_SPLIT_BT_V,
@@ -268,6 +271,10 @@ protected:
   InterSearch*          m_pcInterSearch;
 #endif
 
+#if JVET_O0119_BASE_PALETTE_444
+  bool                  m_doPlt;
+#endif
+
 public:
 
   virtual ~EncModeCtrl              () {}
@@ -327,6 +334,10 @@ public:
 #if JVET_O0592_ENC_ME_IMP
   void setInterSearch                 (InterSearch* pcInterSearch)   { m_pcInterSearch = pcInterSearch; }
 #endif
+#if JVET_O0119_BASE_PALETTE_444
+  void   setPltEnc                    ( bool b )                { m_doPlt = b; }
+  bool   getPltEnc()                                      const { return m_doPlt; }
+#endif
 
 protected:
   void xExtractFeatures ( const EncTestMode encTestmode, CodingStructure& cs );
@@ -468,6 +479,10 @@ private:
   BestEncodingInfo ***m_bestEncInfo[MAX_CU_SIZE >> MIN_CU_LOG2][MAX_CU_SIZE >> MIN_CU_LOG2];
   TCoeff             *m_pCoeff;
   Pel                *m_pPcmBuf;
+#if JVET_O0119_BASE_PALETTE_444
+  bool               *m_runType;
+  Pel                *m_runLength;
+#endif
   CodingStructure     m_dummyCS;
   XUCache             m_dummyCache;
 #if ENABLE_SPLIT_PARALLELISM
diff --git a/source/Lib/EncoderLib/EncSlice.cpp b/source/Lib/EncoderLib/EncSlice.cpp
index f8fa6876b20cce2d2312f8acf01f77c17590f974..349701bfa8041e81b363c87646fd66648e0b5797 100644
--- a/source/Lib/EncoderLib/EncSlice.cpp
+++ b/source/Lib/EncoderLib/EncSlice.cpp
@@ -337,6 +337,9 @@ void EncSlice::initEncSlice(Picture* pcPic, const int pocLast, const int pocCurr
 {
   double dQP;
   double dLambda;
+#if JVET_O0119_BASE_PALETTE_444
+  pcPic->cs->resetPrevPLT(pcPic->cs->prevPLT);
+#endif
 
   rpcSlice = pcPic->slices[0];
   rpcSlice->setSliceBits(0);
@@ -1396,6 +1399,19 @@ void EncSlice::compressSlice( Picture* pcPic, const bool bCompressEntireSlice, c
   }
 #endif // ENABLE_QPA
 
+#if JVET_O0119_BASE_PALETTE_444
+  bool checkPLTRatio = m_pcCfg->getIntraPeriod() != 1 && pcSlice->isIRAP();
+  if (checkPLTRatio)
+  {
+    m_pcCuEncoder->getModeCtrl()->setPltEnc(true);
+  }
+  else
+  {
+    bool doPlt = m_pcLib->getPltEnc();
+    m_pcCuEncoder->getModeCtrl()->setPltEnc(doPlt);
+  }
+#endif
+
 #if ENABLE_WPP_PARALLELISM
   bool bUseThreads = m_pcCfg->getNumWppThreads() > 1;
   if( bUseThreads )
@@ -1430,8 +1446,9 @@ void EncSlice::compressSlice( Picture* pcPic, const bool bCompressEntireSlice, c
   m_pcInterSearch->resetUniMvList();
 #endif 
   encodeCtus( pcPic, bCompressEntireSlice, bFastDeltaQP, startCtuTsAddr, boundingCtuTsAddr, m_pcLib );
-
-
+#if JVET_O0119_BASE_PALETTE_444
+  if (checkPLTRatio) m_pcLib->checkPltStats( pcPic );
+#endif
 }
 
 void EncSlice::checkDisFracMmvd( Picture* pcPic, uint32_t startCtuTsAddr, uint32_t boundingCtuTsAddr )
@@ -1615,12 +1632,18 @@ void EncSlice::encodeCtus( Picture* pcPic, const bool bCompressEntireSlice, cons
     if (ctuRsAddr == firstCtuRsAddrOfTile)
     {
       pCABACWriter->initCtxModels( *pcSlice );
+#if JVET_O0119_BASE_PALETTE_444
+      cs.resetPrevPLT(cs.prevPLT);
+#endif
       prevQP[0] = prevQP[1] = pcSlice->getSliceQp();
     }
     else if (ctuXPosInCtus == tileXPosInCtus && pEncLib->getEntropyCodingSyncEnabledFlag())
     {
       // reset and then update contexts to the state at the end of the top-right CTU (if within current slice and tile).
       pCABACWriter->initCtxModels( *pcSlice );
+#if JVET_O0119_BASE_PALETTE_444
+      cs.resetPrevPLT(cs.prevPLT);
+#endif
       if( cs.getCURestricted( pos.offset(0, -1), pos, pcSlice->getIndependentSliceIdx(), tileMap.getBrickIdxRsMap( pos ), CH_L ) )
       {
         // Top-right is available, we use it.
@@ -1905,6 +1928,9 @@ void EncSlice::encodeSlice   ( Picture* pcPic, OutputBitstream* pcSubstreams, ui
       if (ctuTsAddr != startCtuTsAddr) // if it is the first CTU, then the entropy coder has already been reset
       {
         m_CABACWriter->initCtxModels( *pcSlice );
+#if JVET_O0119_BASE_PALETTE_444
+        cs.resetPrevPLT(cs.prevPLT);
+#endif
       }
     }
     else if (ctuXPosInCtus == tileXPosInCtus && wavefrontsEnabled)
@@ -1913,6 +1939,9 @@ void EncSlice::encodeSlice   ( Picture* pcPic, OutputBitstream* pcSubstreams, ui
       if (ctuTsAddr != startCtuTsAddr) // if it is the first CTU, then the entropy coder has already been reset
       {
         m_CABACWriter->initCtxModels( *pcSlice );
+#if JVET_O0119_BASE_PALETTE_444
+        cs.resetPrevPLT(cs.prevPLT);
+#endif
       }
       if( cs.getCURestricted( pos.offset( 0, -1 ), pos, pcSlice->getIndependentSliceIdx(), tileMap.getBrickIdxRsMap( pos ), CH_L ) )
       {
diff --git a/source/Lib/EncoderLib/IntraSearch.cpp b/source/Lib/EncoderLib/IntraSearch.cpp
index 05d5faabc95c8f620ce0084da90d524bec7b7b95..f5ac671a5d4635ae2280f13ea1c05cfa530d5b5a 100644
--- a/source/Lib/EncoderLib/IntraSearch.cpp
+++ b/source/Lib/EncoderLib/IntraSearch.cpp
@@ -49,10 +49,11 @@
 
 #include <math.h>
 #include <limits>
-
  //! \ingroup EncoderLib
  //! \{
-
+#if JVET_O0119_BASE_PALETTE_444
+#define PLTCtx(c) SubCtx( Ctx::Palette, c )
+#endif
 IntraSearch::IntraSearch()
   : m_pSplitCS      (nullptr)
   , m_pFullCS       (nullptr)
@@ -1538,6 +1539,575 @@ void IntraSearch::xEncPCM(CodingStructure &cs, Partitioner& partitioner, const C
   }
 }
 
+#if JVET_O0119_BASE_PALETTE_444
+void IntraSearch::PLTSearch(CodingStructure &cs, Partitioner& partitioner, ComponentID compBegin, uint32_t numComp)
+{
+  CodingUnit    &cu = *cs.getCU(partitioner.chType);
+  TransformUnit &tu = *cs.getTU(partitioner.chType);
+  uint32_t height = cu.block(compBegin).height;
+  uint32_t width = cu.block(compBegin).width;
+  m_orgCtxRD = PLTCtx(m_CABACEstimator->getCtx());
+
+  if (m_pcEncCfg->getReshaper() && (cs.slice->getLmcsEnabledFlag() && m_pcReshape->getCTUFlag()))
+  {
+    cs.getPredBuf().copyFrom(cs.getOrgBuf());
+    cs.getPredBuf().Y().rspSignal(m_pcReshape->getFwdLUT());
+  }
+
+  Pel  *runLength = tu.getRunLens (compBegin);
+  bool *runType   = tu.getRunTypes(compBegin);
+  cu.lastPLTSize[compBegin] = cs.prevPLT.curPLTSize[compBegin];
+  //derive palette
+  derivePLTLossy(cs, partitioner, compBegin, numComp);
+  reorderPLT(cs, partitioner, compBegin, numComp);
+
+  //calculate palette index
+  preCalcPLTIndex(cs, partitioner, compBegin, numComp);
+  //derive run
+  uint64_t bits = MAX_UINT;
+  deriveRunAndCalcBits(cs, partitioner, compBegin, numComp, PLT_SCAN_HORTRAV, bits);
+  if ((cu.curPLTSize[compBegin] + cu.useEscape[compBegin]) > 1)
+  {
+    deriveRunAndCalcBits(cs, partitioner, compBegin, numComp, PLT_SCAN_VERTRAV, bits);
+  }
+  cu.useRotation[compBegin] = m_bestScanRotationMode;
+  memcpy(runType, m_runTypeRD, sizeof(bool)*width*height);
+  memcpy(runLength, m_runLengthRD, sizeof(Pel)*width*height);
+  //reconstruct pixel
+  PelBuf    curPLTIdx = tu.getcurPLTIdx(compBegin);
+  for (uint32_t y = 0; y < height; y++)
+  {
+    for (uint32_t x = 0; x < width; x++)
+    {
+      if (curPLTIdx.at(x, y) == cu.curPLTSize[compBegin])
+      {
+
+      }
+      else
+      {
+        for (uint32_t compID = compBegin; compID < (compBegin + numComp); compID++)
+        {
+          CompArea area = cu.blocks[compID];
+          PelBuf   recBuf = cs.getRecoBuf(area);
+          uint32_t scaleX = getComponentScaleX((ComponentID)COMPONENT_Cb, cs.sps->getChromaFormatIdc());
+          uint32_t scaleY = getComponentScaleY((ComponentID)COMPONENT_Cb, cs.sps->getChromaFormatIdc());
+          if (compBegin != COMPONENT_Y || compID == COMPONENT_Y)
+          {
+            recBuf.at(x, y) = cu.curPLT[compID][curPLTIdx.at(x, y)];
+          }
+          else if (compBegin == COMPONENT_Y && compID != COMPONENT_Y && y % (1 << scaleY) == 0 && x % (1 << scaleX) == 0)
+          {
+            recBuf.at(x >> scaleX, y >> scaleY) = cu.curPLT[compID][curPLTIdx.at(x, y)];
+          }
+        }
+      }
+    }
+  }
+
+  cs.getPredBuf().fill(0);
+  cs.getResiBuf().fill(0);
+  cs.getOrgResiBuf().fill(0);
+
+  cs.fracBits = MAX_UINT;
+  cs.cost = MAX_DOUBLE;
+  Distortion distortion = 0;
+  for (uint32_t comp = compBegin; comp < (compBegin + numComp); comp++)
+  {
+    const ComponentID compID = ComponentID(comp);
+    CPelBuf reco = cs.getRecoBuf(compID);
+    CPelBuf org = cs.getOrgBuf(compID);
+#if WCG_EXT
+    if (m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled() || (
+      m_pcEncCfg->getReshaper() && (cs.slice->getLmcsEnabledFlag() && m_pcReshape->getCTUFlag())))
+    {
+      const CPelBuf orgLuma = cs.getOrgBuf(cs.area.blocks[COMPONENT_Y]);
+
+      if (compID == COMPONENT_Y && !(m_pcEncCfg->getLumaLevelToDeltaQPMapping().isEnabled()))
+      {
+        const CompArea &areaY = cu.Y();
+        CompArea tmpArea1(COMPONENT_Y, areaY.chromaFormat, Position(0, 0), areaY.size());
+        PelBuf   tmpRecLuma = m_tmpStorageLCU.getBuf(tmpArea1);
+        tmpRecLuma.copyFrom(reco);
+        tmpRecLuma.rspSignal(m_pcReshape->getInvLUT());
+        distortion += m_pcRdCost->getDistPart(org, tmpRecLuma, cs.sps->getBitDepth(toChannelType(compID)), compID, DF_SSE_WTD, &orgLuma);
+      }
+      else
+      {
+        distortion += m_pcRdCost->getDistPart(org, reco, cs.sps->getBitDepth(toChannelType(compID)), compID, DF_SSE_WTD, &orgLuma);
+      }
+    }
+    else
+#endif
+      distortion += m_pcRdCost->getDistPart(org, reco, cs.sps->getBitDepth(toChannelType(compID)), compID, DF_SSE);
+  }
+
+  cs.dist += distortion;
+  const CompArea &area = cu.blocks[compBegin];
+  cs.setDecomp(area);
+  cs.picture->getRecoBuf(area).copyFrom(cs.getRecoBuf(area));
+}
+void IntraSearch::deriveRunAndCalcBits(CodingStructure& cs, Partitioner& partitioner, ComponentID compBegin, uint32_t numComp, PLTScanMode pltScanMode, uint64_t& minBits)
+{
+  CodingUnit    &cu = *cs.getCU(partitioner.chType);
+  TransformUnit &tu = *cs.getTU(partitioner.chType);
+  uint32_t height = cu.block(compBegin).height;
+  uint32_t width = cu.block(compBegin).width;
+  Pel  *runLength = tu.getRunLens (compBegin);
+  bool *runType   = tu.getRunTypes(compBegin);
+
+  cu.useRotation[compBegin] = (pltScanMode == PLT_SCAN_VERTRAV); 
+  m_scanOrder = g_scanOrder[SCAN_UNGROUPED][(cu.useRotation[compBegin]) ? SCAN_TRAV_VER : SCAN_TRAV_HOR][gp_sizeIdxInfo->idxFrom(width)][gp_sizeIdxInfo->idxFrom(height)];
+  deriveRun(cs, partitioner, compBegin);
+
+  m_CABACEstimator->getCtx() = PLTCtx(m_orgCtxRD);
+  m_CABACEstimator->resetBits();
+  CUCtx cuCtx;
+  cuCtx.isDQPCoded = true;
+  cuCtx.isChromaQpAdjCoded = true;
+  m_CABACEstimator->cu_palette_info(cu, compBegin, numComp, cuCtx);
+  uint64_t bitsTemp = m_CABACEstimator->getEstFracBits();
+  if (minBits > bitsTemp)
+  {
+    m_bestScanRotationMode = pltScanMode;
+    memcpy(m_runTypeRD, runType, sizeof(bool)*width*height);
+    memcpy(m_runLengthRD, runLength, sizeof(Pel)*width*height);
+    minBits = bitsTemp;
+  }
+}
+void IntraSearch::deriveRun(CodingStructure& cs, Partitioner& partitioner, ComponentID compBegin)
+{
+  CodingUnit    &cu = *cs.getCU(partitioner.chType);
+  TransformUnit &tu = *cs.getTU(partitioner.chType);
+  uint32_t height = cu.block(compBegin).height;
+  uint32_t width = cu.block(compBegin).width;
+  uint32_t total = height * width, idx = 0;
+  uint32_t startPos = 0;
+  uint64_t indexBits = 0, runBitsIndex = 0, runBitsCopy = 0;
+  m_storeCtxRun = PLTCtx(m_orgCtxRD);
+
+  PLTtypeBuf  runType = tu.getrunType(compBegin);
+  PelBuf      runLength = tu.getrunLength(compBegin);
+  while (idx < total)
+  {
+    startPos = idx;
+    double aveBitsPerPix[NUM_PLT_RUN];
+    uint32_t indexRun = 0;
+    bool runValid = calIndexRun(cs, partitioner, startPos, total, indexRun, compBegin);
+    m_CABACEstimator->getCtx() = PLTCtx(m_storeCtxRun);
+    aveBitsPerPix[PLT_RUN_INDEX] = runValid ? getRunBits(cu, indexRun, startPos, PLT_RUN_INDEX, &indexBits, &runBitsIndex, compBegin) : MAX_DOUBLE;
+    m_storeCtxRunIndex = PLTCtx(m_CABACEstimator->getCtx());
+
+    uint32_t copyRun = 0;
+    bool copyValid = calCopyRun(cs, partitioner, startPos, total, copyRun, compBegin);
+    m_CABACEstimator->getCtx() = PLTCtx(m_storeCtxRun);
+    aveBitsPerPix[PLT_RUN_COPY] = copyValid ? getRunBits(cu, copyRun, startPos, PLT_RUN_COPY, &indexBits, &runBitsCopy, compBegin) : MAX_DOUBLE;
+    m_storeCtxRunCopy = PLTCtx(m_CABACEstimator->getCtx());
+
+    if (copyValid == 0 && runValid == 0)
+    {
+      assert(0);
+    }
+    else
+    {
+      if (aveBitsPerPix[PLT_RUN_COPY] <= aveBitsPerPix[PLT_RUN_INDEX])
+      {
+        for (int runidx = 0; runidx <copyRun; runidx++)
+        {
+          uint32_t posy = m_scanOrder[idx + runidx].y;
+          uint32_t posx = m_scanOrder[idx + runidx].x;
+          runType.at(posx, posy) = PLT_RUN_COPY;
+          runLength.at(posx, posy) = copyRun;
+        }
+        idx += copyRun;
+        m_storeCtxRun = PLTCtx(m_storeCtxRunCopy);
+
+      }
+      else
+      {
+        for (int runidx = 0; runidx <indexRun; runidx++)
+        {
+          uint32_t posy = m_scanOrder[idx + runidx].y;
+          uint32_t posx = m_scanOrder[idx + runidx].x;
+          runType.at(posx, posy) = PLT_RUN_INDEX;
+          runLength.at(posx, posy) = indexRun;
+        }
+        idx += indexRun;
+        m_storeCtxRun = PLTCtx(m_storeCtxRunIndex);
+
+      }
+    }
+  }
+  assert(idx == total);
+}
+double IntraSearch::getRunBits(const CodingUnit&  cu, uint32_t run, uint32_t strPos, PLTRunMode paletteRunMode, uint64_t* indexBits, uint64_t* runBits, ComponentID compBegin)
+{
+  TransformUnit&   tu = *cu.firstTU;
+  uint32_t height = cu.block(compBegin).height;
+  uint32_t width  = cu.block(compBegin).width;
+  uint32_t endPos = height*width;
+  PLTtypeBuf runType   = tu.getrunType(compBegin);
+  PelBuf     curPLTIdx = tu.getcurPLTIdx(compBegin);
+  uint32_t   indexMaxSize = (cu.useEscape[compBegin]) ? (cu.curPLTSize[compBegin] + 1) : cu.curPLTSize[compBegin];
+
+  m_CABACEstimator->resetBits();
+  ///////////////// encode Run Type
+  m_CABACEstimator->encodeRunType(cu, runType, strPos, m_scanOrder, compBegin);
+  uint64_t runTypeBits = m_CABACEstimator->getEstFracBits();
+  uint32_t curLevel = 0;
+  switch (paletteRunMode)
+  {
+  case PLT_RUN_INDEX:
+    curLevel = m_CABACEstimator->writePLTIndex(cu, strPos, curPLTIdx, runType, indexMaxSize, compBegin);
+    *indexBits = m_CABACEstimator->getEstFracBits() - runTypeBits;
+    m_CABACEstimator->cu_run_val(run - 1, PLT_RUN_INDEX, curLevel, endPos - strPos - 1);
+    *runBits = m_CABACEstimator->getEstFracBits() - runTypeBits - (*indexBits);
+    break;
+  case PLT_RUN_COPY:
+    m_CABACEstimator->cu_run_val(run - 1, PLT_RUN_COPY, curLevel, endPos - strPos - 1);
+    *runBits = m_CABACEstimator->getEstFracBits() - runTypeBits;
+    break;
+  default:
+    assert(0);
+  }
+  assert(run >= 1);
+  double costPerPixel = (double)m_CABACEstimator->getEstFracBits() / (double)run;
+  return costPerPixel;
+}
+void IntraSearch::preCalcPLTIndex(CodingStructure& cs, Partitioner& partitioner, ComponentID compBegin, uint32_t numComp)
+{
+  CodingUnit &cu = *cs.getCU(partitioner.chType);
+  TransformUnit &tu = *cs.getTU(partitioner.chType);
+  const int  channelBitDepth_L = cs.sps->getBitDepth(CHANNEL_TYPE_LUMA);
+  const int  channelBitDepth_C = cs.sps->getBitDepth(CHANNEL_TYPE_CHROMA);
+  const int  pcmShiftRight_L = (channelBitDepth_L - PLT_ENCBITDEPTH);
+  const int  pcmShiftRight_C = (channelBitDepth_C - PLT_ENCBITDEPTH);
+
+  uint32_t height = cu.block(compBegin).height;
+  uint32_t width = cu.block(compBegin).width;
+
+  CPelBuf   orgBuf[3];
+  for (int comp = compBegin; comp < (compBegin + numComp); comp++)
+  {
+    CompArea  area = cu.blocks[comp];
+    if (m_pcEncCfg->getReshaper() && (cs.slice->getLmcsEnabledFlag() && m_pcReshape->getCTUFlag()))
+    {
+      orgBuf[comp] = cs.getPredBuf(area);
+    }
+    else
+    {
+      orgBuf[comp] = cs.getOrgBuf(area);
+    }
+  }
+
+  PelBuf   curPLTIdx = tu.getcurPLTIdx(compBegin);
+  int      errorLimit = numComp * g_paletteQuant[cu.qp];
+  uint32_t bestIdx = 0;
+  uint32_t scaleX = getComponentScaleX(COMPONENT_Cb, cs.sps->getChromaFormatIdc());
+  uint32_t scaleY = getComponentScaleY(COMPONENT_Cb, cs.sps->getChromaFormatIdc());
+  for (uint32_t y = 0; y < height; y++)
+  {
+    for (uint32_t x = 0; x < width; x++)
+    {
+      uint32_t pltIdx = 0;
+      uint32_t minError = MAX_UINT;
+      while (pltIdx < cu.curPLTSize[compBegin])
+      {
+        uint32_t absError = 0, pX, pY;
+        for (int comp = compBegin; comp < (compBegin + numComp); comp++)
+        {
+          pX = (comp > 0 && compBegin == COMPONENT_Y) ? (x >> scaleX) : x;
+          pY = (comp > 0 && compBegin == COMPONENT_Y) ? (y >> scaleY) : y;
+          int shift = (comp > 0) ? pcmShiftRight_C : pcmShiftRight_L;
+          absError += abs(cu.curPLT[comp][pltIdx] - orgBuf[comp].at(pX, pY)) >> shift;
+        }
+
+        if (absError < minError)
+        {
+          bestIdx = pltIdx;
+          minError = absError;
+          if (minError == 0)
+          {
+            break;
+          }
+        }
+        pltIdx++;
+      }
+      curPLTIdx.at(x, y) = bestIdx;
+      if (minError > errorLimit)
+      {
+        curPLTIdx.at(x, y) = cu.curPLTSize[compBegin];
+        cu.useEscape[compBegin] = true;
+        calcPixelPred(cs, partitioner, y, x, compBegin, numComp);
+      }
+    }
+  }
+}
+void IntraSearch::calcPixelPred(CodingStructure& cs, Partitioner& partitioner, uint32_t yPos, uint32_t xPos, ComponentID compBegin, uint32_t numComp)
+{
+  CodingUnit    &cu = *cs.getCU(partitioner.chType);
+  TransformUnit &tu = *cs.getTU(partitioner.chType);
+
+  CPelBuf   orgBuf[3];
+  for (int comp = compBegin; comp < (compBegin + numComp); comp++)
+  {
+    CompArea  area = cu.blocks[comp];
+    if (m_pcEncCfg->getReshaper() && (cs.slice->getLmcsEnabledFlag() && m_pcReshape->getCTUFlag()))
+    {
+      orgBuf[comp] = cs.getPredBuf(area);
+    }
+    else
+    {
+      orgBuf[comp] = cs.getOrgBuf(area);
+    }
+  }
+
+  int qp[3];
+  int qpRem[3];
+  int qpPer[3];
+  int quantiserScale[3];
+  int quantiserRightShift[3];
+  int rightShiftOffset[3];
+  int InvquantiserRightShift[3];
+  int add[3];
+  for (uint32_t ch = compBegin; ch < (compBegin + numComp); ch++)
+  {
+    QpParam cQP(tu, ComponentID(ch));
+#if JVET_O0919_TS_MIN_QP
+    qp[ch] = cQP.Qp(false);
+#else
+    qp[ch] = cQP.Qp;
+#endif
+    qpRem[ch] = qp[ch] % 6;
+    qpPer[ch] = qp[ch] / 6;
+    quantiserScale[ch] = g_quantScales[0][qpRem[ch]];
+    quantiserRightShift[ch] = QUANT_SHIFT + qpPer[ch];
+    rightShiftOffset[ch] = 1 << (quantiserRightShift[ch] - 1);
+    InvquantiserRightShift[ch] = IQUANT_SHIFT;
+    add[ch] = 1 << (InvquantiserRightShift[ch] - 1);
+  }
+
+  uint32_t scaleX = getComponentScaleX(COMPONENT_Cb, cs.sps->getChromaFormatIdc());
+  uint32_t scaleY = getComponentScaleY(COMPONENT_Cb, cs.sps->getChromaFormatIdc());
+  for (uint32_t ch = compBegin; ch < (compBegin + numComp); ch++)
+  {
+    const int channelBitDepth = cu.cs->sps->getBitDepth(toChannelType((ComponentID)ch));
+    CompArea  area = cu.blocks[ch];
+    PelBuf    recBuf = cs.getRecoBuf(area);
+    PLTescapeBuf escapeValue = tu.getescapeValue((ComponentID)ch);
+    if (compBegin != COMPONENT_Y || ch == 0)
+    {
+      escapeValue.at(xPos, yPos) = TCoeff(std::max<int>(0, ((orgBuf[ch].at(xPos, yPos) * quantiserScale[ch] + rightShiftOffset[ch]) >> quantiserRightShift[ch])));
+      assert(escapeValue.at(xPos, yPos) < (1 << (channelBitDepth + 1)));
+      recBuf.at(xPos, yPos) = (((escapeValue.at(xPos, yPos)*g_invQuantScales[0][qpRem[ch]]) << qpPer[ch]) + add[ch]) >> InvquantiserRightShift[ch];
+      recBuf.at(xPos, yPos) = Pel(ClipBD<int>(recBuf.at(xPos, yPos), channelBitDepth));//to be checked
+    }
+    else if (compBegin == COMPONENT_Y && ch > 0 && yPos % (1 << scaleY) == 0 && xPos % (1 << scaleX) == 0)
+    {
+      uint32_t yPosC = yPos >> scaleY;
+      uint32_t xPosC = xPos >> scaleX;
+      escapeValue.at(xPosC, yPosC) = TCoeff(std::max<int>(0, ((orgBuf[ch].at(xPosC, yPosC) * quantiserScale[ch] + rightShiftOffset[ch]) >> quantiserRightShift[ch])));
+      assert(escapeValue.at(xPosC, yPosC) < (1 << (channelBitDepth + 1)));
+      recBuf.at(xPosC, yPosC) = (((escapeValue.at(xPosC, yPosC)*g_invQuantScales[0][qpRem[ch]]) << qpPer[ch]) + add[ch]) >> InvquantiserRightShift[ch];
+      recBuf.at(xPosC, yPosC) = Pel(ClipBD<int>(recBuf.at(xPosC, yPosC), channelBitDepth));//to be checked
+    }
+  }
+}
+void IntraSearch::derivePLTLossy(CodingStructure& cs, Partitioner& partitioner, ComponentID compBegin, uint32_t numComp)
+{
+  CodingUnit &cu = *cs.getCU(partitioner.chType);
+  const int channelBitDepth_L = cs.sps->getBitDepth(CHANNEL_TYPE_LUMA);
+  const int channelBitDepth_C = cs.sps->getBitDepth(CHANNEL_TYPE_CHROMA);
+  const int pcmShiftRight_L = (channelBitDepth_L - PLT_ENCBITDEPTH);
+  const int pcmShiftRight_C = (channelBitDepth_C - PLT_ENCBITDEPTH);
+
+  uint32_t height = cu.block(compBegin).height;
+  uint32_t width = cu.block(compBegin).width;
+
+  CPelBuf   orgBuf[3];
+  for (int comp = compBegin; comp < (compBegin + numComp); comp++)
+  {
+    CompArea  area = cu.blocks[comp];
+    if (m_pcEncCfg->getReshaper() && (cs.slice->getLmcsEnabledFlag() && m_pcReshape->getCTUFlag()))
+    {
+      orgBuf[comp] = cs.getPredBuf(area);
+    }
+    else
+    {
+      orgBuf[comp] = cs.getOrgBuf(area);
+    }
+  }
+
+  int errorLimit = g_paletteQuant[cu.qp];
+  uint32_t totalSize = height*width;
+  SortingElement *pelList = new SortingElement[totalSize];
+  SortingElement  element;
+  SortingElement *pelListSort = new SortingElement[MAXPLTSIZE + 1];
+  uint32_t dictMaxSize = MAXPLTSIZE; 
+  uint32_t idx = 0;
+  int last = -1;
+
+  uint32_t scaleX = getComponentScaleX(COMPONENT_Cb, cs.sps->getChromaFormatIdc());
+  uint32_t scaleY = getComponentScaleY(COMPONENT_Cb, cs.sps->getChromaFormatIdc());
+  for (uint32_t y = 0; y < height; y++)
+  {
+    for (uint32_t x = 0; x < width; x++)
+    {
+      uint32_t org[3], pX, pY;
+      for (int comp = compBegin; comp < (compBegin + numComp); comp++)
+      {
+        pX = (comp > 0 && compBegin == COMPONENT_Y) ? (x >> scaleX) : x;
+        pY = (comp > 0 && compBegin == COMPONENT_Y) ? (y >> scaleY) : y;
+        org[comp] = orgBuf[comp].at(pX, pY);
+      }
+      element.setAll(org, compBegin, numComp);
+      int besti = last, bestSAD = (last == -1) ? MAX_UINT : pelList[last].getSAD(element, cs.sps->getBitDepths(), compBegin, numComp);
+      if (bestSAD)
+      {
+        for (int i = idx - 1; i >= 0; i--)
+        {
+          uint32_t sad = pelList[i].getSAD(element, cs.sps->getBitDepths(), compBegin, numComp);
+          if (sad < bestSAD)
+          {
+            bestSAD = sad;
+            besti = i;
+            if (!sad) break;
+          }
+        }
+      }
+      if (besti >= 0 && pelList[besti].almostEqualData(element, errorLimit, cs.sps->getBitDepths(), compBegin, numComp))
+      {
+        pelList[besti].addElement(element, compBegin, numComp);
+        last = besti;
+      }
+      else
+      {
+        pelList[idx].copyDataFrom(element, compBegin, numComp);
+        pelList[idx].setCnt(1);
+        last = idx;
+        idx++;
+      }
+    }
+  }
+
+  for (int i = 0; i < dictMaxSize; i++)
+  {
+    pelListSort[i].setCnt(0);
+    pelListSort[i].resetAll(compBegin, numComp);
+  }
+
+  //bubble sorting
+  dictMaxSize = 1;
+  for (int i = 0; i < idx; i++)
+  {
+    if (pelList[i].getCnt() > pelListSort[dictMaxSize - 1].getCnt())
+    {
+      int j;
+      for (j = dictMaxSize; j > 0; j--)
+      {
+        if (pelList[i].getCnt() > pelListSort[j - 1].getCnt() )
+        {
+          pelListSort[j].copyAllFrom(pelListSort[j - 1], compBegin, numComp);
+          dictMaxSize = std::min(dictMaxSize + 1, (uint32_t)MAXPLTSIZE);
+        }
+        else
+        {
+          break;
+        }
+      }
+      pelListSort[j].copyAllFrom(pelList[i], compBegin, numComp);
+    }
+  }
+
+  uint32_t paletteSize = 0;
+  uint64_t numColorBits = 0;
+  for (int comp = compBegin; comp < (compBegin + numComp); comp++)
+  {
+    numColorBits += (comp > 0) ? channelBitDepth_C : channelBitDepth_L;
+  }
+
+  double bitCost = m_pcRdCost->getLambda()*numColorBits;
+  for (int i = 0; i < MAXPLTSIZE; i++)
+  {
+    if (pelListSort[i].getCnt())
+    {
+      int half = pelListSort[i].getCnt() >> 1;
+      for (int comp = compBegin; comp < (compBegin + numComp); comp++)
+      {
+        cu.curPLT[comp][paletteSize] = (pelListSort[i].getSumData(comp) + half) / pelListSort[i].getCnt();
+      }
+
+      int best = -1;
+      if (errorLimit)
+      {
+        double pal[MAX_NUM_COMPONENT], err = 0.0, bestCost = 0.0;
+        for (int comp = compBegin; comp < (compBegin + numComp); comp++)
+        {
+          const int shift = (comp > 0) ? pcmShiftRight_C : pcmShiftRight_L;
+          pal[comp] = pelListSort[i].getSumData(comp) / (double)pelListSort[i].getCnt();
+          err = pal[comp] - cu.curPLT[comp][paletteSize];
+          bestCost += (err*err) / (1 << (2 * shift));
+        }
+        bestCost = bestCost * pelListSort[i].getCnt() + bitCost;
+
+        for (int t = 0; t < cs.prevPLT.curPLTSize[compBegin]; t++)
+        {
+          double cost = 0.0;
+          for (int comp = compBegin; comp < (compBegin + numComp); comp++)
+          {
+            const int shift = (comp > 0) ? pcmShiftRight_C : pcmShiftRight_L;
+            err = pal[comp] - cs.prevPLT.curPLT[comp][t];
+            cost += (err*err) / (1 << (2 * shift));
+          }
+          cost *= pelListSort[i].getCnt();
+          if (cost < bestCost)
+          {
+            best = t;
+            bestCost = cost;
+          }
+        }
+        if (best != -1)
+        {
+          for (int comp = compBegin; comp < (compBegin + numComp); comp++)
+          {
+            cu.curPLT[comp][paletteSize] = cs.prevPLT.curPLT[comp][best];
+          }
+        }
+      }
+
+      bool duplicate = false;
+      if (pelListSort[i].getCnt() == 1 && best == -1)
+      {
+        duplicate = true;
+      }
+      else
+      {
+        for (int t = 0; t<paletteSize; t++)
+        {
+          bool duplicateTmp = true;
+          for (int comp = compBegin; comp < (compBegin + numComp); comp++)
+          {
+            duplicateTmp = duplicateTmp && (cu.curPLT[comp][paletteSize] == cu.curPLT[comp][t]);
+          }
+          if (duplicateTmp)
+          {
+            duplicate = true;
+            break;
+          }
+        }
+      }
+      if (!duplicate) paletteSize++;
+    }
+    else
+    {
+      break;
+    }
+  }
+  cu.curPLTSize[compBegin] = paletteSize;
+
+  delete[] pelList;
+  delete[] pelListSort;
+}
+#endif
 // -------------------------------------------------------------------------------------------------------------------
 // Intra search
 // -------------------------------------------------------------------------------------------------------------------
@@ -1553,9 +2123,15 @@ void IntraSearch::xEncIntraHeader( CodingStructure &cs, Partitioner &partitioner
     // CU header
     if( isFirst )
     {
+#if JVET_O0119_BASE_PALETTE_444
+    if ((!cs.slice->isIntra() || cs.slice->getSPS()->getIBCFlag() || cs.slice->getSPS()->getPLTMode())
+    && cu.Y().valid()
+    )
+#else
       if ((!cs.slice->isIntra() || cs.slice->getSPS()->getIBCFlag())
         && cu.Y().valid()
         )
+#endif
       {
         if( cs.pps->getTransquantBypassEnabledFlag() )
         {
@@ -1564,6 +2140,12 @@ void IntraSearch::xEncIntraHeader( CodingStructure &cs, Partitioner &partitioner
         m_CABACEstimator->cu_skip_flag( cu );
         m_CABACEstimator->pred_mode   ( cu );
       }
+#if JVET_O0119_BASE_PALETTE_444
+    if (CU::isPLT(cu))
+    {
+      return;
+    }
+#endif
       m_CABACEstimator->bdpcm_mode  ( cu, ComponentID(partitioner.chType) );
       if( CU::isIntra(cu) )
       {
diff --git a/source/Lib/EncoderLib/IntraSearch.h b/source/Lib/EncoderLib/IntraSearch.h
index ee90779e71b7f02e2c4775ea76a5e97d0688de27..86d2d5bbdf923233315b51596c6da1a5402ba356 100644
--- a/source/Lib/EncoderLib/IntraSearch.h
+++ b/source/Lib/EncoderLib/IntraSearch.h
@@ -56,9 +56,113 @@
 // ====================================================================================================================
 // Class definition
 // ====================================================================================================================
-
 class EncModeCtrl;
 
+#if JVET_O0119_BASE_PALETTE_444
+enum PLTScanMode
+{
+  PLT_SCAN_HORTRAV = 0,
+  PLT_SCAN_VERTRAV = 1,
+  NUM_PLT_SCAN = 2
+};
+class SortingElement
+{
+public:
+  inline bool operator<(const SortingElement &other) const
+  {
+    return cnt > other.cnt;
+  }
+  SortingElement() {
+    cnt = shift = lastCnt = 0;
+    data[0] = data[1] = data[2] = 0;
+    sumData[0] = sumData[1] = sumData[2] = 0;
+  }
+  uint32_t  getCnt() const        { return cnt; }
+  void      setCnt(uint32_t val)  { cnt = val; }
+  int       getSumData (int id) const   { return sumData[id]; }
+
+  void resetAll(ComponentID compBegin, uint32_t numComp) 
+  {
+    shift = lastCnt = 0;
+    for (int ch = compBegin; ch < (compBegin + numComp); ch++)
+    {
+      data[ch] = 0;
+      sumData[ch] = 0;
+    }
+  }
+  void setAll(uint32_t* ui, ComponentID compBegin, uint32_t numComp) 
+  {
+    for (int ch = compBegin; ch < (compBegin + numComp); ch++)
+    {
+      data[ch] = ui[ch];
+    }
+  }
+  bool almostEqualData(SortingElement element, int errorLimit, const BitDepths& bitDepths, ComponentID compBegin, uint32_t numComp)
+  {
+    bool almostEqual = true;
+    for (int comp = compBegin; comp < (compBegin + numComp); comp++)
+    {
+      ChannelType chType = (comp > 0) ? CHANNEL_TYPE_CHROMA : CHANNEL_TYPE_LUMA;
+      if ((std::abs(data[comp] - element.data[comp]) >> (bitDepths.recon[chType] - PLT_ENCBITDEPTH)) > errorLimit)
+      {
+        almostEqual = false;
+        break;
+      }
+    }
+    return almostEqual;
+  }
+  uint32_t getSAD(SortingElement element, const BitDepths& bitDepths, ComponentID compBegin, uint32_t numComp)
+  {
+    uint32_t sumAd = 0;
+    for (int comp = compBegin; comp < (compBegin + numComp); comp++)
+    {
+      ChannelType chType = (comp > 0) ? CHANNEL_TYPE_CHROMA : CHANNEL_TYPE_LUMA;
+      sumAd += (std::abs(data[comp] - element.data[comp]) >> (bitDepths.recon[chType] - PLT_ENCBITDEPTH));
+    }
+    return sumAd;
+  }
+  void copyDataFrom(SortingElement element, ComponentID compBegin, uint32_t numComp) 
+  {
+    for (int comp = compBegin; comp < (compBegin + numComp); comp++)
+    {
+      data[comp] = element.data[comp];
+      sumData[comp] = data[comp];
+    }
+    shift = 0; lastCnt = 1;
+  }
+  void copyAllFrom(SortingElement element, ComponentID compBegin, uint32_t numComp) 
+  {
+    copyDataFrom(element, compBegin, numComp);
+    cnt = element.cnt;
+    for (int comp = compBegin; comp < (compBegin + numComp); comp++)
+    {
+      sumData[comp] = element.sumData[comp];
+    }
+    lastCnt = element.lastCnt; shift = element.shift;
+  }
+  void addElement(const SortingElement& element, ComponentID compBegin, uint32_t numComp)
+  {
+    cnt++;
+    for (int i = compBegin; i<(compBegin + numComp); i++)
+    {
+      sumData[i] += element.data[i];
+    }
+    if (cnt>1 && cnt == 2 * lastCnt)
+    {
+      uint32_t rnd = 1 << shift;
+      shift++;
+      for (int i = compBegin; i<(compBegin + numComp); i++)
+      {
+        data[i] = (sumData[i] + rnd) >> shift;
+      }
+      lastCnt = cnt;
+    }
+  }
+private: 
+  uint32_t cnt;
+  int shift, lastCnt, data[3], sumData[3];
+};
+#endif
 /// encoder search class
 class IntraSearch : public IntraPrediction, CrossComponentPrediction
 {
@@ -277,6 +381,10 @@ public:
   bool estIntraPredLumaQT         ( CodingUnit &cu, Partitioner& pm, const double bestCostSoFar  = MAX_DOUBLE, bool mtsCheckRangeFlag = false, int mtsFirstCheckId = 0, int mtsLastCheckId = 0, bool moreProbMTSIdxFirst = false );
   void estIntraPredChromaQT       ( CodingUnit &cu, Partitioner& pm, const double maxCostAllowed = MAX_DOUBLE );
   void IPCMSearch                 (CodingStructure &cs, Partitioner& partitioner);
+#if JVET_O0119_BASE_PALETTE_444
+  void PLTSearch                  ( CodingStructure &cs, Partitioner& partitioner, ComponentID compBegin, uint32_t numComp);
+  void deriveRunAndCalcBits       ( CodingStructure& cs, Partitioner& partitioner, ComponentID compBegin, uint32_t numComp, PLTScanMode pltScanMode, uint64_t& bits);
+#endif
   uint64_t xFracModeBitsIntra     (PredictionUnit &pu, const uint32_t &uiMode, const ChannelType &compID);
   void invalidateBestModeCost     () { for( int i = 0; i < NUM_LFNST_NUM_PER_SET; i++ ) m_bestModeCostValid[ i ] = false; };
 
@@ -316,7 +424,13 @@ protected:
   void reduceHadCandList(static_vector<T, N>& candModeList, static_vector<double, N>& candCostList, int& numModesForFullRD, const double thresholdHadCost, const double thresholdHadCostConv);
 
   double m_bestCostNonMip;
-
+#if JVET_O0119_BASE_PALETTE_444
+  void   deriveRun(CodingStructure &cs, Partitioner& partitioner, ComponentID compBegin);
+  double getRunBits(const CodingUnit&  cu, uint32_t run, uint32_t strPos, PLTRunMode paletteRunMode, uint64_t* indexBits, uint64_t* runBits, ComponentID compBegin);
+  void   derivePLTLossy(CodingStructure& cs, Partitioner& partitioner, ComponentID compBegin, uint32_t numComp);
+  void   calcPixelPred(CodingStructure& cs, Partitioner& partitioner, uint32_t yPos, uint32_t xPos, ComponentID compBegin, uint32_t numComp);
+  void   preCalcPLTIndex(CodingStructure& cs, Partitioner& partitioner, ComponentID compBegin, uint32_t numComp);
+#endif
 #if JVET_O0502_ISP_CLEANUP
   void xGetNextISPMode                    ( ModeInfo& modeInfo, const ModeInfo* lastMode, const Size cuSize );
   void xFindAlreadyTestedNearbyIntraModes ( int currentIntraMode, int* leftIntraMode, int* rightIntraMode, ISPType ispOption, int windowSize );
diff --git a/source/Lib/EncoderLib/VLCWriter.cpp b/source/Lib/EncoderLib/VLCWriter.cpp
index 181220a6452d95136b839c7b0b146e134b344022..5e6eaa0cc1b498ea19092b77ff2aaa69817c1e03 100644
--- a/source/Lib/EncoderLib/VLCWriter.cpp
+++ b/source/Lib/EncoderLib/VLCWriter.cpp
@@ -938,6 +938,12 @@ void HLSWriter::codeSPS( const SPS* pcSPS )
 #endif
   }
   WRITE_FLAG( pcSPS->getUseGBi() ? 1 : 0,                                                      "gbi_flag" );
+#if JVET_O0119_BASE_PALETTE_444
+  if (pcSPS->getChromaFormatIdc() == CHROMA_444)
+  {
+    WRITE_FLAG(pcSPS->getPLTMode() ? 1 : 0, "plt_flag");
+  }
+#endif
   WRITE_FLAG(pcSPS->getIBCFlag() ? 1 : 0,                                                      "ibc_flag");
 
   // KJS: sps_ciip_enabled_flag