diff --git a/source/App/EncoderApp/EncApp.cpp b/source/App/EncoderApp/EncApp.cpp
index e67f36bcabcec0b66e364ada09663d1972ebca4c..a387bbc6ec1f4fb24c3c458935b507c1545fdd4b 100644
--- a/source/App/EncoderApp/EncApp.cpp
+++ b/source/App/EncoderApp/EncApp.cpp
@@ -167,6 +167,15 @@ void EncApp::xInitLibCfg()
 #if X0038_LAMBDA_FROM_QP_CAPABILITY
   m_cEncLib.setIntraQPOffset                                     ( m_intraQPOffset );
   m_cEncLib.setLambdaFromQPEnable                                ( m_lambdaFromQPEnable );
+#endif
+#if JVET_O0650_SIGNAL_CHROMAQP_MAPPING_TABLE
+  m_cEncLib.setSameCQPTableForAllChroma(m_sameCQPTableForAllChroma);
+  for (int i = 0; i < (m_sameCQPTableForAllChroma ? 1 : 3); i++)
+  {
+    m_cEncLib.setNumPtsInCQPTableMinus1(i, (int)m_deltaInValMinus1[i].size() - 1);
+    m_cEncLib.setDeltaInValMinus1(i, m_deltaInValMinus1[i]);
+    m_cEncLib.setDeltaOutVal(i, m_deltaOutVal[i]);
+  }
 #endif
   m_cEncLib.setPad                                               ( m_aiPad );
 
diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp
index 4715e0e251954dc7a80656b10ca90e5a4a744c49..0671b0259286ed8999548fdce1ef74ed163ac017 100644
--- a/source/App/EncoderApp/EncAppCfg.cpp
+++ b/source/App/EncoderApp/EncAppCfg.cpp
@@ -714,7 +714,19 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
   SMultiValueInput<int>  cfg_lumaLeveltoDQPMappingLuma       (0, std::numeric_limits<int>::max(), 0, LUMA_LEVEL_TO_DQP_LUT_MAXSIZE, defaultLumaLevelTodQp_LumaChangePoints, sizeof(defaultLumaLevelTodQp_LumaChangePoints)/sizeof(int));
   uint32_t lumaLevelToDeltaQPMode;
 #endif
-
+#if JVET_O0650_SIGNAL_CHROMAQP_MAPPING_TABLE
+  //const int qpInVals[] = { 25, 33, 43 };
+  //const int qpOutVals[] = { 25, 32, 37 };
+  const int qpInVals[] = { 8, 25, 33, 43 };
+  const int qpOutVals[] = { 8, 26, 33, 38 };
+  SMultiValueInput<int> cfg_qpInValCb(0, 63, 0, 64, qpInVals, sizeof(qpInVals)/sizeof(int));
+  SMultiValueInput<int> cfg_qpOutValCb(0, 63, 0, 64, qpOutVals, sizeof(qpOutVals) / sizeof(int)); 
+  const int zeroVector[] = { 0 };
+  SMultiValueInput<int> cfg_qpInValCr(0, 63, 0, 64, zeroVector, 1);
+  SMultiValueInput<int> cfg_qpOutValCr(0, 63, 0, 64, zeroVector, 1);
+  SMultiValueInput<int> cfg_qpInValCbCr(0, 63, 0, 64, zeroVector, 1);
+  SMultiValueInput<int> cfg_qpOutValCbCr(0, 63, 0, 64, zeroVector, 1);
+#endif
   const uint32_t defaultInputKneeCodes[3]  = { 600, 800, 900 };
   const uint32_t defaultOutputKneeCodes[3] = { 100, 250, 450 };
   SMultiValueInput<uint32_t> cfg_kneeSEIInputKneePointValue      (1,  999, 0, 999, defaultInputKneeCodes,  sizeof(defaultInputKneeCodes )/sizeof(uint32_t));
@@ -1018,7 +1030,16 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
   ("LumaLevelToDeltaQPMappingLuma",                   cfg_lumaLeveltoDQPMappingLuma,  cfg_lumaLeveltoDQPMappingLuma, "Luma to Delta QP Mapping - luma thresholds")
   ("LumaLevelToDeltaQPMappingDQP",                    cfg_lumaLeveltoDQPMappingQP,  cfg_lumaLeveltoDQPMappingQP, "Luma to Delta QP Mapping - DQP values")
 #endif
-
+#if JVET_O0650_SIGNAL_CHROMAQP_MAPPING_TABLE
+  ("UseIdentityTableForNon420Chroma",                 m_useIdentityTableForNon420Chroma,                 true, "True: Indicates that 422/444 chroma uses identity chroma QP mapping tables; False: explicit Qp table may be specified in config")
+  ("SameCQPTablesForAllChroma",                       m_sameCQPTableForAllChroma,                        true, "0: Different tables for Cb, Cr and joint Cb-Cr components, 1 (default): Same tables for all three chroma components")
+  ("QpInValCb",                                       cfg_qpInValCb,                            cfg_qpInValCb, "Input coordinates for the QP table for Cb component")
+  ("QpOutValCb",                                      cfg_qpOutValCb,                          cfg_qpOutValCb, "Output coordinates for the QP table for Cb component")
+  ("QpInValCr",                                       cfg_qpInValCr,                            cfg_qpInValCr, "Input coordinates for the QP table for Cr component")
+  ("QpOutValCr",                                      cfg_qpOutValCr,                          cfg_qpOutValCr, "Output coordinates for the QP table for Cr component")
+  ("QpInValCbCr",                                     cfg_qpInValCbCr,                        cfg_qpInValCbCr, "Input coordinates for the QP table for joint Cb-Cr component")
+  ("QpOutValCbCr",                                    cfg_qpOutValCbCr,                      cfg_qpOutValCbCr, "Output coordinates for the QP table for joint Cb-Cr component")
+#endif
   ("CbQpOffset,-cbqpofs",                             m_cbQpOffset,                                         0, "Chroma Cb QP Offset")
   ("CrQpOffset,-crqpofs",                             m_crQpOffset,                                         0, "Chroma Cr QP Offset")
   ("CbQpOffsetDualTree",                              m_cbQpOffsetDualTree,                                 0, "Chroma Cb QP Offset for dual tree")
@@ -1907,6 +1928,43 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
     }
   }
 #endif
+#if JVET_O0650_SIGNAL_CHROMAQP_MAPPING_TABLE
+
+  CHECK(cfg_qpInValCb.values.size() != cfg_qpOutValCb.values.size(), "Chroma QP table for Cb is incomplete.");
+  CHECK(cfg_qpInValCr.values.size() != cfg_qpOutValCr.values.size(), "Chroma QP table for Cr is incomplete.");
+  CHECK(cfg_qpInValCbCr.values.size() != cfg_qpOutValCbCr.values.size(), "Chroma QP table for CbCr is incomplete.");
+  if (m_useIdentityTableForNon420Chroma && m_chromaFormatIDC != CHROMA_420) 
+  {
+    m_sameCQPTableForAllChroma = true;
+    cfg_qpInValCb.values = { 0 };
+    cfg_qpInValCr.values = { 0 };
+    cfg_qpInValCbCr.values = { 0 };
+  }
+  m_deltaInValMinus1[0].resize(cfg_qpInValCb.values.size());
+  m_deltaOutVal[0].resize(cfg_qpOutValCb.values.size());
+  for (int i = 0; i < cfg_qpInValCb.values.size(); i++)
+  {
+    m_deltaInValMinus1[0][i] = (i == 0) ? cfg_qpInValCb.values[i] + 6*(m_internalBitDepth[CHANNEL_TYPE_CHROMA]-8) : cfg_qpInValCb.values[i] - cfg_qpInValCb.values[i - 1] - 1;
+    m_deltaOutVal[0][i] = (i == 0) ? cfg_qpOutValCb.values[i] + 6*(m_internalBitDepth[CHANNEL_TYPE_CHROMA]-8) : cfg_qpOutValCb.values[i] - cfg_qpOutValCb.values[i - 1];
+  }
+  if (!m_sameCQPTableForAllChroma)
+  {
+    m_deltaInValMinus1[1].resize(cfg_qpInValCr.values.size());
+    m_deltaOutVal[1].resize(cfg_qpOutValCr.values.size());
+    for (int i = 0; i < cfg_qpInValCb.values.size(); i++)
+    {
+      m_deltaInValMinus1[1][i] = (i == 0) ? cfg_qpInValCr.values[i] : cfg_qpInValCr.values[i] - cfg_qpInValCr.values[i - 1] - 1;
+      m_deltaOutVal[1][i] = (i == 0) ? cfg_qpOutValCr.values[i] : cfg_qpOutValCr.values[i] - cfg_qpOutValCr.values[i - 1];
+    }
+    m_deltaInValMinus1[2].resize(cfg_qpInValCbCr.values.size());
+    m_deltaOutVal[2].resize(cfg_qpOutValCbCr.values.size());
+    for (int i = 0; i < cfg_qpInValCb.values.size(); i++)
+    {
+      m_deltaInValMinus1[2][i] = (i == 0) ? cfg_qpInValCbCr.values[i] : cfg_qpInValCbCr.values[i] - cfg_qpInValCbCr.values[i - 1] - 1;
+      m_deltaOutVal[2][i] = (i == 0) ? cfg_qpOutValCbCr.values[i] : cfg_qpOutValCbCr.values[i] - cfg_qpOutValCbCr.values[i - 1];
+    }
+  }
+#endif
 
 #if LUMA_ADAPTIVE_DEBLOCKING_FILTER_QP_OFFSET
   if ( m_LadfEnabed )
diff --git a/source/App/EncoderApp/EncAppCfg.h b/source/App/EncoderApp/EncAppCfg.h
index 417ae65e7325a997e190477d55feccd743d79e55..60d6f4897497de7a0e4b625628c3ad328f66eea8 100644
--- a/source/App/EncoderApp/EncAppCfg.h
+++ b/source/App/EncoderApp/EncAppCfg.h
@@ -202,6 +202,13 @@ protected:
   double    m_fQP;                                            ///< QP value of key-picture (floating point)
 #endif
   int       m_iQP;                                            ///< QP value of key-picture (integer)
+#if JVET_O0650_SIGNAL_CHROMAQP_MAPPING_TABLE
+  bool      m_useIdentityTableForNon420Chroma;
+  bool      m_sameCQPTableForAllChroma;
+  int       m_numPtsInCQPTableMinus1[MAX_NUM_CQP_MAPPING_TABLES];
+  std::vector<int>  m_deltaInValMinus1[MAX_NUM_CQP_MAPPING_TABLES];
+  std::vector<int>  m_deltaOutVal[MAX_NUM_CQP_MAPPING_TABLES];
+#endif
 #if X0038_LAMBDA_FROM_QP_CAPABILITY
   int       m_intraQPOffset;                                  ///< QP offset for intra slice (integer)
   bool      m_lambdaFromQPEnable;                             ///< enable flag for QP:lambda fix
diff --git a/source/Lib/CommonLib/ChromaFormat.h b/source/Lib/CommonLib/ChromaFormat.h
index 8be4be331e05168687d292ac87419a38c04be9d5..0400084e1fe7f02773ac735ed6be8b28f905e0e4 100644
--- a/source/Lib/CommonLib/ChromaFormat.h
+++ b/source/Lib/CommonLib/ChromaFormat.h
@@ -115,12 +115,12 @@ static inline int getTransformShift(const int channelBitDepth, const Size size,
 
 
 //------------------------------------------------
-
+#if !JVET_O0650_SIGNAL_CHROMAQP_MAPPING_TABLE
 static inline int getScaledChromaQP(int unscaledChromaQP, const ChromaFormat chFmt)
 {
   return g_aucChromaScale[chFmt][Clip3(0, (chromaQPMappingTableSize - 1), unscaledChromaQP)];
 }
-
+#endif
 
 //======================================================================================================================
 //Scaling lists  =======================================================================================================
diff --git a/source/Lib/CommonLib/LoopFilter.cpp b/source/Lib/CommonLib/LoopFilter.cpp
index f8ddd4958720f6dda130f789bcbdd84403dedd72..a4352e58d21294115a7511eb5aca79dd21fe3376 100644
--- a/source/Lib/CommonLib/LoopFilter.cpp
+++ b/source/Lib/CommonLib/LoopFilter.cpp
@@ -1273,6 +1273,10 @@ void LoopFilter::xEdgeFilterChroma(const CodingUnit& cu, const DeblockEdgeDir ed
         const int chromaQPOffset = pps.getQpOffset( ComponentID( chromaIdx + 1 ) );
         Pel* piTmpSrcChroma = (chromaIdx == 0) ? piTmpSrcCb : piTmpSrcCr;
 
+#if JVET_O0650_SIGNAL_CHROMAQP_MAPPING_TABLE
+        int iQP = Clip3(0, MAX_QP, ((cuP.qp + cuQ.qp + 1) >> 1) + chromaQPOffset);
+        iQP = sps.getMappedChromaQPValue(ComponentID(chromaIdx + 1), iQP);
+#else
         int iQP = ( ( cuP.qp + cuQ.qp + 1 ) >> 1 ) + chromaQPOffset;
         if (iQP >= chromaQPMappingTableSize)
         {
@@ -1289,6 +1293,7 @@ void LoopFilter::xEdgeFilterChroma(const CodingUnit& cu, const DeblockEdgeDir ed
         {
           iQP = getScaledChromaQP(iQP, sps.getChromaFormatIdc());
         }
+#endif
 
         const int iIndexTC = Clip3<int>(0, MAX_QP + DEFAULT_INTRA_TC_OFFSET, iQP + DEFAULT_INTRA_TC_OFFSET * (bS[chromaIdx] - 1) + (tcOffsetDiv2 << 1));
 #if JVET_O0159_10BITTCTABLE_DEBLOCKING
diff --git a/source/Lib/CommonLib/Quant.cpp b/source/Lib/CommonLib/Quant.cpp
index 71ca554f0148e2ad52516b588d51bfb7591be9d3..608c19b66fa0b548ce1f9bf47e66d8a993293ef9 100644
--- a/source/Lib/CommonLib/Quant.cpp
+++ b/source/Lib/CommonLib/Quant.cpp
@@ -63,23 +63,39 @@
 // ====================================================================================================================
 
 QpParam::QpParam(const int           qpy,
+#if JVET_O0650_SIGNAL_CHROMAQP_MAPPING_TABLE
+                 const ComponentID   compID,
+#else
                  const ChannelType   chType,
+#endif
                  const int           qpBdOffset,
 #if JVET_O0919_TS_MIN_QP
                  const int           minQpPrimeTsMinus4,
 #endif
                  const int           chromaQPOffset,
                  const ChromaFormat  chFmt,
-                 const int           dqp )
+                 const int           dqp 
+#if JVET_O0650_SIGNAL_CHROMAQP_MAPPING_TABLE
+              ,  const SPS           *sps
+#endif
+)
 {
   int baseQp;
-
+#if JVET_O0650_SIGNAL_CHROMAQP_MAPPING_TABLE
+  if (isLuma(compID))
+#else
   if(isLuma(chType))
+#endif
   {
     baseQp = qpy + qpBdOffset;
   }
   else
   {
+#if JVET_O0650_SIGNAL_CHROMAQP_MAPPING_TABLE
+    int qpi = Clip3(-qpBdOffset, MAX_QP, qpy);
+    baseQp = sps->getMappedChromaQPValue(compID, qpi);
+    baseQp = Clip3(-qpBdOffset, MAX_QP, baseQp + chromaQPOffset) + qpBdOffset;
+#else
     baseQp = Clip3( -qpBdOffset, (chromaQPMappingTableSize - 1), qpy + chromaQPOffset );
 
     if(baseQp < 0)
@@ -90,6 +106,7 @@ QpParam::QpParam(const int           qpy,
     {
       baseQp = getScaledChromaQP(baseQp, chFmt) + qpBdOffset;
     }
+#endif
   }
 
   baseQp = Clip3( 0, MAX_QP+qpBdOffset, baseQp + dqp );
@@ -147,10 +164,18 @@ QpParam::QpParam(const TransformUnit& tu, const ComponentID &compIDX, const int
   int dqp = 0;
 
 #if JVET_O0919_TS_MIN_QP
+#if JVET_O0650_SIGNAL_CHROMAQP_MAPPING_TABLE
+  *this = QpParam(QP <= -MAX_INT ? tu.cu->qp : QP, compID, tu.cs->sps->getQpBDOffset(toChannelType(compID)), tu.cs->sps->getMinQpPrimeTsMinus4(toChannelType(compID)), chromaQpOffset, tu.chromaFormat, dqp, tu.cs->sps);
+#else
   *this = QpParam(QP <= -MAX_INT ? tu.cu->qp : QP, toChannelType(compID), tu.cs->sps->getQpBDOffset(toChannelType(compID)), tu.cs->sps->getMinQpPrimeTsMinus4(toChannelType(compID)), chromaQpOffset, tu.chromaFormat, dqp);
+#endif
+#else
+#if JVET_O0650_SIGNAL_CHROMAQP_MAPPING_TABLE
+  *this = QpParam(QP <= -MAX_INT ? tu.cu->qp : QP, compID, tu.cs->sps->getQpBDOffset(toChannelType(compID)), chromaQpOffset, tu.chromaFormat, dqp, tu.cs->sps);
 #else
   *this = QpParam(QP <= -MAX_INT ? tu.cu->qp : QP, toChannelType(compID), tu.cs->sps->getQpBDOffset(toChannelType(compID)), chromaQpOffset, tu.chromaFormat, dqp);
 #endif
+#endif
 }
 
 
diff --git a/source/Lib/CommonLib/Quant.h b/source/Lib/CommonLib/Quant.h
index 16eba76cbc871781b021e5b6ebfe1909bc67c25f..eb36a1a4f80646d4731ba686f75a31f5eed9c622 100644
--- a/source/Lib/CommonLib/Quant.h
+++ b/source/Lib/CommonLib/Quant.h
@@ -84,14 +84,22 @@ struct QpParam
 private:
 
   QpParam(const int           qpy,
+#if JVET_O0650_SIGNAL_CHROMAQP_MAPPING_TABLE
+          const ComponentID   compID,
+#else
           const ChannelType   chType,
+#endif
           const int           qpBdOffset,
 #if JVET_O0919_TS_MIN_QP
           const int           minQpPrimeTsMinus4,
 #endif
           const int           chromaQPOffset,
           const ChromaFormat  chFmt,
-          const int           dqp );
+          const int           dqp 
+#if JVET_O0650_SIGNAL_CHROMAQP_MAPPING_TABLE
+        , const SPS           *sps
+#endif
+  );
 
 public:
 
diff --git a/source/Lib/CommonLib/Rom.cpp b/source/Lib/CommonLib/Rom.cpp
index e32c236eafd10652f87536d1831ca1c2808d9077..721e683b24a53970d6854862703be200a83a6fca 100644
--- a/source/Lib/CommonLib/Rom.cpp
+++ b/source/Lib/CommonLib/Rom.cpp
@@ -498,7 +498,7 @@ const int g_invQuantScales[2][SCALING_LIST_REM_NUM] = // can be represented as a
 //--------------------------------------------------------------------------------------------------
 //coefficients
 //--------------------------------------------------------------------------------------------------
-
+#if !JVET_O0650_SIGNAL_CHROMAQP_MAPPING_TABLE
 const uint8_t g_aucChromaScale[NUM_CHROMA_FORMAT][chromaQPMappingTableSize] =
 {
   //0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69
@@ -507,7 +507,7 @@ const uint8_t g_aucChromaScale[NUM_CHROMA_FORMAT][chromaQPMappingTableSize] =
   { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,63,63,63,63,63,63 },
   { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,63,63,63,63,63,63 }
 };
-
+#endif
 // ====================================================================================================================
 // Intra prediction
 // ====================================================================================================================
diff --git a/source/Lib/CommonLib/Rom.h b/source/Lib/CommonLib/Rom.h
index 877b947d770bd924ac19bc529bd2c5bef5b89cde..3bfe26e204d0077aabff6b31ca36553d5a40f5dd 100644
--- a/source/Lib/CommonLib/Rom.h
+++ b/source/Lib/CommonLib/Rom.h
@@ -82,14 +82,14 @@ extern const uint8_t g_intraMode65to33AngMapping[NUM_INTRA_MODE];
 extern const uint8_t g_mapMipToAngular65[3][MAX_NUM_MIP_MODE];
 extern const uint8_t g_mapAngular33ToMip[3][35];
 extern const int     g_sortedMipMpms    [3][NUM_MPM_MIP];
-
+#if !JVET_O0650_SIGNAL_CHROMAQP_MAPPING_TABLE
 // ====================================================================================================================
 // Luma QP to Chroma QP mapping
 // ====================================================================================================================
 static const int chromaQPMappingTableSize = (MAX_QP + 7);
 
 extern const uint8_t  g_aucChromaScale[NUM_CHROMA_FORMAT][chromaQPMappingTableSize];
-
+#endif
 
 // ====================================================================================================================
 // Scanning order & context mapping table
diff --git a/source/Lib/CommonLib/Slice.cpp b/source/Lib/CommonLib/Slice.cpp
index 58b18d44b594b38d2a3921df60aecb1379323df4..301f00fec11af493b5d31bfa73f003c4965c79e2 100644
--- a/source/Lib/CommonLib/Slice.cpp
+++ b/source/Lib/CommonLib/Slice.cpp
@@ -1476,6 +1476,11 @@ SPS::SPS()
 
   ::memset(m_ltRefPicPocLsbSps, 0, sizeof(m_ltRefPicPocLsbSps));
   ::memset(m_usedByCurrPicLtSPSFlag, 0, sizeof(m_usedByCurrPicLtSPSFlag));
+#if JVET_O0650_SIGNAL_CHROMAQP_MAPPING_TABLE
+  m_numPtsInCQPTableMinus1[0] = 0;
+  m_deltaInValMinus1[0] = { 0 };
+  m_deltaOutVal[0] = { 0 };
+#endif
 }
 
 SPS::~SPS()
@@ -1502,6 +1507,51 @@ void  SPS::createRPLList1(int numRPL)
 const int SPS::m_winUnitX[]={1,2,2,1};
 const int SPS::m_winUnitY[]={1,2,1,1};
 
+#if JVET_O0650_SIGNAL_CHROMAQP_MAPPING_TABLE
+void SPS::derivedChromaQPMappingTables()
+{
+  for (int i = 0; i < (getSameCQPTableForAllChromaFlag() ? 1 : 3); i++)
+  {
+    const int qpBdOffsetC = this->getQpBDOffset(CHANNEL_TYPE_CHROMA);
+    const int numPtsInCQPTableMinus1 = getNumPtsInCQPTableMinus1(i);
+    std::vector<int> qpInVal(numPtsInCQPTableMinus1 + 1), qpOutVal(numPtsInCQPTableMinus1 + 1);
+
+    qpInVal[0] = -qpBdOffsetC + getDeltaInValMinus1(i, 0);
+    qpOutVal[0] = -qpBdOffsetC + getDeltaOutVal(i, 0);
+    for (int j = 1; j <= getNumPtsInCQPTableMinus1(i); j++)
+    {
+      qpInVal[j] = qpInVal[j - 1] + getDeltaInValMinus1(i, j) + 1;
+      qpOutVal[j] = qpOutVal[j - 1] + getDeltaOutVal(i, j);
+    }
+
+    for (int j = 0; j <= getNumPtsInCQPTableMinus1(i); j++)
+    {
+      CHECK(qpInVal[j]  < -qpBdOffsetC || qpInVal[j]  > MAX_QP, "qpInVal out of range");
+      CHECK(qpOutVal[j] < -qpBdOffsetC || qpOutVal[j] > MAX_QP, "qpOutVal out of range");
+    }
+
+    m_chromaQPMappingTables[i][qpInVal[0]] = qpOutVal[0];
+    for (int k = qpInVal[0] - 1; k >= -qpBdOffsetC; k--)
+    {
+      m_chromaQPMappingTables[i][k] = Clip3(-qpBdOffsetC, MAX_QP, m_chromaQPMappingTables[i][k + 1] - 1);
+    }
+    for (int j = 0; j < numPtsInCQPTableMinus1; j++)
+    {
+      int sh = (getDeltaInValMinus1(i, j + 1) + 1 + 1) >> 1;
+      for (int k = qpInVal[j] + 1, m = 1; k <= qpInVal[j + 1]; k++, m++)
+      {
+        m_chromaQPMappingTables[i][k] = m_chromaQPMappingTables[i][qpInVal[j]]
+          + (getDeltaOutVal(i, j + 1) * m + sh) / (getDeltaInValMinus1(i, j + 1) + 1);
+      }
+    }
+    for (int k = qpInVal[numPtsInCQPTableMinus1]+1; k <= MAX_QP; k++)
+    {
+      m_chromaQPMappingTables[i][k] = Clip3(-qpBdOffsetC, MAX_QP, m_chromaQPMappingTables[i][k - 1] + 1);
+    }
+  }
+}
+#endif
+
 PPSRExt::PPSRExt()
 : m_log2MaxTransformSkipBlockSize      (2)
 , m_crossComponentPredictionEnabledFlag(false)
diff --git a/source/Lib/CommonLib/Slice.h b/source/Lib/CommonLib/Slice.h
index 79b3f2c91684d164d54e1ae47cb19f633c15f027..31b36debd178437fb1c2d076708deb1ef787d385 100644
--- a/source/Lib/CommonLib/Slice.h
+++ b/source/Lib/CommonLib/Slice.h
@@ -815,7 +815,7 @@ private:
   bool              m_MIP;
 #if JVET_O0650_SIGNAL_CHROMAQP_MAPPING_TABLE
   bool              m_sameCQPTableForAllChromaFlag;
-  int               m_numPtsInCQPTable[MAX_NUM_CQP_MAPPING_TABLES];
+  int               m_numPtsInCQPTableMinus1[MAX_NUM_CQP_MAPPING_TABLES];
   std::vector<int>  m_deltaInValMinus1[MAX_NUM_CQP_MAPPING_TABLES];
   std::vector<int>  m_deltaOutVal[MAX_NUM_CQP_MAPPING_TABLES];
   std::map<int,int> m_chromaQPMappingTables[MAX_NUM_CQP_MAPPING_TABLES];
@@ -1083,16 +1083,18 @@ public:
   void      setUseWPBiPred        ( bool b )                                        { m_useWeightedBiPred = b; }
 #endif
 #if JVET_O0650_SIGNAL_CHROMAQP_MAPPING_TABLE
-  bool      setSameCQPTableForAllChromaFlag (bool b)                                { m_sameCQPTableForAllChromaFlag = b;  }
+  void      setSameCQPTableForAllChromaFlag (bool b)                                { m_sameCQPTableForAllChromaFlag = b;  }
   bool      getSameCQPTableForAllChromaFlag()                             const     { return m_sameCQPTableForAllChromaFlag; }
-  bool      setNumPtsInCQPTableMinus1             (int tableIdx, int n)                   { m_numPtsInCQPTableMinus1[tableIdx] = n; }
-  bool      getNumPtsInCQPTableMinus1(int tableIdx)                             const     { return m_numPtsInCQPTableMinus1[tableIdx]; }
-  bool      setDeltaInValMinus1             (int tableIdx, int idx, int n)          { m_deltaInValMinus1[tableIdx][idx] = n; }
-  bool      getDeltaInValMinus1(int tableIdx, int idx)                    const     { return m_deltaInValMinus1[tableIdx][idx]; }
-  bool      setDeltaOutVal(int tableIdx, int idx, int n)                            { m_deltaOutVal[tableIdx][idx] = n; }
-  bool      getDeltaOutVal(int tableIdx, int idx)                         const     { return m_deltaOutVal[tableIdx][idx]; }
-
-  int       getMappedChromaQPValue          (ComponentID compID, const int qpVal)   { return m_chromaQPMappingTables[m_sameCQPTableForAllChromaFlag ? 0 : compID - 1][qpVal];  }
+  void      setNumPtsInCQPTableMinus1             (int tableIdx, int n)                   { m_numPtsInCQPTableMinus1[tableIdx] = n; }
+  int       getNumPtsInCQPTableMinus1(int tableIdx)                             const     { return m_numPtsInCQPTableMinus1[tableIdx]; }
+  void      setDeltaInValMinus1(int tableIdx, std::vector<int> &inVals) { m_deltaInValMinus1[tableIdx] = inVals; }
+  void      setDeltaInValMinus1             (int tableIdx, int idx, int n)          { m_deltaInValMinus1[tableIdx][idx] = n; }
+  int       getDeltaInValMinus1(int tableIdx, int idx)                    const     { return m_deltaInValMinus1[tableIdx][idx]; }
+  void      setDeltaOutVal(int tableIdx, std::vector<int> &outVals) { m_deltaOutVal[tableIdx] = outVals; }
+  void      setDeltaOutVal(int tableIdx, int idx, int n)                            { m_deltaOutVal[tableIdx][idx] = n; }
+  int       getDeltaOutVal(int tableIdx, int idx)                         const     { return m_deltaOutVal[tableIdx][idx]; }
+
+  int       getMappedChromaQPValue          (ComponentID compID, const int qpVal)  const  { return m_chromaQPMappingTables[m_sameCQPTableForAllChromaFlag ? 0 : (int)compID - 1].at(qpVal);  }
   void      derivedChromaQPMappingTables();
 #endif
 };
diff --git a/source/Lib/DecoderLib/VLCReader.cpp b/source/Lib/DecoderLib/VLCReader.cpp
index ee3c8b361fac33e1b1662760f743e897f4b8abe9..81be7ff9bf4d3d2bbcc04ac5fab9ac5a337334ba 100644
--- a/source/Lib/DecoderLib/VLCReader.cpp
+++ b/source/Lib/DecoderLib/VLCReader.cpp
@@ -1244,16 +1244,18 @@ void HLSyntaxReader::parseSPS(SPS* pcSPS)
   if (pcSPS->getChromaFormatIdc() != CHROMA_400)
   {
     READ_FLAG(uiCode, "same_qp_table_for_chroma");        pcSPS->setSameCQPTableForAllChromaFlag(uiCode);
-    for (int i = 0; i < pcSPS->getSameCQPTableForAllChromaFlag() ? 1 : 3; i++)
+    for (int i = 0; i < (pcSPS->getSameCQPTableForAllChromaFlag() ? 1 : 3); i++)
     {
-      int numPtsInTableMinus1 = 0;
-      READ_UVLC(uiCode, "num_points_in_qp_table_minus1"); pcSPS->setNumPtsInCQPTableMinus1(i, uiCode);
-
+      READ_UVLC(uiCode, "num_points_in_qp_table_minus1"); pcSPS->setNumPtsInCQPTableMinus1(i,uiCode);
+      std::vector<int> deltaInValMinus1(pcSPS->getNumPtsInCQPTableMinus1(i) + 1);
+      std::vector<int> deltaOutVal(pcSPS->getNumPtsInCQPTableMinus1(i) + 1);
       for (int j = 0; j <= pcSPS->getNumPtsInCQPTableMinus1(i); j++)
       {
-        READ_UVLC(uiCode, "delta_qp_in_val_minus1");  pcSPS->setDeltaInValMinus1(i, j, uiCode);
-        READ_UVLC(uiCode, "delta_qp_out_val");        pcSPS->setDeltaOutVal(i, j, uiCode);
+        READ_UVLC(uiCode, "delta_qp_in_val_minus1");  deltaInValMinus1[j] = uiCode; 
+        READ_UVLC(uiCode, "delta_qp_out_val");        deltaOutVal[j] = uiCode; 
       }
+      pcSPS->setDeltaInValMinus1(i, deltaInValMinus1);
+      pcSPS->setDeltaOutVal(i, deltaOutVal);
     }
     pcSPS->derivedChromaQPMappingTables();
   }
diff --git a/source/Lib/EncoderLib/EncCfg.h b/source/Lib/EncoderLib/EncCfg.h
index 240292bb808abeb7053bec27da00900fde4da9ee..1515a5f832b29310077b3b8640320c8b2ef5ab85 100644
--- a/source/Lib/EncoderLib/EncCfg.h
+++ b/source/Lib/EncoderLib/EncCfg.h
@@ -239,6 +239,12 @@ protected:
   int       m_numReorderPics[MAX_TLAYER];
 
   int       m_iQP;                              //  if (AdaptiveQP == OFF)
+#if JVET_O0650_SIGNAL_CHROMAQP_MAPPING_TABLE
+  bool      m_sameCQPTableForAllChroma;
+  int       m_numPtsInCQPTableMinus1[MAX_NUM_CQP_MAPPING_TABLES];
+  std::vector<int>  m_deltaInValMinus1[MAX_NUM_CQP_MAPPING_TABLES];
+  std::vector<int>  m_deltaOutVal[MAX_NUM_CQP_MAPPING_TABLES];
+#endif
 #if X0038_LAMBDA_FROM_QP_CAPABILITY
   int       m_intraQPOffset;                    ///< QP offset for intra slice (integer)
   int       m_lambdaFromQPEnable;               ///< enable lambda derivation from QP
@@ -780,6 +786,18 @@ public:
 #if X0038_LAMBDA_FROM_QP_CAPABILITY
   void      setIntraQPOffset                ( int   i )         { m_intraQPOffset = i; }
   void      setLambdaFromQPEnable           ( bool  b )         { m_lambdaFromQPEnable = b; }
+#endif
+#if JVET_O0650_SIGNAL_CHROMAQP_MAPPING_TABLE
+  void      setSameCQPTableForAllChroma(bool b) { m_sameCQPTableForAllChroma = b; }
+  bool      getSameCQPTableForAllChroma()                             const { return m_sameCQPTableForAllChroma; }
+  void      setNumPtsInCQPTableMinus1(int tableIdx, int n) { m_numPtsInCQPTableMinus1[tableIdx] = n; }
+  int       getNumPtsInCQPTableMinus1(int tableIdx)                             const { return m_numPtsInCQPTableMinus1[tableIdx]; }
+  void      setDeltaInValMinus1(int tableIdx, std::vector<int> &inVals) { m_deltaInValMinus1[tableIdx] = inVals; }
+  void      setDeltaInValMinus1(int tableIdx, int idx, int n) { m_deltaInValMinus1[tableIdx][idx] = n; }
+  int       getDeltaInValMinus1(int tableIdx, int idx)                    const { return m_deltaInValMinus1[tableIdx][idx]; }
+  void      setDeltaOutVal(int tableIdx, std::vector<int> &outVals) { m_deltaOutVal[tableIdx] = outVals; }
+  void      setDeltaOutVal(int tableIdx, int idx, int n) { m_deltaOutVal[tableIdx][idx] = n; }
+  int       getDeltaOutVal(int tableIdx, int idx)                         const { return m_deltaOutVal[tableIdx][idx]; }
 #endif
   void      setPad                          ( int*  iPad                   )      { for ( int i = 0; i < 2; i++ ) m_aiPad[i] = iPad[i]; }
 
diff --git a/source/Lib/EncoderLib/EncLib.cpp b/source/Lib/EncoderLib/EncLib.cpp
index dd743359da715987bb89226d26cce6ec2fc741fc..c15d7c4bac55cb34e3302262f2c0d6d97e3147cd 100644
--- a/source/Lib/EncoderLib/EncLib.cpp
+++ b/source/Lib/EncoderLib/EncLib.cpp
@@ -1039,6 +1039,16 @@ void EncLib::xInitSPS(SPS &sps)
     sps.setLtRefPicPocLsbSps(k, 0);
     sps.setUsedByCurrPicLtSPSFlag(k, 0);
   }
+#if JVET_O0650_SIGNAL_CHROMAQP_MAPPING_TABLE
+  sps.setSameCQPTableForAllChromaFlag(m_sameCQPTableForAllChroma);
+  for (int i = 0; i < (m_sameCQPTableForAllChroma ? 1 : 3); i++)
+  {
+    sps.setNumPtsInCQPTableMinus1(i, (int)m_deltaInValMinus1[i].size() - 1);
+    sps.setDeltaInValMinus1(i, m_deltaInValMinus1[i]);
+    sps.setDeltaOutVal(i, m_deltaOutVal[i]);
+  }
+  sps.derivedChromaQPMappingTables();
+#endif
 
 #if U0132_TARGET_BITS_SATURATION
   if( getPictureTimingSEIEnabled() || getDecodingUnitInfoSEIEnabled() || getCpbSaturationEnabled() )
diff --git a/source/Lib/EncoderLib/EncSlice.cpp b/source/Lib/EncoderLib/EncSlice.cpp
index 1e75780bbd9b591b0458bf9c8f06a95d8b1aa350..2abbf9b1dc6b31b7885276bd8bd85c7194d00888 100644
--- a/source/Lib/EncoderLib/EncSlice.cpp
+++ b/source/Lib/EncoderLib/EncSlice.cpp
@@ -117,7 +117,11 @@ EncSlice::setUpLambda( Slice* slice, const double dLambda, int iQP)
   {
     const ComponentID compID = ComponentID( compIdx );
     int chromaQPOffset       = slice->getPPS()->getQpOffset( compID ) + slice->getSliceChromaQpDelta( compID );
+#if JVET_O0650_SIGNAL_CHROMAQP_MAPPING_TABLE
+    int qpc = slice->getSPS()->getMappedChromaQPValue(compID, iQP) + chromaQPOffset;
+#else
     int qpc                  = ( iQP + chromaQPOffset < 0 ) ? iQP : getScaledChromaQP( iQP + chromaQPOffset, m_pcCfg->getChromaFormatIdc() );
+#endif
     double tmpWeight         = pow( 2.0, ( iQP - qpc ) / 3.0 );  // takes into account of the chroma qp mapping and chroma qp Offset
     if( m_pcCfg->getDepQuantEnabledFlag() && !( m_pcCfg->getLFNST() ) )
     {
@@ -296,7 +300,11 @@ static int applyQPAdaptationChroma (Picture* const pcPic, Slice* const pcSlice,
         savedLumaQP = averageAdaptedLumaQP;
       } // savedLumaQP < 0
 
+#if JVET_O0650_SIGNAL_CHROMAQP_MAPPING_TABLE
+      const int lumaChromaMappingDQP = savedLumaQP - pcSlice->getSPS()->getMappedChromaQPValue(compID, savedLumaQP);
+#else
       const int lumaChromaMappingDQP = savedLumaQP - getScaledChromaQP (savedLumaQP, pcEncCfg->getChromaFormatIdc());
+#endif
 
       optSliceChromaQpOffset[comp-1] = std::min (3 + lumaChromaMappingDQP, adaptChromaQPOffset + lumaChromaMappingDQP);
     }
diff --git a/source/Lib/EncoderLib/VLCWriter.cpp b/source/Lib/EncoderLib/VLCWriter.cpp
index 27f1230584248a5f2ac7eef7385399dc198bdafe..bf4fdc5e98cadcd4ea3d917849d0b39f771dc071 100644
--- a/source/Lib/EncoderLib/VLCWriter.cpp
+++ b/source/Lib/EncoderLib/VLCWriter.cpp
@@ -844,14 +844,13 @@ void HLSWriter::codeSPS( const SPS* pcSPS )
   if (pcSPS->getChromaFormatIdc() != CHROMA_400)
   {
     WRITE_FLAG(pcSPS->getSameCQPTableForAllChromaFlag(), "same_qp_table_for_chroma");
-    for (int i = 0; i < pcSPS->getSameCQPTableForAllChromaFlag() ? 1 : 3; i++)
+    for (int i = 0; i < (pcSPS->getSameCQPTableForAllChromaFlag() ? 1 : 3); i++)
     {
-      WRITE_UVLC(pcSPS->getNumPtsInCQPTableMinus[i], "num_points_in_qp_table_minus1");
-
-      for (int j = 0; j <= pcSPS->getNumPtsInCQPTableMinus[i]; j++)
+      WRITE_UVLC(pcSPS->getNumPtsInCQPTableMinus1(i), "num_points_in_qp_table_minus1"); 
+      for (int j = 0; j <= pcSPS->getNumPtsInCQPTableMinus1(i); j++)
       {
-        WRITE_UVLC(pcSPS->getDeltaInValMinus1[i][j], "delta_qp_in_val_minus1");
-        WRITE_UVLC(pcSPS->getDeltaOutVal[i][j], "delta_qp_out_val");
+        WRITE_UVLC(pcSPS->getDeltaInValMinus1(i,j),  "delta_qp_in_val_minus1");
+        WRITE_UVLC(pcSPS->getDeltaOutVal(i, j),       "delta_qp_out_val");
       }
     }
   }