diff --git a/source/App/EncoderApp/EncApp.cpp b/source/App/EncoderApp/EncApp.cpp
index 1a6b7365a0b6dc76485180478b122c9c6a10171d..0c29bd61d92707fb797dafd219d91399fa3cf7d9 100644
--- a/source/App/EncoderApp/EncApp.cpp
+++ b/source/App/EncoderApp/EncApp.cpp
@@ -266,6 +266,10 @@ void EncApp::xInitLibCfg( int layerIdx )
                                  m_confWinRight / SPS::getWinUnitX(m_inputChromaFormatIDC),
                                  m_confWinTop / SPS::getWinUnitY(m_inputChromaFormatIDC),
                                  m_confWinBottom / SPS::getWinUnitY(m_inputChromaFormatIDC));
+#if SCALING_WINDOW_ENABLED
+  m_cEncLib.setExplicitScalingWindowEnabled                      ( m_explicitScalingWindowEnabled );
+  m_cEncLib.setScalingWindow                                     ( m_scalWinLeft / SPS::getWinUnitX( m_inputChromaFormatIDC ), m_scalWinRight / SPS::getWinUnitX( m_inputChromaFormatIDC ), m_scalWinTop / SPS::getWinUnitY( m_inputChromaFormatIDC ), m_scalWinBottom / SPS::getWinUnitY( m_inputChromaFormatIDC ) );
+#endif
   m_cEncLib.setScalingRatio                                      ( m_scalingRatioHor, m_scalingRatioVer );
   m_cEncLib.setGOPBasedRPREnabledFlag                            (m_gopBasedRPREnabledFlag);
   m_cEncLib.setGOPBasedRPRQPThreshold                            (m_gopBasedRPRQPThreshold);
@@ -1979,7 +1983,11 @@ void EncApp::xWriteOutput(int numEncoded, std::list<PelUnitBuf *> &recBufList)
         }
         else
         {
+#if SCALING_WINDOW_ENABLED
+          ppsID = ((sps.getMaxPicWidthInLumaSamples() != pcPicYuvRec->get(COMPONENT_Y).width || sps.getMaxPicHeightInLumaSamples() != pcPicYuvRec->get(COMPONENT_Y).height) && !m_explicitScalingWindowEnabled) ? m_resChangeInClvsEnabled ? ENC_PPS_ID_RPR : layerId : layerId;
+#else
           ppsID = (sps.getMaxPicWidthInLumaSamples() != pcPicYuvRec->get(COMPONENT_Y).width || sps.getMaxPicHeightInLumaSamples() != pcPicYuvRec->get(COMPONENT_Y).height) ? ENC_PPS_ID_RPR : layerId;
+#endif
         }
         const PPS& pps = *m_cEncLib.getPPS(ppsID);
         if( m_cEncLib.isResChangeInClvsEnabled() && m_cEncLib.getUpscaledOutput() )
diff --git a/source/App/EncoderApp/EncAppCfg.cpp b/source/App/EncoderApp/EncAppCfg.cpp
index 708092258b5afa03788396e249c4d276abdfd466..d414ffea397658138aed1128819d7b72d3fd1e44 100644
--- a/source/App/EncoderApp/EncAppCfg.cpp
+++ b/source/App/EncoderApp/EncAppCfg.cpp
@@ -821,6 +821,13 @@ bool EncAppCfg::parseCfg( int argc, char* argv[] )
   ("ConfWinRight",                                    m_confWinRight,                                       0, "Right offset for window conformance mode 3")
   ("ConfWinTop",                                      m_confWinTop,                                         0, "Top offset for window conformance mode 3")
   ("ConfWinBottom",                                   m_confWinBottom,                                      0, "Bottom offset for window conformance mode 3")
+#if SCALING_WINDOW_ENABLED
+  ("ScalingWindow",                                   m_explicitScalingWindowEnabled,                   false, "Enable scaling window")
+  ("ScalWinLeft,-swl",                                m_scalWinLeft,                                        0, "Left offset for scaling window")
+  ("ScalWinRight,-swr",                               m_scalWinRight,                                       0, "Right offset for scaling window")
+  ("ScalWinTop,-swt",                                 m_scalWinTop,                                         0, "Top offset for scaling window")
+  ("ScalWinBottom,-swb",                              m_scalWinBottom,                                      0, "Bottom offset for scaling window")
+#endif
   ("AccessUnitDelimiter",                             m_AccessUnitDelimiter,                            false, "Enable Access Unit Delimiter NALUs")
   ("EnablePictureHeaderInSliceHeader",                m_enablePictureHeaderInSliceHeader,                true, "Enable Picture Header in Slice Header")
   ("FrameRate,-fr",                                   frameRate,                            std::to_string(0), "Frame rate")
@@ -4287,6 +4294,26 @@ bool EncAppCfg::xCheckParameter()
                "Top conformance window offset must be an integer multiple of the specified chroma subsampling");
   xConfirmPara(m_confWinBottom % SPS::getWinUnitY(m_chromaFormatIdc) != 0,
                "Bottom conformance window offset must be an integer multiple of the specified chroma subsampling");
+#if SCALING_WINDOW_ENABLED
+  xConfirmPara(m_explicitScalingWindowEnabled && (m_scalingRatioHor != 1.0 || m_scalingRatioVer != 1.0 || m_gopBasedRPREnabledFlag), "ScalingWindow cannot be enabled when GOPBasedRPR is enabled");
+  xConfirmPara(m_scalWinLeft    % SPS::getWinUnitX(m_chromaFormatIDC) != 0, "Left scaling window offset must be an integer multiple of the specified chroma subsampling");
+  xConfirmPara(m_scalWinRight   % SPS::getWinUnitX(m_chromaFormatIDC) != 0, "Right scaling window offset must be an integer multiple of the specified chroma subsampling");
+  xConfirmPara(m_scalWinTop     % SPS::getWinUnitY(m_chromaFormatIDC) != 0, "Top scaling window offset must be an integer multiple of the specified chroma subsampling");
+  xConfirmPara(m_scalWinBottom  % SPS::getWinUnitY(m_chromaFormatIDC) != 0, "Bottom scaling window offset must be an integer multiple of the specified chroma subsampling");
+  xConfirmPara((m_scalWinLeft < -m_sourceWidth * 15) || (m_scalWinLeft >= m_sourceWidth),
+               "The values of SubWidthC * pps_scaling_win_left_offset shall be greater than or equal to -pps_pic_width_in_luma_samples * 15 and less than pps_pic_width_in_luma_samples");
+  xConfirmPara((m_scalWinRight < -m_sourceWidth * 15) || (m_scalWinRight >= m_sourceWidth),
+               "The values of SubWidthC * pps_scaling_win_right_offset shall be greater than or equal to -pps_pic_width_in_luma_samples * 15 and less than pps_pic_width_in_luma_samples");
+  xConfirmPara((m_scalWinTop < -m_sourceHeight * 15) || (m_scalWinTop >= m_sourceHeight),
+               "The values of SubHeightC * pps_scaling_win_top_offset shall be greater than or equal to -pps_pic_height_in_luma_samples * 15 and less than pps_pic_height_in_luma_samples");
+  xConfirmPara((m_scalWinBottom < -m_sourceHeight * 15) || (m_scalWinBottom >= m_sourceHeight),
+               "The values of SubHeightC * pps_scaling_win_bottom_offset shall be greater than or equal to -pps_pic_height_in_luma_samples * 15 and less than pps_pic_height_in_luma_samples");
+  xConfirmPara(((m_scalWinLeft+m_scalWinRight) < -m_sourceWidth * 15) || ((m_scalWinLeft+m_scalWinRight) >= m_sourceWidth),
+               "The values of SubWidthC * (pps_scaling_win_left_offset + pps_scaling_win_right_offset) shall be greater than or equal to -pps_pic_width_in_luma_samples * 15 and less than pps_pic_width_in_luma_samples");
+  xConfirmPara(((m_scalWinTop+m_scalWinBottom) < -m_sourceHeight * 15) || ((m_scalWinTop+m_scalWinBottom) >= m_sourceHeight),
+               "The values of SubHeightC * (pps_scaling_win_top_offset + pps_scaling_win_bottom_offset) shall be greater than or equal to -pps_pic_height_in_luma_samples * 15 and less than pps_pic_height_in_luma_samples");
+#endif
+
 
   // max CU width and height should be power of 2
   uint32_t ui = m_maxCuWidth;
diff --git a/source/App/EncoderApp/EncAppCfg.h b/source/App/EncoderApp/EncAppCfg.h
index 609385864ea28253b5ade88c180075a712a31c68..5d7d1422620f676db1c7abdf8362f0e0b125479c 100644
--- a/source/App/EncoderApp/EncAppCfg.h
+++ b/source/App/EncoderApp/EncAppCfg.h
@@ -111,6 +111,13 @@ protected:
   int       m_confWinRight;
   int       m_confWinTop;
   int       m_confWinBottom;
+#if SCALING_WINDOW_ENABLED
+  bool      m_explicitScalingWindowEnabled;
+  int       m_scalWinLeft;
+  int       m_scalWinRight;
+  int       m_scalWinTop;
+  int       m_scalWinBottom;
+#endif
   int       m_sourcePadding[2];                                       ///< number of padded pixels for width and height
   int       m_firstValidFrame;
   int       m_lastValidFrame;
diff --git a/source/Lib/CommonLib/TypeDef.h b/source/Lib/CommonLib/TypeDef.h
index c5f23c85fb555f5b5a9eca64246390202b67a2f7..d1484a231315afed8687ba99ed66ba26eb6cb5d3 100644
--- a/source/Lib/CommonLib/TypeDef.h
+++ b/source/Lib/CommonLib/TypeDef.h
@@ -148,6 +148,19 @@
 #define ENABLE_USER_DEFINED_WEIGHTS                       0 // User can specify weights for both current and previous picture, such that their sum = 1
 #endif
 
+#ifndef SCALING_WINDOW_ENABLED
+#define SCALING_WINDOW_ENABLED                            1
+#endif
+#ifndef NHK_20230407_1
+#define NHK_20230407_1                                    1   // bug fix
+#endif
+#ifndef NHK_20230407_2
+#define NHK_20230407_2                                    1   // fixed in VTM20.0
+#endif
+#ifndef NHK_20230418
+#define NHK_20230418                                      1   // bug fix
+#endif
+
 // clang-format on
 
 // ====================================================================================================================
diff --git a/source/Lib/EncoderLib/EncCfg.h b/source/Lib/EncoderLib/EncCfg.h
index 851100a4c379e314129fe896fb91aa4f0e68c34e..c0a1988b50b2962816ef3b8cc4d1b420f0f41f8c 100644
--- a/source/Lib/EncoderLib/EncCfg.h
+++ b/source/Lib/EncoderLib/EncCfg.h
@@ -161,6 +161,10 @@ protected:
   int       m_sourceWidth;
   int       m_sourceHeight;
   Window    m_conformanceWindow;
+#if SCALING_WINDOW_ENABLED
+  bool      m_explicitScalingWindowEnabled;
+  Window    m_scalingWindow;
+#endif
   int       m_sourcePadding[2];
   int       m_framesToBeEncoded;
   int       m_firstValidFrame;
@@ -1223,6 +1227,10 @@ public:
 
   Window   &getConformanceWindow()                           { return m_conformanceWindow; }
   void      setConformanceWindow (int confLeft, int confRight, int confTop, int confBottom ) { m_conformanceWindow.setWindow (confLeft, confRight, confTop, confBottom); }
+#if SCALING_WINDOW_ENABLED
+  void      setExplicitScalingWindowEnabled(bool enabled)    { m_explicitScalingWindowEnabled = enabled; }
+  void      setScalingWindow (int scalingLeft, int scalingRight, int scalingTop, int scalingBottom ) { m_scalingWindow.setWindow (scalingLeft, scalingRight, scalingTop, scalingBottom); }
+#endif
 
   void      setFramesToBeEncoded            ( int   i )      { m_framesToBeEncoded = i; }
 
diff --git a/source/Lib/EncoderLib/EncLib.cpp b/source/Lib/EncoderLib/EncLib.cpp
index 332211baaad92b4b46390e5f15fc6c190313b65f..10a2a5eeadae23c0cda77c2920e770c2f194bd6d 100644
--- a/source/Lib/EncoderLib/EncLib.cpp
+++ b/source/Lib/EncoderLib/EncLib.cpp
@@ -197,6 +197,13 @@ void EncLib::init(AUWriterIf *auWriterIf)
     pps0.setConformanceWindow( m_conformanceWindow );
     pps0.setConformanceWindowFlag( m_conformanceWindow.getWindowEnabledFlag() );
   }
+#if SCALING_WINDOW_ENABLED
+  if (m_explicitScalingWindowEnabled)
+  {
+    pps0.setExplicitScalingWindowFlag(true);
+    pps0.setScalingWindow(m_scalingWindow);
+  }
+#endif
   if (!pps0.getExplicitScalingWindowFlag())
   {
     pps0.setScalingWindow(pps0.getConformanceWindow());
@@ -1451,6 +1458,64 @@ void EncLib::xInitSPS( SPS& sps )
 
   sps.setMaxPicWidthInLumaSamples( m_sourceWidth );
   sps.setMaxPicHeightInLumaSamples( m_sourceHeight );
+#if SCALING_WINDOW_ENABLED
+  bool scalingWindowResChanged = false;
+  if (m_multiLayerEnabledFlag && m_vps->getMaxLayers() > 0) {
+    const int minCuSize = std::max(8, 1 << m_log2MinCUSize);
+    int currPicScaledWidth = m_sourceWidth - SPS::getWinUnitX(m_chromaFormatIDC) * (m_scalingWindow.getWindowLeftOffset() + m_scalingWindow.getWindowRightOffset());
+    int currPicScaledHeight = m_sourceHeight - SPS::getWinUnitY( m_chromaFormatIDC) * (m_scalingWindow.getWindowTopOffset() + m_scalingWindow.getWindowBottomOffset());
+
+    int refPicScaledWidth = currPicScaledWidth;
+    int refPicScaledHeight = currPicScaledHeight;
+    const int layerIdx = m_vps->getGeneralLayerIdx(m_layerId);
+    if (getNumRefLayers(layerIdx) > 0) {
+      int dWidth = 0;
+      int dHeight = 0;
+      for (int refLayerIdx = 0; refLayerIdx < layerIdx; refLayerIdx++) {
+        if (m_vps->getDirectRefLayerFlag(layerIdx, refLayerIdx)) {
+          const PPS& refPPS = *getPPS(refLayerIdx);
+          int scaledWidth = refPPS.getPicWidthInLumaSamples() - SPS::getWinUnitX(m_chromaFormatIDC) * (refPPS.getScalingWindow().getWindowLeftOffset() + refPPS.getScalingWindow().getWindowRightOffset());
+          int scaledHeight = refPPS.getPicHeightInLumaSamples() - SPS::getWinUnitY( m_chromaFormatIDC) * (refPPS.getScalingWindow().getWindowTopOffset() + refPPS.getScalingWindow().getWindowBottomOffset());
+          CHECK(currPicScaledWidth * 2 < scaledWidth, "CurrPicScalWinWidthL * 2 shall be greater than or equal to refPicScalWinWidthL");
+          CHECK(currPicScaledHeight * 2 < scaledHeight, "CurrPicScalWinHeightL * 2 shall be greater than or equal to refPicScalWinHeightL");
+          CHECK(currPicScaledWidth > scaledWidth * 8, "CurrPicScalWinWidthL shall be less than or equal to refPicScalWinWidthL * 8");
+          CHECK(currPicScaledHeight > scaledHeight * 8, "CurrPicScalWinHeightL shall be less than or equal to refPicScalWinHeightL * 8");
+
+          int dW = abs(scaledWidth - currPicScaledWidth);
+          int dH = abs(scaledHeight - currPicScaledHeight);
+          if (scaledWidth > currPicScaledWidth && dW > dWidth)
+          {
+            refPicScaledWidth = scaledWidth;  dWidth = dW;
+          }
+          if (scaledHeight > currPicScaledHeight && dH > dHeight)
+          {
+            refPicScaledHeight = scaledHeight;  dHeight = dH;
+          }
+        }
+      }
+    }
+
+    int maxPicWidth = std::max(m_sourceWidth, ((m_sourceWidth - minCuSize) * refPicScaledWidth + currPicScaledWidth - 1) / currPicScaledWidth);
+    int maxPicHeight = std::max(m_sourceHeight, ((m_sourceHeight - minCuSize) * refPicScaledHeight + currPicScaledHeight - 1) / currPicScaledHeight);
+    if (maxPicWidth % minCuSize)
+    {
+      maxPicWidth += ((maxPicWidth / minCuSize) + 1) * minCuSize - maxPicWidth;
+    }
+    if (maxPicHeight % minCuSize)
+    {
+      maxPicHeight += ((maxPicHeight / minCuSize) + 1) * minCuSize - maxPicHeight;
+    }
+    if (maxPicWidth > m_sourceWidth || maxPicHeight > m_sourceHeight)
+      scalingWindowResChanged = true;
+    CHECK((currPicScaledWidth * maxPicWidth) < refPicScaledWidth * (m_sourceWidth - minCuSize),
+          "(CurrPicScalWinWidthL * sps_pic_width_max_in_luma_samples) shall be greater than or equal to (refPicScalWinWidthL * (pps_pic_width_in_luma_samples - Max(8, MinCbSizeY)))");
+    CHECK((currPicScaledHeight * maxPicHeight) < refPicScaledHeight * (m_sourceHeight - minCuSize),
+          "(CurrPicScalWinHeightL * sps_pic_height_max_in_luma_samples) shall be greater than or equal to (refPicScalWinHeightL * (pps_pic_height_in_luma_samples -  Max(8, MinCbSizeY)))");
+    sps.setMaxPicWidthInLumaSamples(maxPicWidth);
+    sps.setMaxPicHeightInLumaSamples(maxPicHeight);
+  }
+#endif
+
   if (m_resChangeInClvsEnabled)
   {
     int maxPicWidth = std::max(m_sourceWidth, (int)((double)m_sourceWidth / m_scalingRatioHor + 0.5));
@@ -1738,8 +1803,13 @@ void EncLib::xInitSPS( SPS& sps )
   sps.setInterLayerPresentFlag( m_layerId > 0 && m_vps->getMaxLayers() > 1 && !m_vps->getAllIndependentLayersFlag() && !m_vps->getIndependentLayerFlag( m_vps->getGeneralLayerIdx( m_layerId ) ) );
   CHECK( m_vps->getIndependentLayerFlag( m_vps->getGeneralLayerIdx( m_layerId ) ) && sps.getInterLayerPresentFlag(), " When vps_independent_layer_flag[GeneralLayerIdx[nuh_layer_id ]]  is equal to 1, the value of inter_layer_ref_pics_present_flag shall be equal to 0." );
 
+#if SCALING_WINDOW_ENABLED
+  sps.setResChangeInClvsEnabledFlag(m_resChangeInClvsEnabled || m_constrainedRaslEncoding || scalingWindowResChanged);
+  sps.setRprEnabledFlag(m_rprEnabledFlag || m_explicitScalingWindowEnabled || scalingWindowResChanged);
+#else
   sps.setResChangeInClvsEnabledFlag(m_resChangeInClvsEnabled || m_constrainedRaslEncoding);
   sps.setRprEnabledFlag(m_rprEnabledFlag);
+#endif
 #if JVET_AD0045
   sps.setGOPBasedRPREnabledFlag(m_gopBasedRPREnabledFlag);
 #endif