diff --git a/cfg/encoder_intra_vtm.cfg b/cfg/encoder_intra_vtm.cfg
index 63f1575885fcb22d75a55a17d30f43f5bb6f7de9..3e8c0359aaca510e07a7ac40ffcc935c2b1ff8c3 100644
--- a/cfg/encoder_intra_vtm.cfg
+++ b/cfg/encoder_intra_vtm.cfg
@@ -111,6 +111,7 @@ LMCSSignalType               : 0      # Input signal type: 0:SDR, 1:HDR-PQ, 2:HD
 LMCSUpdateCtrl               : 1      # LMCS model update control: 0:RA, 1:AI, 2:LDB/LDP
 MIP                          : 1
 JointCbCr                    : 1      # joint coding of chroma residuals (if available): 0: disable, 1: enable
+ChromaTS                     : 1
 
 # Fast tools
 PBIntraFast                  : 1
diff --git a/cfg/encoder_lowdelay_P_vtm.cfg b/cfg/encoder_lowdelay_P_vtm.cfg
index 9ec4a5d13cf00e9d8e1da1bfc47971a6f37dc64c..b869fd5292eef9b34290351c5eb17f5be4323942 100644
--- a/cfg/encoder_lowdelay_P_vtm.cfg
+++ b/cfg/encoder_lowdelay_P_vtm.cfg
@@ -133,6 +133,7 @@ MIP                          : 1
 JointCbCr                    : 1      # joint coding of chroma residuals (if available): 0: disable, 1: enable
 PROF                         : 1
 PPSorSliceMode               : 3
+ChromaTS                     : 1
 
 # Fast tools
 PBIntraFast                  : 1
diff --git a/cfg/encoder_lowdelay_vtm.cfg b/cfg/encoder_lowdelay_vtm.cfg
index c8899e2e74339001db4bce7515fd2c23997dd3a0..932df208e69b4adaf221b49d621ee10eb93e45cd 100644
--- a/cfg/encoder_lowdelay_vtm.cfg
+++ b/cfg/encoder_lowdelay_vtm.cfg
@@ -137,6 +137,7 @@ MIP                          : 1
 JointCbCr                    : 1      # joint coding of chroma residuals (if available): 0: disable, 1: enable
 PROF                         : 1
 PPSorSliceMode               : 2
+ChromaTS                     : 1
 
 # Fast tools
 PBIntraFast                  : 1
diff --git a/cfg/encoder_randomaccess_vtm.cfg b/cfg/encoder_randomaccess_vtm.cfg
index e11813ba3f1e9f7352d753f5ff48ca0db0e1bfe4..c3b70fe2b03a4e986411e633b68e2f7adb0a8c2a 100644
--- a/cfg/encoder_randomaccess_vtm.cfg
+++ b/cfg/encoder_randomaccess_vtm.cfg
@@ -165,6 +165,7 @@ AMaxBT                       : 1
 FastMIP                      : 0
 FastLFNST                    : 0
 FastLocalDualTreeMode        : 1
+ChromaTS                     : 1
 
 # Encoder optimization tools
 AffineAmvrEncOpt             : 1
diff --git a/doc/software-manual.tex b/doc/software-manual.tex
index a1b8bafe51948657d19759eedcce6c6a7d3297c1..b98ab091cd0955c1333b1086d09cddee49ef2c64 100644
--- a/doc/software-manual.tex
+++ b/doc/software-manual.tex
@@ -2220,6 +2220,16 @@ luma TUs are also skipped.
 This option has no effect if TransformSkip is disabled.
 \\
 
+\Option{ChromaTS} &
+%\ShortOption{\None} &
+\Default{false} &
+Enables or disables reduced testing of the transform-skipping mode
+decision for chroma TUs.  When disabled, no RDO search is performed for
+chroma TUs.
+\par
+This option has no effect if TransformSkip is disabled.
+\\
+
 \Option{UseNonLinearAlfLuma} &
 %\ShortOption{\None} &
 \Default{true} &
diff --git a/source/App/EncoderApp/EncApp.cpp b/source/App/EncoderApp/EncApp.cpp
index a7ebf95d8e3f941f8fd694102fe2773f4e4624e0..2a971fe45f8bb9c86016d18d287d3671aabd898d 100644
--- a/source/App/EncoderApp/EncApp.cpp
+++ b/source/App/EncoderApp/EncApp.cpp
@@ -370,6 +370,9 @@ void EncApp::xInitLibCfg()
   m_cEncLib.setLog2SaoOffsetScale                                ( CHANNEL_TYPE_CHROMA, m_log2SaoOffsetScale[CHANNEL_TYPE_CHROMA] );
   m_cEncLib.setUseTransformSkip                                  ( m_useTransformSkip      );
   m_cEncLib.setUseTransformSkipFast                              ( m_useTransformSkipFast  );
+#if JVET_P0058_CHROMA_TS
+  m_cEncLib.setUseChromaTS                                       ( m_useChromaTS && m_useTransformSkip);
+#endif
   m_cEncLib.setUseBDPCM                                          ( m_useBDPCM );
   m_cEncLib.setTransformSkipRotationEnabledFlag                  ( m_transformSkipRotationEnabledFlag );
   m_cEncLib.setTransformSkipContextEnabledFlag                   ( m_transformSkipContextEnabledFlag   );
diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp
index ba71af2acfa0904126166a5db36fb3a5cfc9b36a..4ac5792b0078a18320d3234d1fdf0ee194fb647b 100644
--- a/source/App/EncoderApp/EncAppCfg.cpp
+++ b/source/App/EncoderApp/EncAppCfg.cpp
@@ -1134,6 +1134,9 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
   ("TransformSkip",                                   m_useTransformSkip,                               false, "Intra transform skipping")
   ("TransformSkipFast",                               m_useTransformSkipFast,                           false, "Fast encoder search for transform skipping, winner takes it all mode.")
   ("TransformSkipLog2MaxSize",                        m_log2MaxTransformSkipBlockSize,                     5U, "Specify transform-skip maximum size. Minimum 2, Maximum 5. (not valid in V1 profiles)")
+#if JVET_P0058_CHROMA_TS
+  ("ChromaTS",                                        m_useChromaTS,                                    false, "Enable encoder search of chromaTS")
+#endif
   ("BDPCM",                                           m_useBDPCM,                                       false, "BDPCM")
   ("ISPFast",                                         m_useFastISP,                                     false, "Fast encoder search for ISP")
   ("ImplicitResidualDPCM",                            m_rdpcmEnabledFlag[RDPCM_SIGNAL_IMPLICIT],        false, "Enable implicitly signalled residual DPCM for intra (also known as sample-adaptive intra predict) (not valid in V1 profiles)")
@@ -3586,6 +3589,9 @@ void EncAppCfg::xPrintParameter()
   msg( VERBOSE, "TransformSkip:%d ",     m_useTransformSkip     );
   msg( VERBOSE, "TransformSkipFast:%d ", m_useTransformSkipFast );
   msg( VERBOSE, "TransformSkipLog2MaxSize:%d ", m_log2MaxTransformSkipBlockSize);
+#if JVET_P0058_CHROMA_TS
+  msg(VERBOSE, "ChromaTS:%d ", m_useChromaTS);
+#endif
   msg( VERBOSE, "BDPCM:%d ", m_useBDPCM                         );
   msg( VERBOSE, "Slice: M=%d ", int(m_sliceMode));
   if (m_sliceMode!=NO_SLICES)
diff --git a/source/App/EncoderApp/EncAppCfg.h b/source/App/EncoderApp/EncAppCfg.h
index 267c895f257277da34cec92bcbc2407e70f70038..7ae720a425c416bf519505bc42eb4228a4d18a1e 100644
--- a/source/App/EncoderApp/EncAppCfg.h
+++ b/source/App/EncoderApp/EncAppCfg.h
@@ -305,6 +305,9 @@ protected:
   int       m_MmvdDisNum;
   unsigned  m_PLTMode;
   bool      m_JointCbCrMode;
+#if JVET_P0058_CHROMA_TS
+  bool      m_useChromaTS;
+#endif
   unsigned  m_IBCMode;
   unsigned  m_IBCLocalSearchRangeX;
   unsigned  m_IBCLocalSearchRangeY;
diff --git a/source/Lib/CommonLib/ContextModelling.cpp b/source/Lib/CommonLib/ContextModelling.cpp
index 217bf1b732cb5b6f70043c81bbca77ab566859f4..f1c33e0b2cf920f8d0dac44cdfee23ecdc66892c 100644
--- a/source/Lib/CommonLib/ContextModelling.cpp
+++ b/source/Lib/CommonLib/ContextModelling.cpp
@@ -68,7 +68,11 @@ CoeffCodingContext::CoeffCodingContext( const TransformUnit& tu, ComponentID com
   , m_lastOffsetY               (0)
   , m_lastShiftX                (0)
   , m_lastShiftY                (0)
+#if JVET_P0058_CHROMA_TS
+  , m_TrafoBypass               (tu.cs->sps->getSpsRangeExtension().getTransformSkipContextEnabledFlag() && (tu.cu->transQuantBypass || tu.mtsIdx[m_compID] == MTS_SKIP))
+#else
   , m_TrafoBypass               (tu.cs->sps->getSpsRangeExtension().getTransformSkipContextEnabledFlag() &&  (tu.cu->transQuantBypass || tu.mtsIdx==MTS_SKIP))
+#endif
   , m_scanPosLast               (-1)
   , m_subSetId                  (-1)
   , m_subSetPos                 (-1)
diff --git a/source/Lib/CommonLib/Contexts.cpp b/source/Lib/CommonLib/Contexts.cpp
index a1037dbdc891af452afd4086058d72bc4780632d..b8a593afdc0659e1d7c983f608ddea9004dec4d5 100755
--- a/source/Lib/CommonLib/Contexts.cpp
+++ b/source/Lib/CommonLib/Contexts.cpp
@@ -735,10 +735,17 @@ const CtxSet ContextSetCfg::RdpcmDir = ContextSetCfg::addCtxSet
 
 const CtxSet ContextSetCfg::MTSIndex = ContextSetCfg::addCtxSet
 ({
+#if JVET_P0058_CHROMA_TS
+  { 29, CNU, CNU, CNU, CNU, CNU,  33,  18,  27,   0, CNU, CNU },
+  { 29, CNU, CNU, CNU, CNU, CNU,  18,  33,  27,   0, CNU, CNU },
+  { 20, CNU, CNU, CNU, CNU, CNU,  33,   0,  42,   0, CNU, CNU },
+  {  8, DWS, DWS, DWS, DWS, DWS,   1,   0,   9,   0, DWS,   1 },
+#else
   {  29, CNU, CNU, CNU, CNU, CNU,  33,  18,  27,   0, CNU, },
   {  29, CNU, CNU, CNU, CNU, CNU,  18,  33,  27,   0, CNU, },
   {  20, CNU, CNU, CNU, CNU, CNU,  33,   0,  42,   0, CNU, },
   {   8, DWS, DWS, DWS, DWS, DWS,   1,   0,   9,   0, DWS, },
+#endif
 });
 
 const CtxSet ContextSetCfg::ISPMode = ContextSetCfg::addCtxSet
diff --git a/source/Lib/CommonLib/DepQuant.cpp b/source/Lib/CommonLib/DepQuant.cpp
index ce6484410b4b14e5f97b64e00fcdffa3aa1b1769..8cb1c93d6eff6abe774888f2c6cd9c8fd9610ae6 100644
--- a/source/Lib/CommonLib/DepQuant.cpp
+++ b/source/Lib/CommonLib/DepQuant.cpp
@@ -667,7 +667,12 @@ namespace DQIntern
   {
     CHECKD( lambda <= 0.0, "Lambda must be greater than 0" );
 
+#if JVET_P0058_CHROMA_TS
+    const int         qpDQ                  = cQP.Qp(tu.mtsIdx[compID] == MTS_SKIP) + 1;
+#else
     const int         qpDQ                  = cQP.Qp(tu.mtsIdx==MTS_SKIP && isLuma(compID)) + 1;
+
+#endif
     const int         qpPer                 = qpDQ / 6;
     const int         qpRem                 = qpDQ - 6 * qpPer;
     const SPS&        sps                   = *tu.cs->sps;
@@ -676,7 +681,11 @@ namespace DQIntern
     const int         channelBitDepth       = sps.getBitDepth( chType );
     const int         maxLog2TrDynamicRange = sps.getMaxLog2TrDynamicRange( chType );
     const int         nomTransformShift     = getTransformShift( channelBitDepth, area.size(), maxLog2TrDynamicRange );
+#if JVET_P0058_CHROMA_TS
+    const bool        clipTransformShift    = ( tu.mtsIdx[compID] == MTS_SKIP && sps.getSpsRangeExtension().getExtendedPrecisionProcessingFlag());
+#else
     const bool        clipTransformShift    = ( tu.mtsIdx==MTS_SKIP && sps.getSpsRangeExtension().getExtendedPrecisionProcessingFlag() );
+#endif
     const bool    needsSqrt2ScaleAdjustment = TU::needsSqrt2Scale(tu, compID);
     const int         transformShift        = ( clipTransformShift ? std::max<int>( 0, nomTransformShift ) : nomTransformShift ) + (needsSqrt2ScaleAdjustment?-1:0);
     // quant parameters
@@ -732,7 +741,11 @@ namespace DQIntern
     }
 
     //----- set dequant parameters -----
+#if JVET_P0058_CHROMA_TS
+    const int         qpDQ                  = cQP.Qp(tu.mtsIdx[compID] == MTS_SKIP) + 1;
+#else
     const int         qpDQ                  = cQP.Qp(tu.mtsIdx==MTS_SKIP && isLuma(compID)) + 1;
+#endif
     const int         qpPer                 = qpDQ / 6;
     const int         qpRem                 = qpDQ - 6 * qpPer;
     const SPS&        sps                   = *tu.cs->sps;
@@ -742,7 +755,11 @@ namespace DQIntern
     const TCoeff      minTCoeff             = -( 1 << maxLog2TrDynamicRange );
     const TCoeff      maxTCoeff             =  ( 1 << maxLog2TrDynamicRange ) - 1;
     const int         nomTransformShift     = getTransformShift( channelBitDepth, area.size(), maxLog2TrDynamicRange );
+#if JVET_P0058_CHROMA_TS
+    const bool        clipTransformShift    = ( tu.mtsIdx[compID] == MTS_SKIP && sps.getSpsRangeExtension().getExtendedPrecisionProcessingFlag());
+#else
     const bool        clipTransformShift    = ( tu.mtsIdx==MTS_SKIP && sps.getSpsRangeExtension().getExtendedPrecisionProcessingFlag() );
+#endif
     const bool    needsSqrt2ScaleAdjustment = TU::needsSqrt2Scale(tu, compID);
     const int         transformShift        = ( clipTransformShift ? std::max<int>( 0, nomTransformShift ) : nomTransformShift ) + (needsSqrt2ScaleAdjustment?-1:0);
     Intermediate_Int  shift                 = IQUANT_SHIFT + 1 - qpPer - transformShift + (enableScalingLists ? LOG2_SCALING_LIST_NEUTRAL_VALUE : 0);
@@ -1469,7 +1486,11 @@ namespace DQIntern
     bool zeroOut = false;
     bool zeroOutforThres = false;
     int effWidth = tuPars.m_width, effHeight = tuPars.m_height;
+#if JVET_P0058_CHROMA_TS
+    if( ( tu.mtsIdx[compID] > MTS_SKIP || (tu.cs->sps->getUseMTS() && tu.cu->sbtInfo != 0 && tuPars.m_height <= 32 && tuPars.m_width <= 32)) && !tu.cu->transQuantBypass && compID == COMPONENT_Y)
+#else
     if( ( tu.mtsIdx > MTS_SKIP || ( tu.cs->sps->getUseMTS() && tu.cu->sbtInfo != 0 && tuPars.m_height <= 32 && tuPars.m_width <= 32 ) ) && !tu.cu->transQuantBypass && compID == COMPONENT_Y )
+#endif
     {
       effHeight = (tuPars.m_height == 32) ? 16 : tuPars.m_height;
       effWidth = (tuPars.m_width == 32) ? 16 : tuPars.m_width;
@@ -1478,7 +1499,11 @@ namespace DQIntern
     zeroOutforThres = zeroOut || (32 < tuPars.m_height || 32 < tuPars.m_width);
     //===== find first test position =====
     int firstTestPos = numCoeff - 1;
+#if JVET_P0058_CHROMA_TS
+    if (lfnstIdx > 0 && tu.mtsIdx[compID] != MTS_SKIP && width >= 4 && height >= 4)
+#else
     if( lfnstIdx > 0 && tu.mtsIdx != MTS_SKIP && width >= 4 && height >= 4 )
+#endif
     {
       firstTestPos = ( ( width == 4 && height == 4 ) || ( width == 8 && height == 8 ) )  ? 7 : 15 ;
     }
@@ -1583,10 +1608,18 @@ DepQuant::~DepQuant()
 
 void DepQuant::quant( TransformUnit &tu, const ComponentID &compID, const CCoeffBuf &pSrc, TCoeff &uiAbsSum, const QpParam &cQP, const Ctx& ctx )
 {
-  if( tu.cs->slice->getDepQuantEnabledFlag() && (tu.mtsIdx != MTS_SKIP || !isLuma(compID)) )
+#if JVET_P0058_CHROMA_TS
+  if ( tu.cs->slice->getDepQuantEnabledFlag() && (tu.mtsIdx[compID] != MTS_SKIP) )
+#else
+  if ( tu.cs->slice->getDepQuantEnabledFlag() && (tu.mtsIdx != MTS_SKIP || !isLuma(compID)) )
+#endif
   {
     //===== scaling matrix ====
+#if JVET_P0058_CHROMA_TS
+    const int         qpDQ            = cQP.Qp(tu.mtsIdx[compID] == MTS_SKIP) + 1;
+#else
     const int         qpDQ            = cQP.Qp(tu.mtsIdx==MTS_SKIP && isLuma(compID)) + 1;
+#endif
     const int         qpPer           = qpDQ / 6;
     const int         qpRem           = qpDQ - 6 * qpPer;
     const CompArea    &rect           = tu.blocks[compID];
@@ -1596,7 +1629,11 @@ void DepQuant::quant( TransformUnit &tu, const ComponentID &compID, const CCoeff
     CHECK(scalingListType >= SCALING_LIST_NUM, "Invalid scaling list");
     const uint32_t    log2TrWidth     = floorLog2(width);
     const uint32_t    log2TrHeight    = floorLog2(height);
+#if JVET_P0058_CHROMA_TS
+    const bool        enableScalingLists = getUseScalingList(width, height, (tu.mtsIdx[compID] == MTS_SKIP));
+#else
     const bool        enableScalingLists = getUseScalingList(width, height, (tu.mtsIdx == MTS_SKIP && isLuma(compID)));
+#endif
     static_cast<DQIntern::DepQuant*>(p)->quant( tu, pSrc, compID, cQP, Quant::m_dLambda, ctx, uiAbsSum, enableScalingLists, Quant::getQuantCoeff(scalingListType, qpRem, log2TrWidth, log2TrHeight) );
   }
   else
@@ -1607,9 +1644,17 @@ void DepQuant::quant( TransformUnit &tu, const ComponentID &compID, const CCoeff
 
 void DepQuant::dequant( const TransformUnit &tu, CoeffBuf &dstCoeff, const ComponentID &compID, const QpParam &cQP )
 {
+#if JVET_P0058_CHROMA_TS
+  if( tu.cs->slice->getDepQuantEnabledFlag() && (tu.mtsIdx[compID] != MTS_SKIP))
+#else
   if( tu.cs->slice->getDepQuantEnabledFlag() && (tu.mtsIdx != MTS_SKIP || !isLuma(compID)) )
+#endif
   {
+#if JVET_P0058_CHROMA_TS
+    const int         qpDQ            = cQP.Qp(tu.mtsIdx[compID] == MTS_SKIP) + 1;
+#else
     const int         qpDQ            = cQP.Qp(tu.mtsIdx==MTS_SKIP && isLuma(compID)) + 1;
+#endif
     const int         qpPer           = qpDQ / 6;
     const int         qpRem           = qpDQ - 6 * qpPer;
     const CompArea    &rect           = tu.blocks[compID];
@@ -1619,8 +1664,11 @@ void DepQuant::dequant( const TransformUnit &tu, CoeffBuf &dstCoeff, const Compo
     CHECK(scalingListType >= SCALING_LIST_NUM, "Invalid scaling list");
     const uint32_t    log2TrWidth  = floorLog2(width);
     const uint32_t    log2TrHeight = floorLog2(height);
-
+#if JVET_P0058_CHROMA_TS
+    const bool enableScalingLists = getUseScalingList(width, height, (tu.mtsIdx[compID] == MTS_SKIP));
+#else
     const bool enableScalingLists = getUseScalingList(width, height, (tu.mtsIdx == MTS_SKIP && isLuma(compID)));
+#endif
     static_cast<DQIntern::DepQuant*>(p)->dequant( tu, dstCoeff, compID, cQP, enableScalingLists, Quant::getDequantCoeff(scalingListType, qpRem, log2TrWidth, log2TrHeight) );
   }
   else
diff --git a/source/Lib/CommonLib/Quant.cpp b/source/Lib/CommonLib/Quant.cpp
index 0264a6055aaf2657ba4853f4865a5bef7c5debbd..45b2dcee4f243ea864d63a23935cc7eb89ef7a31 100644
--- a/source/Lib/CommonLib/Quant.cpp
+++ b/source/Lib/CommonLib/Quant.cpp
@@ -369,7 +369,11 @@ void Quant::dequant(const TransformUnit &tu,
   const int             maxLog2TrDynamicRange = sps->getMaxLog2TrDynamicRange(toChannelType(compID));
   const TCoeff          transformMinimum   = -(1 << maxLog2TrDynamicRange);
   const TCoeff          transformMaximum   =  (1 << maxLog2TrDynamicRange) - 1;
+#if JVET_P0058_CHROMA_TS
+  const bool            isTransformSkip = (tu.mtsIdx[compID] == MTS_SKIP);
+#else
   const bool            isTransformSkip = tu.mtsIdx==MTS_SKIP && isLuma(compID);
+#endif
   const bool            enableScalingLists = getUseScalingList(uiWidth, uiHeight, isTransformSkip);
   const int             scalingListType    = getScalingListType(tu.cu->predMode, compID);
   const int             channelBitDepth    = sps->getBitDepth(toChannelType(compID));
@@ -389,7 +393,11 @@ void Quant::dequant(const TransformUnit &tu,
   CHECK(uiWidth > m_uiMaxTrSize, "Unsupported transformation size");
 
   // Represents scaling through forward transform
+#if JVET_P0058_CHROMA_TS
+  const bool bClipTransformShiftTo0 = tu.mtsIdx[compID] != MTS_SKIP && sps->getSpsRangeExtension().getExtendedPrecisionProcessingFlag();
+#else
   const bool bClipTransformShiftTo0 = tu.mtsIdx!=MTS_SKIP && sps->getSpsRangeExtension().getExtendedPrecisionProcessingFlag();
+#endif
   const int  originalTransformShift = getTransformShift(channelBitDepth, area.size(), maxLog2TrDynamicRange);
   const bool needSqrtAdjustment     = TU::needsBlockSizeTrafoScale( tu, compID );
   const int  iTransformShift        = (bClipTransformShiftTo0 ? std::max<int>(0, originalTransformShift) : originalTransformShift) + (needSqrtAdjustment?-1:0);
@@ -932,7 +940,11 @@ void Quant::quant(TransformUnit &tu, const ComponentID &compID, const CCoeffBuf
   const CCoeffBuf &piCoef   = pSrc;
         CoeffBuf   piQCoef  = tu.getCoeffs(compID);
 
+#if JVET_P0058_CHROMA_TS
+  const bool useTransformSkip      = (tu.mtsIdx[compID] == MTS_SKIP);
+#else
   const bool useTransformSkip      = tu.mtsIdx==MTS_SKIP && isLuma(compID);
+#endif
   const int  maxLog2TrDynamicRange = sps.getMaxLog2TrDynamicRange(toChannelType(compID));
 
   {
@@ -1012,7 +1024,11 @@ bool Quant::xNeedRDOQ(TransformUnit &tu, const ComponentID &compID, const CCoeff
 
   const CCoeffBuf piCoef    = pSrc;
 
+#if JVET_P0058_CHROMA_TS
+  const bool useTransformSkip      = (tu.mtsIdx[compID] == MTS_SKIP);
+#else
   const bool useTransformSkip      = tu.mtsIdx == MTS_SKIP && isLuma(compID);
+#endif
   const int  maxLog2TrDynamicRange = sps.getMaxLog2TrDynamicRange(toChannelType(compID));
 
   int scalingListType = getScalingListType(tu.cu->predMode, compID);
@@ -1072,7 +1088,11 @@ void Quant::transformSkipQuantOneSample(TransformUnit &tu, const ComponentID &co
   const int            iTransformShift                = getTransformShift(channelBitDepth, rect.size(), maxLog2TrDynamicRange);
   const int            scalingListType                = getScalingListType(tu.cu->predMode, compID);
   const bool           enableScalingLists             = getUseScalingList(uiWidth, uiHeight, true);
+#if JVET_P0058_CHROMA_TS
+  const bool           useTransformSkip = (tu.mtsIdx[compID] == MTS_SKIP);
+#else
   const bool useTransformSkip      = tu.mtsIdx == MTS_SKIP && isLuma(compID);
+#endif
   const int            defaultQuantisationCoefficient = g_quantScales[0][cQP.rem(useTransformSkip)];
 
   CHECK( scalingListType >= SCALING_LIST_NUM, "Invalid scaling list" );
@@ -1124,8 +1144,13 @@ void Quant::invTrSkipDeQuantOneSample(TransformUnit &tu, const ComponentID &comp
   const CompArea      &rect                   = tu.blocks[compID];
   const uint32_t           uiWidth                = rect.width;
   const uint32_t           uiHeight               = rect.height;
+#if JVET_P0058_CHROMA_TS
+  const int            QP_per                 = cQP.per(tu.mtsIdx[compID] == MTS_SKIP);
+  const int            QP_rem                 = cQP.rem(tu.mtsIdx[compID] == MTS_SKIP);
+#else
   const int            QP_per                 = cQP.per(tu.mtsIdx==MTS_SKIP && isLuma(compID));
   const int            QP_rem                 = cQP.rem(tu.mtsIdx==MTS_SKIP && isLuma(compID));
+#endif
   const int            maxLog2TrDynamicRange  = sps.getMaxLog2TrDynamicRange(toChannelType(compID));
   const int            channelBitDepth        = sps.getBitDepth(toChannelType(compID));
   const int            iTransformShift        = getTransformShift(channelBitDepth, rect.size(), maxLog2TrDynamicRange);
diff --git a/source/Lib/CommonLib/QuantRDOQ.cpp b/source/Lib/CommonLib/QuantRDOQ.cpp
index e72c83a866e6d92351b9580d06fac4d1a3877894..ff150c610b2aa4ba2c3a4b61c00dc9701a9f51c1 100644
--- a/source/Lib/CommonLib/QuantRDOQ.cpp
+++ b/source/Lib/CommonLib/QuantRDOQ.cpp
@@ -520,7 +520,11 @@ void QuantRDOQ::quant(TransformUnit &tu, const ComponentID &compID, const CCoeff
   const CCoeffBuf &piCoef   = pSrc;
         CoeffBuf   piQCoef  = tu.getCoeffs(compID);
 
+#if JVET_P0058_CHROMA_TS
+  const bool useTransformSkip      = (tu.mtsIdx[compID] == MTS_SKIP);
+#else
   const bool useTransformSkip      = tu.mtsIdx==MTS_SKIP && isLuma(compID);
+#endif
 
   bool useRDOQ = useTransformSkip ? m_useRDOQTS : m_useRDOQ;
 
@@ -536,7 +540,11 @@ void QuantRDOQ::quant(TransformUnit &tu, const ComponentID &compID, const CCoeff
     if (!m_useSelectiveRDOQ || xNeedRDOQ(tu, compID, piCoef, cQP))
     {
 #endif
+#if JVET_P0058_CHROMA_TS
+      if( useTransformSkip )
+#else
       if( isLuma( compID ) && useTransformSkip )
+#endif
       {
         if( tu.cu->bdpcmMode && isLuma(compID) )
         {
@@ -592,7 +600,11 @@ void QuantRDOQ::xRateDistOptQuant(TransformUnit &tu, const ComponentID &compID,
   // Represents scaling through forward transform
   int iTransformShift = getTransformShift(channelBitDepth, rect.size(), maxLog2TrDynamicRange);
 
+#if JVET_P0058_CHROMA_TS
+  if (tu.mtsIdx[compID] == MTS_SKIP && extendedPrecision)
+#else
   if (tu.mtsIdx==MTS_SKIP && extendedPrecision)
+#endif
   {
     iTransformShift = std::max<int>(0, iTransformShift);
   }
@@ -629,7 +641,11 @@ void QuantRDOQ::xRateDistOptQuant(TransformUnit &tu, const ComponentID &compID,
 
 
   const bool needSqrtAdjustment= TU::needsBlockSizeTrafoScale( tu, compID );
+#if JVET_P0058_CHROMA_TS
+  const bool   isTransformSkip = (tu.mtsIdx[compID] == MTS_SKIP);
+#else
   const bool   isTransformSkip = tu.mtsIdx==MTS_SKIP && isLuma(compID);
+#endif
   const double *const pdErrScale = xGetErrScaleCoeffSL(scalingListType, uiLog2BlockWidth, uiLog2BlockHeight, cQP.rem(isTransformSkip));
   const int    *const piQCoef    = getQuantCoeff(scalingListType, cQP.rem(isTransformSkip), uiLog2BlockWidth, uiLog2BlockHeight);
   const bool   enableScalingLists             = getUseScalingList(uiWidth, uiHeight, isTransformSkip);
@@ -1220,7 +1236,11 @@ void QuantRDOQ::xRateDistOptQuantTS( TransformUnit &tu, const ComponentID &compI
   m_bdpcm = 0;
 
   const bool   needsSqrt2Scale = TU::needsSqrt2Scale( tu, compID );  // should always be false - transform-skipped blocks don't require sqrt(2) compensation.
+#if JVET_P0058_CHROMA_TS
+  const bool   isTransformSkip = (tu.mtsIdx[compID] == MTS_SKIP);
+#else
   const bool   isTransformSkip = tu.mtsIdx==MTS_SKIP && isLuma(compID);
+#endif
   const int    qBits = QUANT_SHIFT + qp.per(isTransformSkip) + transformShift + ( needsSqrt2Scale ? -1 : 0 );  // Right shift of non-RDOQ quantizer;  level = (coeff*uiQ + offset)>>q_bits
   const int    quantisationCoefficient = g_quantScales[needsSqrt2Scale?1:0][qp.rem(isTransformSkip)];
   const double errorScale              = xGetErrScaleCoeff( TU::needsSqrt2Scale( tu, compID ), width, height, qp.rem(isTransformSkip), maxLog2TrDynamicRange, channelBitDepth );
@@ -1456,7 +1476,11 @@ void QuantRDOQ::forwardRDPCM( TransformUnit &tu, const ComponentID &compID, cons
   m_bdpcm = dirMode;
 
   const bool   needsSqrt2Scale = TU::needsSqrt2Scale(tu, compID);  // should always be false - transform-skipped blocks don't require sqrt(2) compensation.
+#if JVET_P0058_CHROMA_TS
+  const bool   isTransformSkip = (tu.mtsIdx[compID] == MTS_SKIP);
+#else
   const bool   isTransformSkip = tu.mtsIdx==MTS_SKIP && isLuma(compID);
+#endif
   const int    qBits = QUANT_SHIFT + qp.per(isTransformSkip) + transformShift + ( needsSqrt2Scale ? -1 : 0 );  // Right shift of non-RDOQ quantizer;  level = (coeff*uiQ + offset)>>q_bits
   const int    quantisationCoefficient = g_quantScales[needsSqrt2Scale ? 1 : 0][qp.rem(isTransformSkip)];
   const double errorScale = xGetErrScaleCoeff(TU::needsSqrt2Scale(tu, compID), width, height, qp.rem(isTransformSkip), maxLog2TrDynamicRange, channelBitDepth);
diff --git a/source/Lib/CommonLib/TrQuant.cpp b/source/Lib/CommonLib/TrQuant.cpp
index 11ecdde1833448b1756b3609ba55a0592e4680f7..51ba7016fdafa50ffca741cd3bb9e74922721e1c 100644
--- a/source/Lib/CommonLib/TrQuant.cpp
+++ b/source/Lib/CommonLib/TrQuant.cpp
@@ -314,7 +314,11 @@ void TrQuant::xInvLfnst( const TransformUnit &tu, const ComponentID compID )
   const uint32_t  height   = area.height;
   const uint32_t  lfnstIdx = tu.cu->lfnstIdx;
 
+#if JVET_P0058_CHROMA_TS
+  if (lfnstIdx && tu.mtsIdx[compID] != MTS_SKIP && width >= 4 && height >= 4)
+#else
   if( lfnstIdx && tu.mtsIdx != MTS_SKIP && width >= 4 && height >= 4 )
+#endif
   {
     const bool whge3 = width >= 8 && height >= 8;
     const ScanElement * scan = whge3 ? g_coefTopLeftDiagScan8x8[ gp_sizeIdxInfo->idxFrom( width ) ] : g_scanOrder[ SCAN_GROUPED_4x4 ][ SCAN_DIAG ][ gp_sizeIdxInfo->idxFrom( width ) ][ gp_sizeIdxInfo->idxFrom( height ) ];
@@ -406,7 +410,11 @@ void TrQuant::xFwdLfnst( const TransformUnit &tu, const ComponentID compID, cons
   const uint32_t  height   = area.height;
   const uint32_t  lfnstIdx = tu.cu->lfnstIdx;
 
+#if JVET_P0058_CHROMA_TS
+  if( lfnstIdx && tu.mtsIdx[compID] != MTS_SKIP && width >= 4 && height >= 4)
+#else
   if( lfnstIdx && tu.mtsIdx != MTS_SKIP && width >= 4 && height >= 4 )
+#endif
   {
     const bool whge3 = width >= 8 && height >= 8;
     const ScanElement * scan = whge3 ? g_coefTopLeftDiagScan8x8[ gp_sizeIdxInfo->idxFrom( width ) ] : g_scanOrder[ SCAN_GROUPED_4x4 ][ SCAN_DIAG ][ gp_sizeIdxInfo->idxFrom( width ) ][ gp_sizeIdxInfo->idxFrom( height ) ];
@@ -432,7 +440,11 @@ void TrQuant::xFwdLfnst( const TransformUnit &tu, const ComponentID compID, cons
       bool            tu8x8Flag       = ( width == 8 && height == 8 );
       TCoeff*         lfnstTemp;
       TCoeff*         coeffTemp;
+#if JVET_P0058_CHROMA_TS
+      TCoeff *        tempCoeff = loadTr ? m_mtsCoeffs[tu.mtsIdx[compID]] : m_tempCoeff;
+#else
       TCoeff *        tempCoeff = loadTr ? m_mtsCoeffs[tu.mtsIdx] : m_tempCoeff;
+#endif
 
           int y;
           lfnstTemp = m_tempInMatrix; // forward low frequency non-separable transform
@@ -526,7 +538,11 @@ void TrQuant::invTransformNxN( TransformUnit &tu, const ComponentID &compID, Pel
       xInvLfnst( tu, compID );
     }
 
+#if JVET_P0058_CHROMA_TS
+    if( tu.mtsIdx[compID] == MTS_SKIP )
+#else
     if( isLuma(compID) && tu.mtsIdx == MTS_SKIP )
+#endif
     {
       xITransformSkip( tempCoeff, pResi, tu, compID );
     }
@@ -545,7 +561,11 @@ void TrQuant::invRdpcmNxN(TransformUnit& tu, const ComponentID &compID, PelBuf &
 {
   const CompArea &area    = tu.blocks[compID];
 
+#if JVET_P0058_CHROMA_TS
+  if (CU::isRDPCMEnabled(*tu.cu) && (tu.mtsIdx[compID] == MTS_SKIP || tu.cu->transQuantBypass))
+#else
   if (CU::isRDPCMEnabled(*tu.cu) && (tu.mtsIdx==MTS_SKIP || tu.cu->transQuantBypass))
+#endif
   {
     const uint32_t uiWidth  = area.width;
     const uint32_t uiHeight = area.height;
@@ -740,11 +760,19 @@ void TrQuant::getTrTypes(const TransformUnit tu, const ComponentID compID, int &
 
   if (isExplicitMTS)
   {
+#if JVET_P0058_CHROMA_TS
+    if (tu.mtsIdx[compID] > MTS_SKIP)
+#else
     if (tu.mtsIdx > MTS_SKIP)
+#endif
     {
+#if JVET_P0058_CHROMA_TS
+      int indHor = (tu.mtsIdx[compID] - MTS_DST7_DST7) & 1;
+      int indVer = (tu.mtsIdx[compID] - MTS_DST7_DST7) >> 1;
+#else
       int indHor = (tu.mtsIdx - MTS_DST7_DST7) & 1;
       int indVer = (tu.mtsIdx - MTS_DST7_DST7) >> 1;
-
+#endif
       trTypeHor = indHor ? DCT8 : DST7;
       trTypeVer = indVer ? DCT8 : DST7;
     }
@@ -915,6 +943,10 @@ void TrQuant::xITransformSkip(const CCoeffBuf     &pCoeff,
   const int maxLog2TrDynamicRange = tu.cs->sps->getMaxLog2TrDynamicRange(toChannelType(compID));
   const int channelBitDepth = tu.cs->sps->getBitDepth(toChannelType(compID));
 
+#if JVET_P0058_CHROMA_TS && RExt__DECODER_DEBUG_TOOL_STATISTICS
+  CodingStatistics::IncrementStatisticTool(CodingStatisticsClassType{ STATS__TOOL_EMT, uint32_t(width), uint32_t(height), compID });
+#endif
+
   int iTransformShift = getTransformShift(channelBitDepth, area.size(), maxLog2TrDynamicRange);
   if( tu.cs->sps->getSpsRangeExtension().getExtendedPrecisionProcessingFlag() )
   {
@@ -973,8 +1005,16 @@ void TrQuant::transformNxN( TransformUnit& tu, const ComponentID& compID, const
   const double facBB[] = { 1.2, 1.3, 1.3, 1.4, 1.5 };
   while( it != trModes->end() )
   {
+#if JVET_P0058_CHROMA_TS
+    tu.mtsIdx[compID] = it->first;
+#else
     tu.mtsIdx = it->first;
+#endif
+#if JVET_P0058_CHROMA_TS
+    CoeffBuf tempCoeff( m_mtsCoeffs[tu.mtsIdx[compID]], rect);
+#else
     CoeffBuf tempCoeff( m_mtsCoeffs[tu.mtsIdx], rect );
+#endif
     if( tu.noResidual )
     {
       int sumAbs = 0;
@@ -983,7 +1023,11 @@ void TrQuant::transformNxN( TransformUnit& tu, const ComponentID& compID, const
       continue;
     }
 
+#if JVET_P0058_CHROMA_TS
+    if ( tu.mtsIdx[compID] == MTS_SKIP )
+#else
     if( isLuma(compID) && tu.mtsIdx == MTS_SKIP )
+#endif
     {
       xTransformSkip( tu, compID, resiBuf, tempCoeff.buf );
     }
@@ -999,7 +1043,11 @@ void TrQuant::transformNxN( TransformUnit& tu, const ComponentID& compID, const
     }
 
     double scaleSAD=1.0;
+#if JVET_P0058_CHROMA_TS
+    if ( tu.mtsIdx[compID] == MTS_SKIP && ((floorLog2(width) + floorLog2(height)) & 1) == 1)
+#else
     if (isLuma(compID) && tu.mtsIdx==MTS_SKIP && ((floorLog2(width) + floorLog2(height)) & 1) == 1 )
+#endif
     {
       scaleSAD=1.0/1.414213562; // compensate for not scaling transform skip coefficients by 1/sqrt(2)
     }
@@ -1009,7 +1057,11 @@ void TrQuant::transformNxN( TransformUnit& tu, const ComponentID& compID, const
 
   int numTests = 0;
   std::vector<TrCost>::iterator itC = trCosts.begin();
+#if JVET_P0058_CHROMA_TS
+  const double fac   = facBB[std::max(0, floorLog2(std::max(width, height)) - 2)];
+#else
   const double fac   = facBB[floorLog2(std::max(width, height))-2];
+#endif
   const double thr   = fac * trCosts.begin()->first;
   const double thrTS = trCosts.begin()->first;
   while( itC != trCosts.end() )
@@ -1044,7 +1096,11 @@ void TrQuant::transformNxN( TransformUnit& tu, const ComponentID& compID, const
 
   if( tu.cu->bdpcmMode && isLuma(compID) )
   {
+#if JVET_P0058_CHROMA_TS
+    tu.mtsIdx[compID] = MTS_SKIP;
+#else
     tu.mtsIdx = MTS_SKIP;
+#endif
   }
 
   if (rdpcmMode == RDPCM_OFF)
@@ -1080,13 +1136,21 @@ void TrQuant::transformNxN( TransformUnit& tu, const ComponentID& compID, const
       CHECK( cs.sps->getMaxTbSize() < uiWidth, "Unsupported transformation size" );
 
 
+#if JVET_P0058_CHROMA_TS
+      CoeffBuf tempCoeff(loadTr ? m_mtsCoeffs[tu.mtsIdx[compID]] : m_tempCoeff, rect);
+#else
       CoeffBuf tempCoeff(loadTr ? m_mtsCoeffs[tu.mtsIdx] : m_tempCoeff, rect);
+#endif
 
       DTRACE_PEL_BUF( D_RESIDUALS, resiBuf, tu, tu.cu->predMode, compID );
 
       if( !loadTr )
       {
+#if JVET_P0058_CHROMA_TS
+        if ( tu.mtsIdx[compID] == MTS_SKIP )
+#else
         if( isLuma(compID) && tu.mtsIdx == MTS_SKIP )
+#endif
       {
         xTransformSkip( tu, compID, resiBuf, tempCoeff.buf );
       }
@@ -1174,7 +1238,11 @@ void TrQuant::applyForwardRDPCM(TransformUnit &tu, const ComponentID &compID, co
 
 void TrQuant::rdpcmNxN(TransformUnit &tu, const ComponentID &compID, const QpParam &cQP, TCoeff &uiAbsSum, RDPCMMode &rdpcmMode)
 {
+#if JVET_P0058_CHROMA_TS
+  if (!CU::isRDPCMEnabled(*tu.cu) || (tu.mtsIdx[compID] != MTS_SKIP && !tu.cu->transQuantBypass))
+#else
   if (!CU::isRDPCMEnabled(*tu.cu) || (tu.mtsIdx!=MTS_SKIP && !tu.cu->transQuantBypass))
+#endif
   {
     rdpcmMode = RDPCM_OFF;
   }
diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index cb17fd390981a6f10d030cbb53a5408ac47e5fe1..93e22e5485d6123ab3ef622bddfa8dbb5fb875a9 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -91,6 +91,11 @@
 
 #define JVET_P0170_ZERO_POS_SIMPLIFICATION                1 // JVET-P0170: Simplification of deriving ZeroPos
 
+#define JVET_P0058_CHROMA_TS                              1 // JVET-P0058: Enable Transform skip for chroma
+#if JVET_P0058_CHROMA_TS
+#define JVET_P0058_CHROMA_TS_ENCODER_INTRA_SAD_MOD        1 // Modified cost criterion for chroma intra encoder search
+#endif
+
 #define JVET_P0436_CQP_OFFSET_SIGNALLING                  1 // JVET_P0436: CU chroma QP offset signalling consistent with VPDU and bugfix 
 
 #define JVET_P0505_ALF_CLIP_VALUE                         1 // JVET-P0505: Modified non-linear ALF clipping value derivations
diff --git a/source/Lib/CommonLib/Unit.cpp b/source/Lib/CommonLib/Unit.cpp
index 05d0035b99eca17823259cdf3571fd9c078c83d0..4b2874848c1270845768df01e0d40798469ca896 100644
--- a/source/Lib/CommonLib/Unit.cpp
+++ b/source/Lib/CommonLib/Unit.cpp
@@ -734,9 +734,14 @@ void TransformUnit::initData()
     cbf[i]           = 0;
     rdpcm[i]         = NUMBER_OF_RDPCM_MODES;
     compAlpha[i]     = 0;
+#if JVET_P0058_CHROMA_TS
+    mtsIdx[i]        = MTS_DCT2_DCT2;
+#endif
   }
   depth              = 0;
+#if !JVET_P0058_CHROMA_TS
   mtsIdx             = MTS_DCT2_DCT2;
+#endif
   noResidual         = false;
   jointCbCr          = 0;
   m_chromaResScaleInv = 0;
@@ -774,9 +779,14 @@ TransformUnit& TransformUnit::operator=(const TransformUnit& other)
     cbf[i]           = other.cbf[i];
     rdpcm[i]         = other.rdpcm[i];
     compAlpha[i]     = other.compAlpha[i];
+#if JVET_P0058_CHROMA_TS
+    mtsIdx[i] = other.mtsIdx[i];
+#endif
   }
   depth              = other.depth;
+#if !JVET_P0058_CHROMA_TS
   mtsIdx             = other.mtsIdx;
+#endif
   noResidual         = other.noResidual;
   jointCbCr          = other.jointCbCr;
   return *this;
@@ -800,7 +810,11 @@ void TransformUnit::copyComponentFrom(const TransformUnit& other, const Componen
   compAlpha[i]     = other.compAlpha[i];
 
   depth            = other.depth;
+#if JVET_P0058_CHROMA_TS
+  mtsIdx[i]        = other.mtsIdx[i];
+#else
   mtsIdx           = isLuma( i ) ? other.mtsIdx : mtsIdx;
+#endif
   noResidual       = other.noResidual;
   jointCbCr        = isChroma( i ) ? other.jointCbCr : jointCbCr;
 }
@@ -845,7 +859,11 @@ int TransformUnit::getTbAreaAfterCoefZeroOut(ComponentID compID) const
   int tbArea = blocks[compID].width * blocks[compID].height;
   int tbZeroOutWidth = blocks[compID].width;
   int tbZeroOutHeight = blocks[compID].height;
+#if JVET_P0058_CHROMA_TS
+  if ((mtsIdx[compID] > MTS_SKIP || (cs->sps->getUseMTS() && cu->sbtInfo != 0 && blocks[compID].width <= 32 && blocks[compID].height <= 32)) && !cu->transQuantBypass && compID == COMPONENT_Y)
+#else
   if ((mtsIdx > MTS_SKIP || (cs->sps->getUseMTS() && cu->sbtInfo != 0 && blocks[compID].width <= 32 && blocks[compID].height <= 32)) && !cu->transQuantBypass && compID == COMPONENT_Y)
+#endif
   {
     tbZeroOutWidth = (blocks[compID].width == 32) ? 16 : tbZeroOutWidth;
     tbZeroOutHeight = (blocks[compID].height == 32) ? 16 : tbZeroOutHeight;
diff --git a/source/Lib/CommonLib/Unit.h b/source/Lib/CommonLib/Unit.h
index 305aa0ac860c465bb0774dee06546cffc3818a2f..1cfc5a798da2558b05a0e82cebc72ba4eff53507 100644
--- a/source/Lib/CommonLib/Unit.h
+++ b/source/Lib/CommonLib/Unit.h
@@ -463,7 +463,11 @@ struct TransformUnit : public UnitArea
   int              m_chromaResScaleInv;
 
   uint8_t        depth;
+#if JVET_P0058_CHROMA_TS
+  uint8_t        mtsIdx     [ MAX_NUM_TBLOCKS ];
+#else
   uint8_t        mtsIdx;
+#endif
   bool           noResidual;
   uint8_t        jointCbCr;
   uint8_t        cbf        [ MAX_NUM_TBLOCKS ];
diff --git a/source/Lib/CommonLib/UnitTools.cpp b/source/Lib/CommonLib/UnitTools.cpp
index 2ca98de92fde125f01a050cdd88d5fc8b6488a95..dd49aed52a395140fc5d90784cec7693ebfb7cc8 100644
--- a/source/Lib/CommonLib/UnitTools.cpp
+++ b/source/Lib/CommonLib/UnitTools.cpp
@@ -3794,15 +3794,29 @@ void TU::setCbfAtDepth(TransformUnit &tu, const ComponentID &compID, const unsig
 
 bool TU::isTSAllowed(const TransformUnit &tu, const ComponentID compID)
 {
+#if !JVET_P0058_CHROMA_TS
   bool    tsAllowed = compID == COMPONENT_Y;
+#endif
   const int maxSize = tu.cs->pps->getLog2MaxTransformSkipBlockSize();
 
+#if JVET_P0058_CHROMA_TS
+  bool tsAllowed = tu.cs->sps->getTransformSkipEnabledFlag();
+#else
   tsAllowed &= tu.cs->sps->getTransformSkipEnabledFlag();
+#endif
   tsAllowed &= !tu.cu->transQuantBypass;
   tsAllowed &= ( !tu.cu->ispMode || !isLuma(compID) );
   SizeType transformSkipMaxSize = 1 << maxSize;
+#if JVET_P0058_CHROMA_TS
+  tsAllowed &= !(tu.cu->bdpcmMode && isLuma(compID));
+#else
   tsAllowed &= !(tu.cu->bdpcmMode && tu.lwidth() <= transformSkipMaxSize && tu.lheight() <= transformSkipMaxSize);
+#endif
+#if JVET_P0058_CHROMA_TS
+  tsAllowed &= tu.blocks[compID].width <= transformSkipMaxSize && tu.blocks[compID].height <= transformSkipMaxSize;
+#else
   tsAllowed &= tu.lwidth() <= transformSkipMaxSize && tu.lheight() <= transformSkipMaxSize;
+#endif
   tsAllowed &= !tu.cu->sbtInfo;
 
   return tsAllowed;
@@ -3841,7 +3855,11 @@ bool TU::hasCrossCompPredInfo( const TransformUnit &tu, const ComponentID &compI
 bool TU::needsSqrt2Scale( const TransformUnit &tu, const ComponentID &compID )
 {
   const Size &size=tu.blocks[compID];
+#if JVET_P0058_CHROMA_TS
+  const bool isTransformSkip = (tu.mtsIdx[compID] == MTS_SKIP);
+#else
   const bool isTransformSkip = tu.mtsIdx==MTS_SKIP && isLuma(compID);
+#endif
   return (!isTransformSkip) && (((floorLog2(size.width) + floorLog2(size.height)) & 1) == 1);
 }
 
diff --git a/source/Lib/CommonLib/dtrace_blockstatistics.cpp b/source/Lib/CommonLib/dtrace_blockstatistics.cpp
index 350af8f22b00d2070b3859ebb7053c71ae34b9ab..8b4bdab1027a14e1b4f3b0ecfd6276e01940c7bc 100644
--- a/source/Lib/CommonLib/dtrace_blockstatistics.cpp
+++ b/source/Lib/CommonLib/dtrace_blockstatistics.cpp
@@ -712,13 +712,18 @@ void writeAllData(const CodingStructure& cs, const UnitArea& ctuArea)
         if (tu.Y().valid())
         {
           DTRACE_BLOCK_SCALAR(g_trace_ctx, D_BLOCK_STATISTICS_ALL, tu, GetBlockStatisticName(BlockStatistic::Cbf_Y), tu.cbf[COMPONENT_Y]);
+#if JVET_P0058_CHROMA_TS
+          DTRACE_BLOCK_SCALAR(g_trace_ctx, D_BLOCK_STATISTICS_ALL, tu, GetBlockStatisticName(BlockStatistic::MTSIdx_Y), tu.mtsIdx[COMPONENT_Y]);
+#else
           DTRACE_BLOCK_SCALAR(g_trace_ctx, D_BLOCK_STATISTICS_ALL, tu, GetBlockStatisticName(BlockStatistic::MTSIdx), tu.mtsIdx);
+#endif
         }
         if ( tu.Cb().valid() )
         {
           DTRACE_BLOCK_SCALAR(g_trace_ctx, D_BLOCK_STATISTICS_ALL, tu, GetBlockStatisticName(BlockStatistic::JointCbCr), tu.jointCbCr);
         }
 
+#if !JVET_P0058_CHROMA_TS
         if( !CU::isIntra(cu) && CU::isRDPCMEnabled(cu) && ( tu.mtsIdx==MTS_SKIP || cu.transQuantBypass ) )
         {
           if (tu.Y().valid())
@@ -734,7 +739,7 @@ void writeAllData(const CodingStructure& cs, const UnitArea& ctuArea)
             DTRACE_BLOCK_SCALAR(g_trace_ctx, D_BLOCK_STATISTICS_ALL, tu, GetBlockStatisticName(BlockStatistic::RDPCM_Cr), tu.rdpcm[COMPONENT_Cr]);
           }
         }
-
+#endif
         bool lumaOnly  = ( cu.chromaFormat == CHROMA_400 || !tu.blocks[COMPONENT_Cb].valid() );
         if( !lumaOnly )
         {
@@ -752,6 +757,10 @@ void writeAllData(const CodingStructure& cs, const UnitArea& ctuArea)
         {
           DTRACE_BLOCK_SCALAR_CHROMA(g_trace_ctx, D_BLOCK_STATISTICS_ALL, tu, GetBlockStatisticName(BlockStatistic::Cbf_Cb), tu.cbf[COMPONENT_Cb]);
           DTRACE_BLOCK_SCALAR_CHROMA(g_trace_ctx, D_BLOCK_STATISTICS_ALL, tu, GetBlockStatisticName(BlockStatistic::Cbf_Cr), tu.cbf[COMPONENT_Cr]);
+#if JVET_P0058_CHROMA_TS
+          DTRACE_BLOCK_SCALAR_CHROMA(g_trace_ctx, D_BLOCK_STATISTICS_ALL, tu, GetBlockStatisticName(BlockStatistic::MTSIdx_Cb), tu.mtsIdx[COMPONENT_Cb]);
+          DTRACE_BLOCK_SCALAR_CHROMA(g_trace_ctx, D_BLOCK_STATISTICS_ALL, tu, GetBlockStatisticName(BlockStatistic::MTSIdx_Cr), tu.mtsIdx[COMPONENT_Cr]);
+#endif
         }
       }
     }
@@ -1009,12 +1018,20 @@ void writeAllCodedData(const CodingStructure & cs, const UnitArea & ctuArea)
           if (tu.Y().valid())
           {
             DTRACE_BLOCK_SCALAR(g_trace_ctx, D_BLOCK_STATISTICS_CODED, tu, GetBlockStatisticName(BlockStatistic::Cbf_Y), tu.cbf[COMPONENT_Y]);
+#if JVET_P0058_CHROMA_TS
+            DTRACE_BLOCK_SCALAR(g_trace_ctx, D_BLOCK_STATISTICS_CODED, tu, GetBlockStatisticName(BlockStatistic::MTSIdx_Y), tu.mtsIdx[COMPONENT_Y]);
+#else
             DTRACE_BLOCK_SCALAR( g_trace_ctx, D_BLOCK_STATISTICS_CODED, tu, GetBlockStatisticName( BlockStatistic::MTSIdx ), tu.mtsIdx );
+#endif
           }
           if (!(cu.chromaFormat == CHROMA_400 || (cu.isSepTree() && cu.chType == CHANNEL_TYPE_LUMA)))
           {
             DTRACE_BLOCK_SCALAR_CHROMA(g_trace_ctx, D_BLOCK_STATISTICS_CODED, tu, GetBlockStatisticName(BlockStatistic::Cbf_Cb), tu.cbf[COMPONENT_Cb]);
             DTRACE_BLOCK_SCALAR_CHROMA(g_trace_ctx, D_BLOCK_STATISTICS_CODED, tu, GetBlockStatisticName(BlockStatistic::Cbf_Cr), tu.cbf[COMPONENT_Cr]);
+#if JVET_P0058_CHROMA_TS
+            DTRACE_BLOCK_SCALAR_CHROMA(g_trace_ctx, D_BLOCK_STATISTICS_CODED, tu, GetBlockStatisticName(BlockStatistic::MTSIdx_Cb), tu.mtsIdx[COMPONENT_Cb]);
+            DTRACE_BLOCK_SCALAR_CHROMA(g_trace_ctx, D_BLOCK_STATISTICS_CODED, tu, GetBlockStatisticName(BlockStatistic::MTSIdx_Cr), tu.mtsIdx[COMPONENT_Cr]);
+#endif
           }
         }
       }
diff --git a/source/Lib/CommonLib/dtrace_blockstatistics.h b/source/Lib/CommonLib/dtrace_blockstatistics.h
index b802f1183fd303c02e654cf27dc3ea139f8600db..18e7a4ba4bc548e61b7b91b6e12082f65f0a2a2d 100644
--- a/source/Lib/CommonLib/dtrace_blockstatistics.h
+++ b/source/Lib/CommonLib/dtrace_blockstatistics.h
@@ -64,7 +64,13 @@ enum class BlockStatistic {
   QP,
   SplitSeries,
   TransQuantBypassFlag,
+#if JVET_P0058_CHROMA_TS
+  MTSIdx_Y,
+  MTSIdx_Cb,
+  MTSIdx_Cr,
+#else
   MTSIdx,
+#endif
   BDPCM,
   TileIdx,
   IndependentSliceIdx,
@@ -159,7 +165,13 @@ static const std::map<BlockStatistic, std::tuple<std::string, BlockStatisticType
   { BlockStatistic::Luma_IntraMode,         std::tuple<std::string, BlockStatisticType, std::string>{"Luma_IntraMode",              BlockStatisticType::Integer,                "[0, " + std::to_string(NUM_INTRA_MODE) + "]"}},
   { BlockStatistic::Chroma_IntraMode,       std::tuple<std::string, BlockStatisticType, std::string>{"Chroma_IntraMode",            BlockStatisticType::Integer,                "[0, " + std::to_string(NUM_INTRA_MODE) + "]"}},
   { BlockStatistic::SkipFlag,               std::tuple<std::string, BlockStatisticType, std::string>{"SkipFlag",                    BlockStatisticType::Flag,                   ""}},
+#if JVET_P0058_CHROMA_TS
+  { BlockStatistic::MTSIdx_Y,               std::tuple<std::string, BlockStatisticType, std::string> {"MTS_Y",                      BlockStatisticType::Integer,                ""} },
+  { BlockStatistic::MTSIdx_Cb,               std::tuple<std::string, BlockStatisticType, std::string>{"MTS_Cb",                     BlockStatisticType::Integer,                ""} },
+  { BlockStatistic::MTSIdx_Cr,               std::tuple<std::string, BlockStatisticType, std::string>{"MTS_Cr",                     BlockStatisticType::Integer,                ""} },
+#else
   { BlockStatistic::MTSIdx,                 std::tuple<std::string, BlockStatisticType, std::string>{"TransformSkipFlag_Y",         BlockStatisticType::Integer,                ""}},
+#endif
   { BlockStatistic::BDPCM,                  std::tuple<std::string, BlockStatisticType, std::string>{"BDPCM",                       BlockStatisticType::Flag,                   ""}},    // called bdpcmMode, but used like a flag in the software? related to intra, but signalled always?
   { BlockStatistic::TileIdx,                std::tuple<std::string, BlockStatisticType, std::string>{"TileIdx",                     BlockStatisticType::Integer,                ""}},
   { BlockStatistic::IndependentSliceIdx,    std::tuple<std::string, BlockStatisticType, std::string>{"IndependentSliceIdx",         BlockStatisticType::Integer,                ""}},
diff --git a/source/Lib/DecoderLib/CABACReader.cpp b/source/Lib/DecoderLib/CABACReader.cpp
index db62d52ddc215410ca27e6470c60f7e4862b373c..71321b20de47fecd3c0253ec760061f3cfde903a 100644
--- a/source/Lib/DecoderLib/CABACReader.cpp
+++ b/source/Lib/DecoderLib/CABACReader.cpp
@@ -2914,7 +2914,11 @@ void CABACReader::residual_coding( TransformUnit& tu, ComponentID compID, CUCtx&
   mts_coding         ( tu, compID );
   explicit_rdpcm_mode( tu, compID );
 
+#if JVET_P0058_CHROMA_TS
+  if( tu.mtsIdx[compID] == MTS_SKIP || (tu.cu->bdpcmMode && isLuma(compID) ) )
+#else
   if( isLuma( compID ) && ( tu.mtsIdx == MTS_SKIP || tu.cu->bdpcmMode ) )
+#endif
   {
     residual_codingTS( tu, compID );
     return;
@@ -2922,7 +2926,11 @@ void CABACReader::residual_coding( TransformUnit& tu, ComponentID compID, CUCtx&
 
   // determine sign hiding
   bool signHiding  = ( cu.cs->slice->getSignDataHidingEnabledFlag() && !cu.transQuantBypass && tu.rdpcm[compID] == RDPCM_OFF );
+#if JVET_P0058_CHROMA_TS
+  if(  signHiding && CU::isIntra(cu) && CU::isRDPCMEnabled(cu) && tu.mtsIdx[compID] == MTS_SKIP )
+#else
   if(  signHiding && CU::isIntra(cu) && CU::isRDPCMEnabled(cu) && tu.mtsIdx==MTS_SKIP )
+#endif
   {
     const ChannelType chType    = toChannelType( compID );
     const unsigned    intraMode = PU::getFinalIntraMode( *cu.cs->getPU( tu.blocks[compID].pos(), chType ), chType );
@@ -2938,12 +2946,20 @@ void CABACReader::residual_coding( TransformUnit& tu, ComponentID compID, CUCtx&
 
   // parse last coeff position
   cctx.setScanPosLast( last_sig_coeff( cctx, tu, compID ) );
+#if JVET_P0058_CHROMA_TS
+  if (tu.mtsIdx[compID] != MTS_SKIP && tu.blocks[compID].height >= 4 && tu.blocks[compID].width >= 4 )
+#else
   if( tu.mtsIdx != MTS_SKIP && tu.blocks[ compID ].height >= 4 && tu.blocks[ compID ].width >= 4 )
+#endif
   {
     const int maxLfnstPos = ((tu.blocks[compID].height == 4 && tu.blocks[compID].width == 4) || (tu.blocks[compID].height == 8 && tu.blocks[compID].width == 8)) ? 7 : 15;
     cuCtx.violatesLfnstConstrained[ toChannelType(compID) ] |= cctx.scanPosLast() > maxLfnstPos;
   }
+#if JVET_P0058_CHROMA_TS
+  if( tu.mtsIdx[compID] != MTS_SKIP && tu.blocks[compID].height >= 4 && tu.blocks[compID].width >= 4 )
+#else
   if( tu.mtsIdx != MTS_SKIP && tu.blocks[ compID ].height >= 4 && tu.blocks[ compID ].width >= 4 )
+#endif
   {
     const int lfnstLastScanPosTh = isLuma( compID ) ? LFNST_LAST_SIG_LUMA : LFNST_LAST_SIG_CHROMA;
     cuCtx.lfnstLastScanPos |= cctx.scanPosLast() >= lfnstLastScanPosTh;
@@ -2958,7 +2974,11 @@ void CABACReader::residual_coding( TransformUnit& tu, ComponentID compID, CUCtx&
     for( int subSetId = ( cctx.scanPosLast() >> cctx.log2CGSize() ); subSetId >= 0; subSetId--)
     {
       cctx.initSubblock       ( subSetId );
+#if JVET_P0058_CHROMA_TS
+      if( ( tu.mtsIdx[compID] > MTS_SKIP || (tu.cs->sps->getUseMTS() && tu.cu->sbtInfo != 0 && tu.blocks[compID].height <= 32 && tu.blocks[compID].width <= 32)) && !tu.cu->transQuantBypass && compID == COMPONENT_Y)
+#else
       if( ( tu.mtsIdx > MTS_SKIP || ( tu.cs->sps->getUseMTS() && tu.cu->sbtInfo != 0 && tu.blocks[ compID ].height <= 32 && tu.blocks[ compID ].width <= 32 ) ) && !tu.cu->transQuantBypass && compID == COMPONENT_Y )
+#endif
       {
         if( ( tu.blocks[ compID ].height == 32 && cctx.cgPosY() >= ( 16 >> cctx.log2CGHeight() ) ) || ( tu.blocks[ compID ].width == 32 && cctx.cgPosX() >= ( 16 >> cctx.log2CGWidth() ) ) )
         {
@@ -2975,7 +2995,11 @@ void CABACReader::mts_coding( TransformUnit& tu, ComponentID compID )
   const bool  tsAllowed = TU::isTSAllowed ( tu, compID );
   const bool mtsAllowed = TU::isMTSAllowed( tu, compID );
 
+#if JVET_P0058_CHROMA_TS
+  if ( tu.cu->bdpcmMode && isLuma(compID)) tu.mtsIdx[compID] = MTS_SKIP;
+#else
   if( tu.cu->bdpcmMode ) tu.mtsIdx = MTS_SKIP;
+#endif
   if( !mtsAllowed && !tsAllowed ) return;
 
   RExt__DECODER_DEBUG_BIT_STATISTICS_CREATE_SET_SIZE2( STATS__CABAC_BITS__MTS_FLAGS, tu.blocks[compID], compID );
@@ -2985,12 +3009,24 @@ void CABACReader::mts_coding( TransformUnit& tu, ComponentID compID )
 
   if( tsAllowed )
   {
+#if JVET_P0058_CHROMA_TS
+    ctxIdx = isLuma(compID) ? 6 : 11;
+#else
     ctxIdx = 6;
+#endif
     symbol = m_BinDecoder.decodeBin( Ctx::MTSIndex( ctxIdx ) );
+#if JVET_P0058_CHROMA_TS
+    tu.mtsIdx[compID] = symbol ? MTS_SKIP : MTS_DCT2_DCT2;
+#else
     tu.mtsIdx = symbol ? MTS_SKIP : MTS_DCT2_DCT2;
+#endif
   }
 
+#if JVET_P0058_CHROMA_TS
+  if (tu.mtsIdx[compID] != MTS_SKIP )
+#else
   if( tu.mtsIdx != MTS_SKIP )
+#endif
   {
     if( mtsAllowed )
     {
@@ -3000,12 +3036,19 @@ void CABACReader::mts_coding( TransformUnit& tu, ComponentID compID )
       if( symbol )
       {
         ctxIdx    = 7;
+#if JVET_P0058_CHROMA_TS
+        tu.mtsIdx[compID] = MTS_DST7_DST7; // mtsIdx = 2 -- 4
+#else
         tu.mtsIdx = MTS_DST7_DST7; // mtsIdx = 2 -- 4
+#endif
         for( int i = 0; i < 3; i++, ctxIdx++ )
         {
           symbol = m_BinDecoder.decodeBin( Ctx::MTSIndex( ctxIdx ) );
+#if JVET_P0058_CHROMA_TS
+          tu.mtsIdx[compID] += symbol;
+#else
           tu.mtsIdx += symbol;
-
+#endif
           if( !symbol )
           {
             break;
@@ -3014,7 +3057,11 @@ void CABACReader::mts_coding( TransformUnit& tu, ComponentID compID )
       }
     }
   }
+#if JVET_P0058_CHROMA_TS
+  DTRACE( g_trace_ctx, D_SYNTAX, "mts_coding() etype=%d pos=(%d,%d) mtsIdx=%d\n", compID, tu.cu->lx(), tu.cu->ly(), tu.mtsIdx[compID]);
+#else
   DTRACE(g_trace_ctx, D_SYNTAX, "mts_coding() etype=%d pos=(%d,%d) mtsIdx=%d\n", COMPONENT_Y, tu.cu->lx(), tu.cu->ly(), tu.mtsIdx);
+#endif
 }
 
 void CABACReader::isp_mode( CodingUnit& cu )
@@ -3043,7 +3090,11 @@ void CABACReader::explicit_rdpcm_mode( TransformUnit& tu, ComponentID compID )
 
   tu.rdpcm[compID] = RDPCM_OFF;
 
+#if JVET_P0058_CHROMA_TS
+  if (!CU::isIntra(cu) && CU::isRDPCMEnabled(cu) && ( tu.mtsIdx[compID] == MTS_SKIP || cu.transQuantBypass))
+#else
   if( !CU::isIntra(cu) && CU::isRDPCMEnabled(cu) && ( tu.mtsIdx==MTS_SKIP || cu.transQuantBypass ) )
+#endif
   {
     RExt__DECODER_DEBUG_BIT_STATISTICS_CREATE_SET_SIZE( STATS__EXPLICIT_RDPCM_BITS, tu.blocks[tu.chType].lumaSize() );
 
@@ -3081,7 +3132,11 @@ void CABACReader::residual_lfnst_mode( CodingUnit& cu,  CUCtx& cuCtx  )
     const bool lumaFlag              = cu.isSepTree() ? (   isLuma( cu.chType ) ? true : false ) : true;
     const bool chromaFlag            = cu.isSepTree() ? ( isChroma( cu.chType ) ? true : false ) : true;
     bool nonZeroCoeffNonTsCorner8x8 = ( lumaFlag && cuCtx.violatesLfnstConstrained[CHANNEL_TYPE_LUMA] ) || (chromaFlag && cuCtx.violatesLfnstConstrained[CHANNEL_TYPE_CHROMA] );
+#if JVET_P0058_CHROMA_TS
+    const bool isNonDCT2 = (TU::getCbf(*cu.firstTU, ComponentID(COMPONENT_Y)) && cu.firstTU->mtsIdx[COMPONENT_Y] != MTS_DCT2_DCT2);
+#else
     const bool isNonDCT2 = (TU::getCbf(*cu.firstTU, ComponentID(COMPONENT_Y)) && cu.firstTU->mtsIdx != MTS_DCT2_DCT2);
+#endif
     if( !cuCtx.lfnstLastScanPos || nonZeroCoeffNonTsCorner8x8 || isNonDCT2 )
     {
       cu.lfnstIdx = 0;
@@ -3116,7 +3171,11 @@ int CABACReader::last_sig_coeff( CoeffCodingContext& cctx, TransformUnit& tu, Co
   unsigned maxLastPosX = cctx.maxLastPosX();
   unsigned maxLastPosY = cctx.maxLastPosY();
 
+#if JVET_P0058_CHROMA_TS
+  if( ( tu.mtsIdx[compID] > MTS_SKIP || (tu.cs->sps->getUseMTS() && tu.cu->sbtInfo != 0 && tu.blocks[compID].width <= 32 && tu.blocks[compID].height <= 32)) && !tu.cu->transQuantBypass && compID == COMPONENT_Y)
+#else
   if( ( tu.mtsIdx > MTS_SKIP || ( tu.cs->sps->getUseMTS() && tu.cu->sbtInfo != 0 && tu.blocks[ compID ].width <= 32 && tu.blocks[ compID ].height <= 32 ) ) && !tu.cu->transQuantBypass && compID == COMPONENT_Y )
+#endif
   {
     maxLastPosX = ( tu.blocks[ compID ].width  == 32 ) ? g_uiGroupIdx[ 15 ] : maxLastPosX;
     maxLastPosY = ( tu.blocks[ compID ].height == 32 ) ? g_uiGroupIdx[ 15 ] : maxLastPosY;
diff --git a/source/Lib/EncoderLib/CABACWriter.cpp b/source/Lib/EncoderLib/CABACWriter.cpp
index d45a177a094ccd0c45566204ef486cc12eb8c5fe..bcaf7e21c3430334cd2022b9122aee7fcb02271b 100644
--- a/source/Lib/EncoderLib/CABACWriter.cpp
+++ b/source/Lib/EncoderLib/CABACWriter.cpp
@@ -2684,7 +2684,11 @@ void CABACWriter::residual_coding( const TransformUnit& tu, ComponentID compID,
   mts_coding         ( tu, compID );
   explicit_rdpcm_mode( tu, compID );
 
+#if JVET_P0058_CHROMA_TS
+  if (tu.mtsIdx[compID] == MTS_SKIP || (tu.cu->bdpcmMode && isLuma(compID)))
+#else
   if( isLuma( compID ) && ( tu.mtsIdx == MTS_SKIP || tu.cu->bdpcmMode ) )
+#endif
   {
     residual_codingTS( tu, compID );
     return;
@@ -2692,7 +2696,11 @@ void CABACWriter::residual_coding( const TransformUnit& tu, ComponentID compID,
 
   // determine sign hiding
   bool signHiding  = ( cu.cs->slice->getSignDataHidingEnabledFlag() && !cu.transQuantBypass && tu.rdpcm[compID] == RDPCM_OFF );
+#if JVET_P0058_CHROMA_TS
+  if(  signHiding && CU::isIntra(cu) && CU::isRDPCMEnabled(cu) && tu.mtsIdx[compID] == MTS_SKIP)
+#else
   if(  signHiding && CU::isIntra(cu) && CU::isRDPCMEnabled(cu) && tu.mtsIdx==MTS_SKIP )
+#endif
   {
     const ChannelType chType    = toChannelType( compID );
     const unsigned    intraMode = PU::getFinalIntraMode( *cu.cs->getPU( tu.blocks[compID].pos(), chType ), chType );
@@ -2721,12 +2729,20 @@ void CABACWriter::residual_coding( const TransformUnit& tu, ComponentID compID,
   CHECK( scanPosLast < 0, "Coefficient coding called for empty TU" );
   cctx.setScanPosLast(scanPosLast);
 
+#if JVET_P0058_CHROMA_TS
+  if (cuCtx && tu.mtsIdx[compID] != MTS_SKIP && tu.blocks[compID].height >= 4 && tu.blocks[compID].width >= 4)
+#else
   if( cuCtx && tu.mtsIdx != MTS_SKIP && tu.blocks[ compID ].height >= 4 && tu.blocks[ compID ].width >= 4 )
+#endif
   {
     const int maxLfnstPos = ((tu.blocks[compID].height == 4 && tu.blocks[compID].width == 4) || (tu.blocks[compID].height == 8 && tu.blocks[compID].width == 8)) ? 7 : 15;
     cuCtx->violatesLfnstConstrained[ toChannelType(compID) ] |= cctx.scanPosLast() > maxLfnstPos;
   }
+#if JVET_P0058_CHROMA_TS
+  if (cuCtx && tu.mtsIdx[compID] != MTS_SKIP && tu.blocks[compID].height >= 4 && tu.blocks[compID].width >= 4)
+#else
   if( cuCtx && tu.mtsIdx != MTS_SKIP && tu.blocks[ compID ].height >= 4 && tu.blocks[ compID ].width >= 4 )
+#endif
   {
     const int lfnstLastScanPosTh = isLuma( compID ) ? LFNST_LAST_SIG_LUMA : LFNST_LAST_SIG_CHROMA;
     cuCtx->lfnstLastScanPos |= cctx.scanPosLast() >= lfnstLastScanPosTh;
@@ -2744,7 +2760,11 @@ void CABACWriter::residual_coding( const TransformUnit& tu, ComponentID compID,
   for( int subSetId = ( cctx.scanPosLast() >> cctx.log2CGSize() ); subSetId >= 0; subSetId--)
   {
     cctx.initSubblock       ( subSetId, sigGroupFlags[subSetId] );
+#if JVET_P0058_CHROMA_TS
+    if( ( tu.mtsIdx[compID] > MTS_SKIP || (tu.cs->sps->getUseMTS() && tu.cu->sbtInfo != 0 && tu.blocks[compID].height <= 32 && tu.blocks[compID].width <= 32)) && !tu.cu->transQuantBypass && compID == COMPONENT_Y)
+#else
     if( ( tu.mtsIdx > MTS_SKIP || ( tu.cs->sps->getUseMTS() && tu.cu->sbtInfo != 0 && tu.blocks[ compID ].height <= 32 && tu.blocks[ compID ].width <= 32 ) ) && !tu.cu->transQuantBypass && compID == COMPONENT_Y )
+#endif    
     {
       if( ( tu.blocks[ compID ].height == 32 && cctx.cgPosY() >= ( 16 >> cctx.log2CGHeight() ) )
        || ( tu.blocks[ compID ].width  == 32 && cctx.cgPosX() >= ( 16 >> cctx.log2CGWidth()  ) ) )
@@ -2753,10 +2773,7 @@ void CABACWriter::residual_coding( const TransformUnit& tu, ComponentID compID,
       }
     }
     residual_coding_subblock( cctx, coeff, stateTab, state );
-
   }
-
-
 }
 
 void CABACWriter::mts_coding( const TransformUnit& tu, ComponentID compID )
@@ -2771,16 +2788,33 @@ void CABACWriter::mts_coding( const TransformUnit& tu, ComponentID compID )
 
   if( tsAllowed )
   {
+#if JVET_P0058_CHROMA_TS
+    symbol = (tu.mtsIdx[compID] == MTS_SKIP) ? 1 : 0;
+#else
     symbol = (tu.mtsIdx == MTS_SKIP) ? 1 : 0;
+#endif
+
+#if JVET_P0058_CHROMA_TS
+    ctxIdx = isLuma(compID) ? 6 : 11;
+#else
     ctxIdx = 6;
+#endif
     m_BinEncoder.encodeBin( symbol, Ctx::MTSIndex( ctxIdx ) );
   }
 
+#if JVET_P0058_CHROMA_TS
+  if( tu.mtsIdx[compID] != MTS_SKIP )
+#else
   if( tu.mtsIdx != MTS_SKIP )
+#endif
   {
     if( mtsAllowed )
     {
+#if JVET_P0058_CHROMA_TS
+      symbol = tu.mtsIdx[compID] != MTS_DCT2_DCT2 ? 1 : 0;
+#else
       symbol = tu.mtsIdx != MTS_DCT2_DCT2 ? 1 : 0;
+#endif
       ctxIdx = 0;
       m_BinEncoder.encodeBin( symbol, Ctx::MTSIndex( ctxIdx ) );
 
@@ -2789,7 +2823,11 @@ void CABACWriter::mts_coding( const TransformUnit& tu, ComponentID compID )
         ctxIdx = 7;
         for( int i = 0; i < 3; i++, ctxIdx++ )
         {
+#if JVET_P0058_CHROMA_TS
+          symbol = tu.mtsIdx[compID] > i + MTS_DST7_DST7 ? 1 : 0;
+#else
           symbol = tu.mtsIdx > i + MTS_DST7_DST7 ? 1 : 0;
+#endif
           m_BinEncoder.encodeBin( symbol, Ctx::MTSIndex( ctxIdx ) );
 
           if( !symbol )
@@ -2800,7 +2838,11 @@ void CABACWriter::mts_coding( const TransformUnit& tu, ComponentID compID )
       }
     }
   }
+#if JVET_P0058_CHROMA_TS
+  DTRACE( g_trace_ctx, D_SYNTAX, "mts_coding() etype=%d pos=(%d,%d) mtsIdx=%d\n", compID, tu.cu->lx(), tu.cu->ly(), tu.mtsIdx[compID]);
+#else
   DTRACE( g_trace_ctx, D_SYNTAX, "mts_coding() etype=%d pos=(%d,%d) mtsIdx=%d\n", COMPONENT_Y, tu.cu->lx(), tu.cu->ly(), tu.mtsIdx);
+#endif
 }
 
 void CABACWriter::isp_mode( const CodingUnit& cu )
@@ -2825,7 +2867,11 @@ void CABACWriter::isp_mode( const CodingUnit& cu )
 void CABACWriter::explicit_rdpcm_mode( const TransformUnit& tu, ComponentID compID )
 {
   const CodingUnit& cu = *tu.cu;
+#if JVET_P0058_CHROMA_TS
+  if (!CU::isIntra(cu) && CU::isRDPCMEnabled(cu) && (tu.mtsIdx[compID] == MTS_SKIP || cu.transQuantBypass))
+#else
   if( !CU::isIntra(cu) && CU::isRDPCMEnabled(cu) && ( tu.mtsIdx==MTS_SKIP || cu.transQuantBypass ) )
+#endif
   {
     ChannelType chType = toChannelType( compID );
     switch( tu.rdpcm[compID] )
@@ -2861,7 +2907,11 @@ void CABACWriter::residual_lfnst_mode( const CodingUnit& cu, CUCtx& cuCtx )
     const bool lumaFlag                   = cu.isSepTree() ? (   isLuma( cu.chType ) ? true : false ) : true;
     const bool chromaFlag                 = cu.isSepTree() ? ( isChroma( cu.chType ) ? true : false ) : true;
           bool nonZeroCoeffNonTsCorner8x8 = ( lumaFlag && cuCtx.violatesLfnstConstrained[CHANNEL_TYPE_LUMA] ) || (chromaFlag && cuCtx.violatesLfnstConstrained[CHANNEL_TYPE_CHROMA] );
+#if JVET_P0058_CHROMA_TS
+    const bool isNonDCT2 = (TU::getCbf(*cu.firstTU, ComponentID(COMPONENT_Y)) && cu.firstTU->mtsIdx[COMPONENT_Y] != MTS_DCT2_DCT2);
+#else
     const bool isNonDCT2 = (TU::getCbf(*cu.firstTU, ComponentID(COMPONENT_Y)) && cu.firstTU->mtsIdx != MTS_DCT2_DCT2);
+#endif
     if( !cuCtx.lfnstLastScanPos || nonZeroCoeffNonTsCorner8x8 || isNonDCT2 )
     {
       return;
@@ -2904,7 +2954,11 @@ void CABACWriter::last_sig_coeff( CoeffCodingContext& cctx, const TransformUnit&
   unsigned maxLastPosX = cctx.maxLastPosX();
   unsigned maxLastPosY = cctx.maxLastPosY();
 
+#if JVET_P0058_CHROMA_TS
+  if ((tu.mtsIdx[compID] > MTS_SKIP || (tu.cs->sps->getUseMTS() && tu.cu->sbtInfo != 0 && tu.blocks[compID].width <= 32 && tu.blocks[compID].height <= 32)) && !tu.cu->transQuantBypass && compID == COMPONENT_Y)
+#else
   if( ( tu.mtsIdx > MTS_SKIP || ( tu.cs->sps->getUseMTS() && tu.cu->sbtInfo != 0 && tu.blocks[ compID ].width <= 32 && tu.blocks[ compID ].height <= 32 ) ) && !tu.cu->transQuantBypass && compID == COMPONENT_Y )
+#endif
   {
     maxLastPosX = ( tu.blocks[compID].width  == 32 ) ? g_uiGroupIdx[ 15 ] : maxLastPosX;
     maxLastPosY = ( tu.blocks[compID].height == 32 ) ? g_uiGroupIdx[ 15 ] : maxLastPosY;
diff --git a/source/Lib/EncoderLib/EncCfg.h b/source/Lib/EncoderLib/EncCfg.h
index 7d899ae5b632e6f928de9759ee4e9e6b20fc5903..40b26e02a855c9768b3accc6ac0916936248efb6 100644
--- a/source/Lib/EncoderLib/EncCfg.h
+++ b/source/Lib/EncoderLib/EncCfg.h
@@ -434,6 +434,9 @@ protected:
   uint32_t      m_log2SaoOffsetScale[MAX_NUM_CHANNEL_TYPE];
   bool      m_useTransformSkip;
   bool      m_useTransformSkipFast;
+#if JVET_P0058_CHROMA_TS
+  bool      m_useChromaTS;
+#endif
   bool      m_useBDPCM;
   uint32_t      m_log2MaxTransformSkipBlockSize;
   bool      m_transformSkipRotationEnabledFlag;
@@ -1221,6 +1224,10 @@ public:
   void setTransformSkipRotationEnabledFlag             (const bool value)  { m_transformSkipRotationEnabledFlag = value; }
   bool getTransformSkipContextEnabledFlag              ()            const { return m_transformSkipContextEnabledFlag;  }
   void setTransformSkipContextEnabledFlag              (const bool value)  { m_transformSkipContextEnabledFlag = value; }
+#if JVET_P0058_CHROMA_TS
+  bool getUseChromaTS                                  ()       { return m_useChromaTS; }
+  void setUseChromaTS                                  (bool b) { m_useChromaTS = b; }
+#endif
   bool getUseBDPCM                                     ()         { return m_useBDPCM; }
   void setUseBDPCM                                     ( bool b ) { m_useBDPCM  = b;   }
   bool getUseJointCbCr                                 ()         { return m_JointCbCrMode; }
diff --git a/source/Lib/EncoderLib/EncCu.cpp b/source/Lib/EncoderLib/EncCu.cpp
index 39f28c445a19896b1fe384c73737bd837c4f53b6..5788126d7cb38d387640999f02417e23b7daed78 100644
--- a/source/Lib/EncoderLib/EncCu.cpp
+++ b/source/Lib/EncoderLib/EncCu.cpp
@@ -1923,7 +1923,11 @@ void EncCu::xCheckRDCostIntra( CodingStructure *&tempCS, CodingStructure *&bestC
               if( bestCS->cus.size() == 1 )
               {
                 CodingUnit &cu = *bestCS->cus.front();
+#if JVET_P0058_CHROMA_TS
+                if (cu.firstTU->mtsIdx[COMPONENT_Y] == MTS_SKIP)
+#else
                 if( cu.firstTU->mtsIdx == MTS_SKIP )
+#endif
                 {
                   if( ( floorLog2( cu.firstTU->blocks[ COMPONENT_Y ].width ) + floorLog2( cu.firstTU->blocks[ COMPONENT_Y ].height ) ) >= 6 )
                   {
@@ -4345,8 +4349,13 @@ void EncCu::xEncodeInterResidual(   CodingStructure *&tempCS
     sbtOffCost = tempCS->cost;
     sbtOffDist = tempCS->dist;
     sbtOffRootCbf = cu->rootCbf;
+#if JVET_P0058_CHROMA_TS
+    currBestSbt = CU::getSbtInfo(cu->firstTU->mtsIdx[COMPONENT_Y] > MTS_SKIP ? SBT_OFF_MTS : SBT_OFF_DCT, 0);
+    currBestTrs = cu->firstTU->mtsIdx[COMPONENT_Y];
+#else
     currBestSbt = CU::getSbtInfo( cu->firstTU->mtsIdx > MTS_SKIP ? SBT_OFF_MTS : SBT_OFF_DCT, 0 );
     currBestTrs = cu->firstTU->mtsIdx;
+#endif
 
 #if WCG_EXT
     DTRACE_MODE_COST( *tempCS, m_pcRdCost->getLambda( true ) );
@@ -4477,7 +4486,11 @@ void EncCu::xEncodeInterResidual(   CodingStructure *&tempCS
       if( tempCS->cost < currBestCost )
       {
         currBestSbt = cu->sbtInfo;
+#if JVET_P0058_CHROMA_TS
+        currBestTrs = tempCS->tus[cu->sbtInfo ? cu->getSbtPos() : 0]->mtsIdx[COMPONENT_Y];
+#else
         currBestTrs = tempCS->tus[cu->sbtInfo ? cu->getSbtPos() : 0]->mtsIdx;
+#endif
         assert( currBestTrs == 0 || currBestTrs == 1 );
         currBestCost = tempCS->cost;
       }
diff --git a/source/Lib/EncoderLib/InterSearch.cpp b/source/Lib/EncoderLib/InterSearch.cpp
index e5f6ec1e8eb5dfecac51c2848163c243a533219b..d676186951573d7be8a93f6ba38d258e7c39a4d4 100644
--- a/source/Lib/EncoderLib/InterSearch.cpp
+++ b/source/Lib/EncoderLib/InterSearch.cpp
@@ -6504,7 +6504,11 @@ void InterSearch::xEstimateInterResidualQT(CodingStructure &cs, Partitioner &par
   {
     TransformUnit &tu = csFull->addTU(CS::getArea(cs, currArea, partitioner.chType), partitioner.chType);
     tu.depth          = currDepth;
+#if JVET_P0058_CHROMA_TS
+    for (int i = 0; i<MAX_NUM_TBLOCKS; i++) tu.mtsIdx[i] = MTS_DCT2_DCT2;
+#else
     tu.mtsIdx         = MTS_DCT2_DCT2;
+#endif
     tu.checkTuNoResidual( partitioner.currPartIdx() );
 
     const Slice           &slice = *cs.slice;
@@ -6560,7 +6564,11 @@ void InterSearch::xEstimateInterResidualQT(CodingStructure &cs, Partitioner &par
         preCalcAlpha = xCalcCrossComponentPredictionAlpha( tu, compID, m_pcEncCfg->getUseReconBasedCrossCPredictionEstimate() );
       }
 
+#if JVET_P0058_CHROMA_TS
+      const bool tsAllowed  = TU::isTSAllowed(tu, compID) && (isLuma(compID) || (isChroma(compID) && m_pcEncCfg->getUseChromaTS()));
+#else
       const bool tsAllowed  = TU::isTSAllowed ( tu, compID );
+#endif
       const bool mtsAllowed = TU::isMTSAllowed( tu, compID );
       uint8_t nNumTransformCands = 1 + ( tsAllowed ? 1 : 0 ) + ( mtsAllowed ? 4 : 0 ); // DCT + TS + 4 MTS = 6 tests
       std::vector<TrMode> trModes;
@@ -6613,9 +6621,16 @@ void InterSearch::xEstimateInterResidualQT(CodingStructure &cs, Partitioner &par
           m_CABACEstimator->getCtx() = ctxStart;
           m_CABACEstimator->resetBits();
 
+#if JVET_P0058_CHROMA_TS
+#else
           if( isLuma( compID ) )
+#endif
           {
+#if JVET_P0058_CHROMA_TS
+            if (bestTU.mtsIdx[compID] == MTS_SKIP && m_pcEncCfg->getUseTransformSkipFast())
+#else
             if( bestTU.mtsIdx == MTS_SKIP && m_pcEncCfg->getUseTransformSkipFast() )
+#endif
             {
               continue;
             }
@@ -6623,7 +6638,11 @@ void InterSearch::xEstimateInterResidualQT(CodingStructure &cs, Partitioner &par
             {
               continue;
             }
+#if JVET_P0058_CHROMA_TS
+            tu.mtsIdx[compID] = trModes[transformMode].first;
+#else
             tu.mtsIdx = trModes[transformMode].first;
+#endif
           }
           tu.compAlpha[compID]      = bUseCrossCPrediction ? preCalcAlpha : 0;
 
@@ -6669,7 +6688,11 @@ void InterSearch::xEstimateInterResidualQT(CodingStructure &cs, Partitioner &par
 #else
               m_pcTrQuant->transformNxN( tu, compID, cQP, &trModes, CU::isIntra( *tu.cu ) ? m_pcEncCfg->getIntraMTSMaxCand() : m_pcEncCfg->getInterMTSMaxCand() );
 #endif
+#if JVET_P0058_CHROMA_TS
+              tu.mtsIdx[compID] = trModes[0].first;
+#else
               tu.mtsIdx = trModes[0].first;
+#endif
             }
             m_pcTrQuant->transformNxN( tu, compID, cQP, currAbsSum, m_CABACEstimator->getCtx(), true );
           }
@@ -6876,7 +6899,10 @@ void InterSearch::xEstimateInterResidualQT(CodingStructure &cs, Partitioner &par
 
         tu.jointCbCr = (uint8_t) cbfMask;
         tu.compAlpha[COMPONENT_Cb] = tu.compAlpha[COMPONENT_Cr] = 0;
-
+#if JVET_P0058_CHROMA_TS
+        // encoder bugfix: initialize mtsIdx for chroma under JointCbCrMode.
+        tu.mtsIdx[COMPONENT_Cb] = tu.mtsIdx[COMPONENT_Cr] = MTS_DCT2_DCT2;
+#endif
         const QpParam cQP(tu, COMPONENT_Cb);  // note: uses tu.transformSkip[compID]
 
 #if RDOQ_CHROMA_LAMBDA
diff --git a/source/Lib/EncoderLib/IntraSearch.cpp b/source/Lib/EncoderLib/IntraSearch.cpp
index d93c8f81d79d326affd17c4b8bde0cba90713cb6..0dacb9e318ef0b15453b60749656f021d0e168a2 100644
--- a/source/Lib/EncoderLib/IntraSearch.cpp
+++ b/source/Lib/EncoderLib/IntraSearch.cpp
@@ -1156,9 +1156,16 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
       {
         modeIsEnable[i] = 1;
       }
-
+#if JVET_P0058_CHROMA_TS_ENCODER_INTRA_SAD_MOD
+      DistParam distParamSad;
+      DistParam distParamSatd;
+#else
       DistParam distParam;
+#endif
+#if JVET_P0058_CHROMA_TS_ENCODER_INTRA_SAD_MOD
+#else
       const bool useHadamard = !cu.transQuantBypass;
+#endif
       pu.intraDir[1] = MDLM_L_IDX; // temporary assigned, just to indicate this is a MDLM mode. for luma down-sampling operation.
 
       initIntraPatternChType(cu, pu.Cb());
@@ -1179,16 +1186,32 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
         }
         pu.intraDir[1] = mode; // temporary assigned, for SATD checking.
 
+#if JVET_P0058_CHROMA_TS_ENCODER_INTRA_SAD_MOD
+        int64_t sad = 0;
+        int64_t sadCb = 0;
+        int64_t satdCb = 0;
+        int64_t sadCr = 0;
+        int64_t satdCr = 0;
+#else
         int64_t sad = 0;
+#endif
         CodingStructure& cs = *(pu.cs);
 
         CompArea areaCb = pu.Cb();
         PelBuf orgCb = cs.getOrgBuf(areaCb);
         PelBuf predCb = cs.getPredBuf(areaCb);
-
+#if JVET_P0058_CHROMA_TS_ENCODER_INTRA_SAD_MOD
+        m_pcRdCost->setDistParam(distParamSad, orgCb, predCb, pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA), COMPONENT_Cb, false);
+        m_pcRdCost->setDistParam(distParamSatd, orgCb, predCb, pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA), COMPONENT_Cb, true);
+#else
         m_pcRdCost->setDistParam(distParam, orgCb, predCb, pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA), COMPONENT_Cb, useHadamard);
+#endif
+#if JVET_P0058_CHROMA_TS_ENCODER_INTRA_SAD_MOD
+        distParamSad.applyWeight = false;
+        distParamSatd.applyWeight = false;
+#else
         distParam.applyWeight = false;
-
+#endif
         if (PU::isLMCMode(mode))
         {
           predIntraChromaLM(COMPONENT_Cb, predCb, pu, areaCb, mode);
@@ -1198,16 +1221,28 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
           initPredIntraParams(pu, pu.Cb(), *pu.cs->sps);
           predIntraAng(COMPONENT_Cb, predCb, pu);
         }
-
+#if JVET_P0058_CHROMA_TS_ENCODER_INTRA_SAD_MOD
+        sadCb = distParamSad.distFunc(distParamSad) * 2;
+        satdCb = distParamSatd.distFunc(distParamSatd);
+        sad += std::min(sadCb, satdCb);
+#else
         sad += distParam.distFunc(distParam);
-
+#endif
         CompArea areaCr = pu.Cr();
         PelBuf orgCr = cs.getOrgBuf(areaCr);
         PelBuf predCr = cs.getPredBuf(areaCr);
-
+#if JVET_P0058_CHROMA_TS_ENCODER_INTRA_SAD_MOD
+        m_pcRdCost->setDistParam(distParamSad, orgCr, predCr, pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA), COMPONENT_Cr, false);
+        m_pcRdCost->setDistParam(distParamSatd, orgCr, predCr, pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA), COMPONENT_Cr, true);
+#else
         m_pcRdCost->setDistParam(distParam, orgCr, predCr, pu.cs->sps->getBitDepth(CHANNEL_TYPE_CHROMA), COMPONENT_Cr, useHadamard);
+#endif
+#if JVET_P0058_CHROMA_TS_ENCODER_INTRA_SAD_MOD
+        distParamSad.applyWeight = false;
+        distParamSatd.applyWeight = false;
+#else
         distParam.applyWeight = false;
-
+#endif
         if (PU::isLMCMode(mode))
         {
           predIntraChromaLM(COMPONENT_Cr, predCr, pu, areaCr, mode);
@@ -1217,7 +1252,13 @@ void IntraSearch::estIntraPredChromaQT( CodingUnit &cu, Partitioner &partitioner
           initPredIntraParams(pu, pu.Cr(), *pu.cs->sps);
           predIntraAng(COMPONENT_Cr, predCr, pu);
         }
+#if JVET_P0058_CHROMA_TS_ENCODER_INTRA_SAD_MOD
+        sadCr = distParamSad.distFunc(distParamSad) * 2;
+        satdCr = distParamSatd.distFunc(distParamSatd);
+        sad += std::min(sadCr, satdCr);
+#else
         sad += distParam.distFunc(distParam);
+#endif
         satdSortedCost[idx] = sad;
       }
       // sort the mode based on the cost from small to large.
@@ -2427,7 +2468,11 @@ void IntraSearch::xIntraCodingTUBlock(TransformUnit &tu, const ComponentID &comp
 #else
       m_pcTrQuant->transformNxN(tu, compID, cQP, trModes, CU::isIntra(*tu.cu) ? m_pcEncCfg->getIntraMTSMaxCand() : m_pcEncCfg->getInterMTSMaxCand());
 #endif
+#if JVET_P0058_CHROMA_TS
+      tu.mtsIdx[compID] = trModes->at(0).first;
+#else
       tu.mtsIdx = trModes->at(0).first;
+#endif
     }
     m_pcTrQuant->transformNxN(tu, compID, cQP, uiAbsSum, m_CABACEstimator->getCtx(), loadTr);
 
@@ -2466,7 +2511,24 @@ void IntraSearch::xIntraCodingTUBlock(TransformUnit &tu, const ComponentID &comp
     }
     PelBuf& codeResi = ( codeCompId == COMPONENT_Cr ? crResi : piResi );
     uiAbsSum = 0;
+
+#if JVET_P0058_CHROMA_TS
+    if (trModes)
+    {
+#if JVET_P0273_MTSIntraMaxCand
+        m_pcTrQuant->transformNxN(tu, compID, qpCbCr, trModes, m_pcEncCfg->getMTSIntraMaxCand());
+#else
+        m_pcTrQuant->transformNxN(tu, compID, qpCbCr, trModes, CU::isIntra(*tu.cu) ? m_pcEncCfg->getIntraMTSMaxCand() : m_pcEncCfg->getInterMTSMaxCand());
+#endif
+        tu.mtsIdx[compID] = trModes->at(0).first;
+    }
+#endif
+#if JVET_P0058_CHROMA_TS
+    // encoder bugfix: Set loadTr to aovid redundant transform process
+    m_pcTrQuant->transformNxN(tu, codeCompId, qpCbCr, uiAbsSum, m_CABACEstimator->getCtx(), loadTr);
+#else
     m_pcTrQuant->transformNxN(tu, codeCompId, qpCbCr, uiAbsSum, m_CABACEstimator->getCtx());
+#endif
     DTRACE( g_trace_ctx, D_TU_ABS_SUM, "%d: comp=%d, abssum=%d\n", DTRACE_GET_COUNTER( g_trace_ctx, D_TU_ABS_SUM ), codeCompId, uiAbsSum );
     if( uiAbsSum > 0 )
     {
@@ -2810,7 +2872,11 @@ bool IntraSearch::xRecurIntraCodingLumaQT( CodingStructure &cs, Partitioner &par
       }
       else
       {
+#if JVET_P0058_CHROMA_TS
+        if( !cbfDCT2 || ( m_pcEncCfg->getUseTransformSkipFast() && bestModeId[ COMPONENT_Y ] == MTS_SKIP))
+#else
         if( !cbfDCT2 || ( m_pcEncCfg->getUseTransformSkipFast() && bestModeId[ COMPONENT_Y ] == 1 ) )
+#endif
         {
           break;
         }
@@ -2819,11 +2885,19 @@ bool IntraSearch::xRecurIntraCodingLumaQT( CodingStructure &cs, Partitioner &par
           continue;
         }
         //we compare the DCT-II cost against the best ISP cost so far (except for TS)
+#if JVET_P0058_CHROMA_TS
+        if (m_pcEncCfg->getUseFastISP() && !cu.ispMode && ispIsCurrentWinner && trModes[modeId].first != MTS_DCT2_DCT2 && (trModes[modeId].first != MTS_SKIP || !tsAllowed) && bestDCT2cost > bestCostSoFar * threshold)
+#else
         if( m_pcEncCfg->getUseFastISP() && !cu.ispMode && ispIsCurrentWinner && trModes[ modeId ].first != 0 && ( trModes[ modeId ].first != 1 || !tsAllowed ) && bestDCT2cost > bestCostSoFar * threshold )
+#endif
         {
           continue;
         }
+#if JVET_P0058_CHROMA_TS
+        tu.mtsIdx[COMPONENT_Y] = trModes[modeId].first;
+#else
         tu.mtsIdx = trModes[ modeId ].first;
+#endif
       }
 
 
@@ -2867,25 +2941,45 @@ bool IntraSearch::xRecurIntraCodingLumaQT( CodingStructure &cs, Partitioner &par
 
             if( transformIndex == 1 )
             {
+#if JVET_P0058_CHROMA_TS
+              tu.mtsIdx[COMPONENT_Y] = (uiIntraMode < 34) ? MTS_DST7_DCT8 : MTS_DCT8_DST7;
+#else
               tu.mtsIdx = ( uiIntraMode < 34 ) ? MTS_DST7_DCT8 : MTS_DCT8_DST7;
+#endif
             }
             else if( transformIndex == 2 )
             {
+#if JVET_P0058_CHROMA_TS
+              tu.mtsIdx[COMPONENT_Y] = (uiIntraMode < 34) ? MTS_DCT8_DST7 : MTS_DST7_DCT8;
+#else
               tu.mtsIdx = ( uiIntraMode < 34 ) ? MTS_DCT8_DST7 : MTS_DST7_DCT8;
+#endif
             }
             else
             {
+#if JVET_P0058_CHROMA_TS
+              tu.mtsIdx[COMPONENT_Y] = MTS_DST7_DST7 + transformIndex;
+#else
               tu.mtsIdx = MTS_DST7_DST7 + transformIndex;
+#endif
             }
           }
           else
           {
+#if JVET_P0058_CHROMA_TS
+            tu.mtsIdx[COMPONENT_Y] = MTS_DST7_DST7 + transformIndex;
+#else
             tu.mtsIdx = MTS_DST7_DST7 + transformIndex;
+#endif
           }
         }
         else
         {
+#if JVET_P0058_CHROMA_TS
+          tu.mtsIdx[COMPONENT_Y] = transformIndex;
+#else
           tu.mtsIdx = transformIndex;
+#endif
         }
 
         if( !cu.mtsFlag && checkTransformSkip )
@@ -3299,9 +3393,26 @@ ChromaCbfs IntraSearch::xRecurIntraChromaCodingQT( CodingStructure &cs, Partitio
       int        bestModeId     = 0;
       Distortion singleDistCTmp = 0;
       double     singleCostTmp  = 0;
-
       const int  crossCPredictionModesToTest = checkCrossComponentPrediction ? 2 : 1;
+#if JVET_P0058_CHROMA_TS
+      const bool tsAllowed = TU::isTSAllowed(currTU, compID) && (m_pcEncCfg->getUseChromaTS());
+      uint8_t nNumTransformCands = 1 + (tsAllowed ? 1 : 0); // DCT + TS = 2 tests
+      std::vector<TrMode> trModes;
+      trModes.push_back(TrMode(0, true)); // DCT2
+
+      if (tsAllowed)
+      {
+          trModes.push_back(TrMode(1, true));//TS
+      }
+      CHECK(!currTU.Cb().valid(), "Invalid TU");
+#endif
+
+#if JVET_P0058_CHROMA_TS
+      const int  totalModesToTest            = crossCPredictionModesToTest * nNumTransformCands;
+      bool cbfDCT2 = true;
+#else
       const int  totalModesToTest            = crossCPredictionModesToTest;
+#endif
       const bool isOneMode                   = false;
       maxModesTested                         = totalModesToTest > maxModesTested ? totalModesToTest : maxModesTested;
 
@@ -3314,6 +3425,9 @@ ChromaCbfs IntraSearch::xRecurIntraChromaCodingQT( CodingStructure &cs, Partitio
         ctxStart = m_CABACEstimator->getCtx();
       }
 
+#if JVET_P0058_CHROMA_TS
+      for (int modeId = 0; modeId < nNumTransformCands; modeId++)
+#endif
       {
         for (int crossCPredictionModeId = 0; crossCPredictionModeId < crossCPredictionModesToTest; crossCPredictionModeId++)
         {
@@ -3322,11 +3436,27 @@ ChromaCbfs IntraSearch::xRecurIntraChromaCodingQT( CodingStructure &cs, Partitio
 
           currTU.compAlpha    [compID] = ( crossCPredictionModeId ? compAlpha[compID] : 0 );
 
+#if JVET_P0058_CHROMA_TS
+          currTU.mtsIdx[compID] = trModes[modeId].first;
+#endif
+
           currModeId++;
 
           const bool isFirstMode = (currModeId == 1);
           const bool isLastMode  = false; // Always store output to saveCS and tmpTU
 
+#if JVET_P0058_CHROMA_TS
+           //if DCT2's cbf==0, skip ts search
+          if (!cbfDCT2 && trModes[modeId].first == MTS_SKIP)
+          {
+              break;
+          }
+          if (!trModes[modeId].second)
+          {
+              continue;
+          }
+#endif
+
           if (!isFirstMode) // if not first mode to be tested
           {
             m_CABACEstimator->getCtx() = ctxStart;
@@ -3334,9 +3464,24 @@ ChromaCbfs IntraSearch::xRecurIntraChromaCodingQT( CodingStructure &cs, Partitio
 
           singleDistCTmp = 0;
 
+#if JVET_P0058_CHROMA_TS
+          if (nNumTransformCands > 1)
+          {
+              xIntraCodingTUBlock(currTU, compID, crossCPredictionModeId != 0, singleDistCTmp, default0Save1Load2, nullptr, modeId == 0 ? &trModes : nullptr, true);
+          }
+          else
+          {
+              xIntraCodingTUBlock(currTU, compID, crossCPredictionModeId != 0, singleDistCTmp, default0Save1Load2);
+          }
+#else
           xIntraCodingTUBlock( currTU, compID, crossCPredictionModeId != 0, singleDistCTmp, default0Save1Load2 );
+#endif
 
+#if JVET_P0058_CHROMA_TS
+          if (((crossCPredictionModeId == 1) && (currTU.compAlpha[compID] == 0)) || ((currTU.mtsIdx[compID] == MTS_SKIP) && !TU::getCbf(currTU, compID))) //In order not to code TS flag when cbf is zero, the case for TS with cbf being zero is forbidden.
+#else
           if( ( ( crossCPredictionModeId == 1 ) && ( currTU.compAlpha[compID] == 0 ) ) ) //In order not to code TS flag when cbf is zero, the case for TS with cbf being zero is forbidden.
+#endif
           {
             singleCostTmp = MAX_DOUBLE;
           }
@@ -3371,6 +3516,13 @@ ChromaCbfs IntraSearch::xRecurIntraChromaCodingQT( CodingStructure &cs, Partitio
               bestDistCr = singleDistCTmp;
             }
 
+#if JVET_P0058_CHROMA_TS
+            if (currTU.mtsIdx[compID] == MTS_DCT2_DCT2)
+            {
+                cbfDCT2 = TU::getCbfAtDepth(currTU, compID, currDepth);
+            }
+#endif
+
             if( !isLastMode )
             {
 #if KEEP_PRED_AND_RESI_SIGNALS
@@ -3430,7 +3582,10 @@ ChromaCbfs IntraSearch::xRecurIntraChromaCodingQT( CodingStructure &cs, Partitio
         currTU.jointCbCr               = (uint8_t)cbfMask;
         currTU.compAlpha[COMPONENT_Cb] = 0;
         currTU.compAlpha[COMPONENT_Cr] = 0;
-
+#if JVET_P0058_CHROMA_TS
+        // encoder bugfix: initialize mtsIdx for chroma under JointCbCrMode.
+        currTU.mtsIdx[COMPONENT_Cb] = currTU.mtsIdx[COMPONENT_Cr]  = MTS_DCT2_DCT2;
+#endif
         m_CABACEstimator->getCtx() = ctxStartTU;
 
         resiCb.copyFrom( orgResiCb[cbfMask] );