H.266/VVC代码学习61:VTM中deblocking源码阅读

自从找实习以来,很久一阵子没看VTM代码了,一直在文本层面做改动和优化。远程控制实验室电脑时断时续,本机上只有比较老的6.0版本,经过对比发现8.0上的改动也非常小。为了在疫情期间保持读代码的能力,决定入手一个新技术的实现方法。

阅读过后感觉,DBF的代码是一个很优化的代码。可以很明确的知道每个函数的具体意义及内外层结构,这也许是被ALF代码虐了一两个月锻炼出来的吧,对于刚接触代码的新手可以从deblocking入门。

这里只放上代码及注释了,建议先看万老师书上有关去方块滤波的内容,除了运算层面变化基本不大。具体的讲解在后续奉上吧~

/* The copyright in this software is being made available under the BSD
 * License, included below. This software may be subject to other third party
 * and contributor rights, including patent rights, and no such rights are
 * granted under this license.
 *
 * Copyright (c) 2010-2019, ITU/ISO/IEC
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *  * Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *  * Neither the name of the ITU/ISO/IEC nor the names of its contributors may
 *    be used to endorse or promote products derived from this software without
 *    specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 */

/** \file     LoopFilter.cpp
    \brief    deblocking filter
*/

#include "LoopFilter.h"
#include "Slice.h"
#include "Mv.h"
#include "Unit.h"
#include "UnitTools.h"
#include "UnitPartitioner.h"
#include "dtrace_codingstruct.h"
#include "dtrace_buffer.h"

//! \ingroup CommonLib
//! \{
    
    

// ====================================================================================================================
// Constants
// ====================================================================================================================

//#define   EDGE_VER    0
//#define   EDGE_HOR    1

#define DEBLOCK_SMALLEST_BLOCK  8


#define DEFAULT_INTRA_TC_OFFSET 2 ///< Default intra TC offset

// ====================================================================================================================
// Tables  根据每一个亮度QP,得到tc表和beta表
// ====================================================================================================================

const uint16_t LoopFilter::sm_tcTable[MAX_QP + 1 + DEFAULT_INTRA_TC_OFFSET] =//tc表
{
    
    
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,3,4,4,4,4,5,5,5,5,7,7,8,9,10,10,11,13,14,15,17,19,21,24,25,29,33,36,41,45,51,57,64,71,80,89,100,112,125,141,157,177,198,222,250,280,314,352,395
};
const uint8_t LoopFilter::sm_betaTable[MAX_QP + 1] =//β表
{
    
    
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,6,7,8,9,10,11,12,13,14,15,16,17,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,60,62,64
  , 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88
};

inline static uint32_t getRasterIdx(const Position& pos, const PreCalcValues& pcv)
{
    
    
  return ( ( pos.x & pcv.maxCUWidthMask ) >> pcv.minCUWidthLog2 ) + ( ( pos.y & pcv.maxCUHeightMask ) >> pcv.minCUHeightLog2 ) * pcv.partsInCtuWidth;
}

// ====================================================================================================================
// utility functions 左边和上边是否可用
// ====================================================================================================================

static bool isAvailableLeft( const CodingUnit& cu, const CodingUnit& cu2, const bool bEnforceSliceRestriction, const bool bEnforceTileRestriction )
{
    
    
  return ( ( !bEnforceSliceRestriction || CU::isSameSlice( cu, cu2 ) ) && ( !bEnforceTileRestriction || CU::isSameTile( cu, cu2 ) ) );
}

static bool isAvailableAbove( const CodingUnit& cu, const CodingUnit& cu2, const bool bEnforceSliceRestriction, const bool bEnforceTileRestriction )
{
    
    
  return ( !bEnforceSliceRestriction || CU::isSameSlice( cu, cu2 ) ) && ( !bEnforceTileRestriction || CU::isSameTile( cu, cu2 ) );
}


// ====================================================================================================================
// Constructor / destructor / create / destroy 构造函数、析构函数
// ====================================================================================================================

LoopFilter::LoopFilter()
{
    
    
}

LoopFilter::~LoopFilter()
{
    
    
}

// ====================================================================================================================
// Public member functions 公有成员函数(重要)
// ====================================================================================================================
void LoopFilter::create( const unsigned uiMaxCUDepth )
{
    
    
  destroy();
  const unsigned numPartitions = 1 << ( uiMaxCUDepth << 1 );
  for( int edgeDir = 0; edgeDir < NUM_EDGE_DIR; edgeDir++ )
  {
    
    
    m_aapucBS       [edgeDir].resize( numPartitions );
    m_aapbEdgeFilter[edgeDir].resize( numPartitions );
  }
  m_enc = false;
}

void LoopFilter::initEncPicYuvBuffer(ChromaFormat chromaFormat, int lumaWidth, int lumaHeight)
{
    
    
  const UnitArea picArea(chromaFormat, Area(0, 0, lumaWidth, lumaHeight));
  m_encPicYuvBuffer.destroy();
  m_encPicYuvBuffer.create(picArea);
}

void LoopFilter::destroy()
{
    
    
  for( int edgeDir = 0; edgeDir < NUM_EDGE_DIR; edgeDir++ )
  {
    
    
    m_aapucBS       [edgeDir].clear();
    m_aapbEdgeFilter[edgeDir].clear();
  }
  m_encPicYuvBuffer.destroy();
}

/**
 - call deblocking function for every CU
 .
 \param  pcPic   picture class (Pic) pointer
 */
void LoopFilter::loopFilterPic( CodingStructure& cs//去方块滤波入口函数
                                )
{
    
    
  const PreCalcValues& pcv = *cs.pcv;
  m_shiftHor = ::getComponentScaleX( COMPONENT_Cb, cs.pcv->chrFormat );
  m_shiftVer = ::getComponentScaleY( COMPONENT_Cb, cs.pcv->chrFormat );

  DTRACE_UPDATE( g_trace_ctx, ( std::make_pair( "poc", cs.slice->getPOC() ) ) );
#if ENABLE_TRACING
  for( int y = 0; y < pcv.heightInCtus; y++ )
  {
    
    
    for( int x = 0; x < pcv.widthInCtus; x++ )
    {
    
    
      const UnitArea ctuArea( pcv.chrFormat, Area( x << pcv.maxCUWidthLog2, y << pcv.maxCUHeightLog2, pcv.maxCUWidth, pcv.maxCUWidth ) );
      DTRACE    ( g_trace_ctx, D_CRC, "CTU %d %d", ctuArea.Y().x, ctuArea.Y().y );
      DTRACE_CRC( g_trace_ctx, D_CRC, cs, cs.picture->getRecoBuf( clipArea( ctuArea, *cs.picture ) ), &ctuArea.Y() );
    }
  }
#endif


  /************************************* 先对垂直边界水平滤波 *********************************************/
  for( int y = 0; y < pcv.heightInCtus; y++ )//这张图片的一列有多少个CTU
  {
    
    
    for( int x = 0; x < pcv.widthInCtus; x++ )//这张图片的一行有多少个CTU
    {
    
    
		/***************************** 亮度滤波 ******************************/
      memset( m_aapucBS       [EDGE_VER].data(), 0,     m_aapucBS       [EDGE_VER].byte_size() );//初始化滤波强度
      memset( m_aapbEdgeFilter[EDGE_VER].data(), false, m_aapbEdgeFilter[EDGE_VER].byte_size() );//初始化是否是边缘
      memset( m_maxFilterLengthP, 0, sizeof(m_maxFilterLengthP) );
      memset( m_maxFilterLengthQ, 0, sizeof(m_maxFilterLengthQ) );
      memset( m_transformEdge, false, sizeof(m_transformEdge) );
      m_ctuXLumaSamples = x << pcv.maxCUWidthLog2;
      m_ctuYLumaSamples = y << pcv.maxCUHeightLog2;

      const UnitArea ctuArea( pcv.chrFormat, Area( x << pcv.maxCUWidthLog2, y << pcv.maxCUHeightLog2, pcv.maxCUWidth, pcv.maxCUWidth ) );//确定到当前CTU
      CodingUnit* firstCU = cs.getCU( ctuArea.lumaPos(), CH_L);
      cs.slice = firstCU->slice;

      // CU-based deblocking CU级滤波(水平?)
      for( auto &currCU : cs.traverseCUs( CS::getArea( cs, ctuArea, CH_L ), CH_L ) )//对亮度CTU中的每个CU滤波具体操作
      {
    
    
        xDeblockCU( currCU, EDGE_VER );
      }
	    /***************************** 色度滤波 ******************************/
      if( CS::isDualITree( cs ) )//如果独立划分,才进行色度滤波
      {
    
    
        memset( m_aapucBS       [EDGE_VER].data(), 0,     m_aapucBS       [EDGE_VER].byte_size() );
        memset( m_aapbEdgeFilter[EDGE_VER].data(), false, m_aapbEdgeFilter[EDGE_VER].byte_size() );
        memset( m_maxFilterLengthP, 0, sizeof(m_maxFilterLengthP) );
        memset( m_maxFilterLengthQ, 0, sizeof(m_maxFilterLengthQ) );
        memset( m_transformEdge, false, sizeof(m_transformEdge) );

		// CU-based deblocking CU级滤波(水平?)
        for( auto &currCU : cs.traverseCUs( CS::getArea( cs, ctuArea, CH_C ), CH_C ) )//色度滤波具体操作
        {
    
    
          xDeblockCU( currCU, EDGE_VER );
        }
      }
    }
  }

  /************************************* 再对水平边界垂直滤波 *********************************************/
  for( int y = 0; y < pcv.heightInCtus; y++ )
  {
    
    
    for( int x = 0; x < pcv.widthInCtus; x++ )
    {
    
    
      memset( m_aapucBS       [EDGE_HOR].data(), 0,     m_aapucBS       [EDGE_HOR].byte_size() );
      memset( m_aapbEdgeFilter[EDGE_HOR].data(), false, m_aapbEdgeFilter[EDGE_HOR].byte_size() );
      memset( m_maxFilterLengthP, 0, sizeof(m_maxFilterLengthP) );
      memset( m_maxFilterLengthQ, 0, sizeof(m_maxFilterLengthQ) );
      memset( m_transformEdge, false, sizeof(m_transformEdge) );
      m_ctuXLumaSamples = x << pcv.maxCUWidthLog2;
      m_ctuYLumaSamples = y << pcv.maxCUHeightLog2;

      const UnitArea ctuArea( pcv.chrFormat, Area( x << pcv.maxCUWidthLog2, y << pcv.maxCUHeightLog2, pcv.maxCUWidth, pcv.maxCUWidth ) );
      CodingUnit* firstCU = cs.getCU( ctuArea.lumaPos(), CH_L);
      cs.slice = firstCU->slice;

      // CU-based deblocking
      for( auto &currCU : cs.traverseCUs( CS::getArea( cs, ctuArea, CH_L ), CH_L ) )
      {
    
    
        xDeblockCU( currCU, EDGE_HOR );
      }

      if( CS::isDualITree( cs ) )
      {
    
    
        memset( m_aapucBS       [EDGE_HOR].data(), 0,     m_aapucBS       [EDGE_HOR].byte_size() );
        memset( m_aapbEdgeFilter[EDGE_HOR].data(), false, m_aapbEdgeFilter[EDGE_HOR].byte_size() );
        memset( m_maxFilterLengthP, 0, sizeof(m_maxFilterLengthP) );
        memset( m_maxFilterLengthQ, 0, sizeof(m_maxFilterLengthQ) );
        memset( m_transformEdge, false, sizeof(m_transformEdge) );

        for( auto &currCU : cs.traverseCUs( CS::getArea( cs, ctuArea, CH_C ), CH_C ) )
        {
    
    
          xDeblockCU( currCU, EDGE_HOR );
        }
      }
    }
  }

  DTRACE_PIC_COMP(D_REC_CB_LUMA_LF,   cs, cs.getRecoBuf(), COMPONENT_Y);
  DTRACE_PIC_COMP(D_REC_CB_CHROMA_LF, cs, cs.getRecoBuf(), COMPONENT_Cb);
  DTRACE_PIC_COMP(D_REC_CB_CHROMA_LF, cs, cs.getRecoBuf(), COMPONENT_Cr);

  DTRACE    ( g_trace_ctx, D_CRC, "LoopFilter" );
  DTRACE_CRC( g_trace_ctx, D_CRC, cs, cs.getRecoBuf() );
}


// ====================================================================================================================
// Protected member functions 私有成员函数(重要)
// ====================================================================================================================

/**
 Deblocking filter process in CU-based (the same function as conventional's)

 \param cu               the CU to be deblocked
 \param edgeDir          the direction of the edge in block boundary (horizontal/vertical), which is added newly
*/
void LoopFilter::xDeblockCU( CodingUnit& cu, const DeblockEdgeDir edgeDir )
{
    
    
  /*********************** 进行初始化 ***********************/
  const PreCalcValues& pcv = *cu.cs->pcv;
  const Area area          = cu.Y().valid() ? cu.Y() : Area( recalcPosition( cu.chromaFormat, cu.chType, CHANNEL_TYPE_LUMA, cu.blocks[cu.chType].pos() ), recalcSize( cu.chromaFormat, cu.chType, CHANNEL_TYPE_LUMA, cu.blocks[cu.chType].size() ) );

  bool horEdgeFilter = false, verEdgeFilter = false;
  int  numHorVirBndry = 0, numVerVirBndry = 0;
  int  horVirBndryPos[] = {
    
     0, 0, 0 };
  int  verVirBndryPos[] = {
    
     0, 0, 0 };

  bool isCuCrossedByVirtualBoundaries = isCrossedByVirtualBoundaries( area.x, area.y, area.width, area.height, numHorVirBndry, numVerVirBndry, horVirBndryPos, verVirBndryPos, cu.cs->slice->getPPS() );

  xSetLoopfilterParam( cu );//设置滤波是否可用(左、上、内)
  static_vector<int, 2*MAX_CU_SIZE> edgeIdx;//边界索引
  edgeIdx.clear();//清空边界索引

  /*********************** 确定坐标位置 ***********************/
  if (m_enc)//如果实在编码时使用
  {
    
    
    m_shiftHor = ::getComponentScaleX(COMPONENT_Cb, cu.chromaFormat);//色度缩放
    m_shiftVer = ::getComponentScaleY(COMPONENT_Cb, cu.chromaFormat);//色度缩放
    int x, y;//x,y是cu坐标
    if (cu.Y().valid())
    {
    
    
      x = cu.block(COMPONENT_Y).x;
      y = cu.block(COMPONENT_Y).y;
    }
    else
    {
    
    
      x = cu.block(COMPONENT_Cb).x << m_shiftHor;
      y = cu.block(COMPONENT_Cb).y << m_shiftVer;
    }
    m_ctuXLumaSamples = x & ~(cu.slice->getSPS()->getMaxCUWidth()  - 1);//CTU在x轴方向的位置
    m_ctuYLumaSamples = y & ~(cu.slice->getSPS()->getMaxCUHeight() - 1);//CTU在y轴方向的位置
  }

  /*********************** 1.0 确定滤波位置和抽头长度 ***********************/
  for( auto &currTU : CU::traverseTUs( cu ) )//遍历TU
  {
    
    
    const Area& areaTu    = cu.Y().valid() ? currTU.block( COMPONENT_Y ) : area;
    verEdgeFilter = m_stLFCUParam.internalEdge;
    horEdgeFilter = m_stLFCUParam.internalEdge;
    if( isCuCrossedByVirtualBoundaries )
    {
    
    
      xDeriveEdgefilterParam( areaTu.x, areaTu.y, numVerVirBndry, numHorVirBndry, verVirBndryPos, horVirBndryPos, verEdgeFilter, horEdgeFilter );
    }
    xSetEdgefilterMultiple( cu, EDGE_VER, areaTu, verEdgeFilter );//设置垂直边界
    xSetEdgefilterMultiple( cu, EDGE_HOR, areaTu, horEdgeFilter );//设置水平边界
    xSetMaxFilterLengthPQFromTransformSizes( edgeDir, cu, currTU );//设置边界附近的多少个像素参与滤波
    edgeIdx.push_back( ( edgeDir == EDGE_HOR ) ? ( currTU.blocks[cu.chType].y - cu.blocks[cu.chType].y ) / 4 : ( currTU.blocks[cu.chType].x - cu.blocks[cu.chType].x ) / 4 );
	//边缘索引的容器中pushback这个TU的信息
  }

  bool mvSubBlocks = false;
  int subBlockSize = 8;
  for( auto &currPU : CU::traversePUs( cu ) )//遍历PU
  {
    
    
    const Area& areaPu = cu.Y().valid() ? currPU.block( COMPONENT_Y ) : area;
    const bool xOff    = currPU.blocks[cu.chType].x != cu.blocks[cu.chType].x;
    const bool yOff    = currPU.blocks[cu.chType].y != cu.blocks[cu.chType].y;

    verEdgeFilter = (xOff ? m_stLFCUParam.internalEdge : m_stLFCUParam.leftEdge);
    horEdgeFilter = (yOff ? m_stLFCUParam.internalEdge : m_stLFCUParam.topEdge);
    if( isCuCrossedByVirtualBoundaries )
    {
    
    
      xDeriveEdgefilterParam( areaPu.x, areaPu.y, numVerVirBndry, numHorVirBndry, verVirBndryPos, horVirBndryPos, verEdgeFilter, horEdgeFilter );
    }

    xSetEdgefilterMultiple( cu, EDGE_VER, areaPu, verEdgeFilter, xOff );
    xSetEdgefilterMultiple( cu, EDGE_HOR, areaPu, horEdgeFilter, yOff );
    edgeIdx.push_back( ( edgeDir == EDGE_HOR ) ? ( currPU.blocks[cu.chType].y - cu.blocks[cu.chType].y ) / 4 : ( currPU.blocks[cu.chType].x - cu.blocks[cu.chType].x ) / 4 );

    if ((currPU.mergeFlag && (currPU.mergeType == MRG_TYPE_SUBPU_ATMVP)) || cu.affine)//如果是SBTMVP或affine
    {
    
    
      mvSubBlocks = true;
      if (edgeDir == EDGE_HOR)//如果是水平滤波
      {
    
    
        for (uint32_t off = subBlockSize; off < areaPu.height; off += subBlockSize)
        {
    
    
          const Area mvBlockH(cu.Y().x, cu.Y().y + off, cu.Y().width, pcv.minCUHeight);
          horEdgeFilter = m_stLFCUParam.internalEdge;
          if( isCuCrossedByVirtualBoundaries )
          {
    
    
            xDeriveEdgefilterParam( mvBlockH.x, mvBlockH.y, 0, numHorVirBndry, verVirBndryPos, horVirBndryPos, verEdgeFilter, horEdgeFilter );
          }

          xSetEdgefilterMultiple(cu, EDGE_HOR, mvBlockH, horEdgeFilter, 1);//设置滤波器位置
          edgeIdx.push_back( ( currPU.blocks[cu.chType].y + off - cu.blocks[cu.chType].y ) / 4 );//边缘索引的容器中pushback这个TU的信息
        }
      }
      else//如果是垂直滤波
      {
    
    
        for (uint32_t off = subBlockSize; off < areaPu.width; off += subBlockSize)
        {
    
    
          const Area mvBlockV(cu.Y().x + off, cu.Y().y, pcv.minCUWidth, cu.Y().height);
          verEdgeFilter = m_stLFCUParam.internalEdge;
          if( isCuCrossedByVirtualBoundaries )
          {
    
    
            xDeriveEdgefilterParam( mvBlockV.x, mvBlockV.y, numVerVirBndry, 0, verVirBndryPos, horVirBndryPos, verEdgeFilter, horEdgeFilter );
          }

          xSetEdgefilterMultiple(cu, EDGE_VER, mvBlockV, verEdgeFilter, 1);//设置滤波器
          edgeIdx.push_back( ( currPU.blocks[cu.chType].x + off - cu.blocks[cu.chType].x ) / 4 );//边缘索引的容器中pushback这个TU的信息
        }
      }
    }

    xSetMaxFilterLengthPQForCodingSubBlocks( edgeDir, cu, currPU, mvSubBlocks, subBlockSize, areaPu );//对子块的边缘长度
  }

  /************************* 1.1 获取边界强度 ***********************/
  const unsigned uiPelsInPart = pcv.minCUWidth;

  for( int y = 0; y < area.height; y += uiPelsInPart )
  {
    
    
    for( int x = 0; x < area.width; x += uiPelsInPart )
    {
    
    
      unsigned uiBSCheck = 1;
      const Position localPos  {
    
     area.x + x, area.y + y };
      const unsigned rasterIdx = getRasterIdx( localPos, pcv );//获取网格索引

      if( m_aapbEdgeFilter[edgeDir][rasterIdx] && uiBSCheck )
      {
    
    
        m_aapucBS[edgeDir][rasterIdx] = xGetBoundaryStrengthSingle( cu, edgeDir, localPos );//获取最终边界强度
      }
    }
  }

  /************************* 进行最终滤波 ***********************/
  std::sort( edgeIdx.begin(), edgeIdx.end() );
  int prevEdgeIdx = -1;
  for ( const int& edge : edgeIdx )
  {
    
    
    if ( edge == prevEdgeIdx ) // skip duplicate edgeIdx marked by both transform and coding subblock processes
    {
    
    
      continue;
    }
    prevEdgeIdx = edge;

    if ( cu.blocks[COMPONENT_Y].valid() )
    {
    
    
      xEdgeFilterLuma( cu, edgeDir, edge );//亮度滤波
    }
    if ( cu.blocks[COMPONENT_Cb].valid() && pcv.chrFormat != CHROMA_400 )
    {
    
    
      if ( !cu.ispMode || edge == 0 )
      {
    
    
        xEdgeFilterChroma( cu, edgeDir, edge );//色度滤波
      }
    }
  }
}

inline bool LoopFilter::isCrossedByVirtualBoundaries(const int xPos, const int yPos, const int width, const int height, int& numHorVirBndry, int& numVerVirBndry, int horVirBndryPos[], int verVirBndryPos[], const PPS* pps)
{
    
    
  numHorVirBndry = 0; numVerVirBndry = 0;
  if (pps->getLoopFilterAcrossVirtualBoundariesDisabledFlag())
  {
    
    
    for (int i = 0; i < pps->getNumHorVirtualBoundaries(); i++)
    {
    
    
      if (yPos <= pps->getVirtualBoundariesPosY(i) && pps->getVirtualBoundariesPosY(i) < yPos + height)
      {
    
    
        horVirBndryPos[numHorVirBndry++] = pps->getVirtualBoundariesPosY(i);
      }
    }
    for (int i = 0; i < pps->getNumVerVirtualBoundaries(); i++)
    {
    
    
      if (xPos <= pps->getVirtualBoundariesPosX(i) && pps->getVirtualBoundariesPosX(i) < xPos + width)
      {
    
    
        verVirBndryPos[numVerVirBndry++] = pps->getVirtualBoundariesPosX(i);
      }
    }
  }
  return numHorVirBndry > 0 || numVerVirBndry > 0;
}

inline void LoopFilter::xDeriveEdgefilterParam( const int xPos, const int yPos, const int numVerVirBndry, const int numHorVirBndry, const int verVirBndryPos[], const int horVirBndryPos[], bool &verEdgeFilter, bool &horEdgeFilter )
{
    
    
  for (int i = 0; i < numVerVirBndry; i++)
  {
    
    
    if (verVirBndryPos[i] == xPos)
    {
    
    
      verEdgeFilter = false;
      break;
    }
  }

  for (int i = 0; i < numHorVirBndry; i++)
  {
    
    
    if (horVirBndryPos[i] == yPos)
    {
    
    
      horEdgeFilter = false;
      break;
    }
  }
}

void LoopFilter::xSetMaxFilterLengthPQFromTransformSizes( const DeblockEdgeDir edgeDir, const CodingUnit& cu, const TransformUnit& currTU )
{
    
    
  const TransformUnit& tuQ = currTU;//当前正在处理的TU

  if ( edgeDir == EDGE_HOR )//如果是水平
  {
    
    
    for ( int cIdx = 0; cIdx < MAX_NUM_COMPONENT; cIdx++ ) // 遍历每个通道
    {
    
    
      const ComponentID comp = ComponentID(cIdx);//通道
      const ChannelType ch   = toChannelType(comp);//分量
      const int shiftHor     = ( ( ch == CH_L ) ? 0 : m_shiftHor );//色度缩放
      const int shiftVer     = ( ( ch == CH_L ) ? 0 : m_shiftVer );//色度缩放
      const int ctuXOff      = currTU.block(comp).x - ( m_ctuXLumaSamples >> shiftHor ); // x方向距离左CTU的距离 x offset from left edge of CTU in respective channel sample units
      const int ctuYOff      = currTU.block(comp).y - ( m_ctuYLumaSamples >> shiftVer ); // y方向距离上CTU的距离y offset from top edge of CTU in respective channel sample units
      const int minCUWidth   = cu.cs->pcv->minCUWidth >> shiftHor;//最小CU宽度
      if ( currTU.block(comp).valid() && ( ( currTU.block(comp).y == cu.block(comp).y ) ? m_stLFCUParam.topEdge : m_stLFCUParam.internalEdge ) ) // Edge deblocking needs to be recomputed since ISP contains whole CU chroma transforms in last TU of the CU
      {
    
    //由于Isp在cu的最后一个TU中包含整个Cu色度变换,因此需要重新计算边缘去块
        for ( int x = 0; x < currTU.blocks[cIdx].width; x += minCUWidth )//调到下一个边界
        {
    
    
          const Position  posQ     = Position( currTU.blocks[ch].x + x, currTU.blocks[ch].y );//q像素的位置(右侧或下侧)
          const Position  posP     = posQ.offset( 0, -1 );//p像素的位置(左侧或上侧)
          const int sizeQSide      = tuQ.block(comp).height;//Q像素长度
          const TransformUnit& tuP = *cu.cs->getTU( posP, ch );
          const int sizePSide      = tuP.block(comp).height;//Q像素长度
          m_transformEdge[cIdx][ctuXOff+x][ctuYOff] = true;

          if ( comp == COMPONENT_Y )//如果是Y分量
          {
    
    
            bool smallBlock = (sizePSide <= 4) || (sizeQSide <= 4);
            if (smallBlock)//如果是小块,只有1个
            {
    
    
              m_maxFilterLengthQ[cIdx][ctuXOff + x][ctuYOff] = 1;
              m_maxFilterLengthP[cIdx][ctuXOff + x][ctuYOff] = 1;
            }
            else//如果是大块,则7个,否则是3个
            {
    
    
              m_maxFilterLengthQ[cIdx][ctuXOff + x][ctuYOff] = (sizeQSide >= 32) ? 7 : 3;
              m_maxFilterLengthP[cIdx][ctuXOff + x][ctuYOff] = (sizePSide >= 32) ? 7 : 3;
            }
          }
          else//如果是色度分量,大块是8个,小块是1个
          {
    
    
            m_maxFilterLengthQ[cIdx][ctuXOff+x][ctuYOff] = ( sizeQSide >= 8 && sizePSide >= 8 ) ? 3 : 1;
            m_maxFilterLengthP[cIdx][ctuXOff+x][ctuYOff] = ( sizeQSide >= 8 && sizePSide >= 8 ) ? 3 : 1;
          }
        }
      }
    }
  }
  if ( edgeDir == EDGE_VER )//如果是垂直
  {
    
    
    for ( int cIdx = 0; cIdx < MAX_NUM_COMPONENT; cIdx++ ) // 遍历每个通道 per component
    {
    
    
      const ComponentID comp = ComponentID(cIdx);
      const ChannelType ch   = toChannelType(comp);
      const int shiftHor     = ( ( ch == CH_L ) ? 0 : m_shiftHor );
      const int shiftVer     = ( ( ch == CH_L ) ? 0 : m_shiftVer );
      const int ctuXOff      = currTU.block(comp).x - ( m_ctuXLumaSamples >> shiftHor ); // x offset from left edge of CTU in respective channel sample units
      const int ctuYOff      = currTU.block(comp).y - ( m_ctuYLumaSamples >> shiftVer ); // y offset from top edge of CTU in respective channel sample units
      const int minCUHeight  = cu.cs->pcv->minCUHeight >> shiftVer;
      if ( currTU.block(comp).valid() && ( ( currTU.block(comp).x == cu.block(comp).x ) ? m_stLFCUParam.leftEdge : m_stLFCUParam.internalEdge ) ) // Edge deblocking needs to be recomputed since ISP contains whole CU chroma transforms in last TU of the CU
      {
    
    
        for ( int y = 0; y < currTU.blocks[cIdx].height; y += minCUHeight )//遍历每个边缘
        {
    
    
          const Position  posQ     = Position( currTU.blocks[ch].x, currTU.blocks[ch].y + y );
          const Position  posP     = posQ.offset( -1, 0 );
          const int sizeQSide      = tuQ.block(comp).width;
          const TransformUnit& tuP = *cu.cs->getTU( posP, ch );
          const int sizePSide      = tuP.block(comp).width;
          m_transformEdge[cIdx][ctuXOff][ctuYOff+y] = true;

          if ( comp == COMPONENT_Y )//亮度小块1,大块3或7
          {
    
    
            bool smallBlock = (sizePSide <= 4) || (sizeQSide <= 4);
            if (smallBlock)
            {
    
    
              m_maxFilterLengthQ[cIdx][ctuXOff][ctuYOff + y] = 1;
              m_maxFilterLengthP[cIdx][ctuXOff][ctuYOff + y] = 1;
            }
            else
            {
    
    
              m_maxFilterLengthQ[cIdx][ctuXOff][ctuYOff + y] = (sizeQSide >= 32) ? 7 : 3;
              m_maxFilterLengthP[cIdx][ctuXOff][ctuYOff + y] = (sizePSide >= 32) ? 7 : 3;
            }
          }
          else//色度小块1大块3
          {
    
    
            m_maxFilterLengthQ[cIdx][ctuXOff][ctuYOff+y] = ( sizeQSide >= 8 && sizePSide >= 8 ) ? 3 : 1;
            m_maxFilterLengthP[cIdx][ctuXOff][ctuYOff+y] = ( sizeQSide >= 8 && sizePSide >= 8 ) ? 3 : 1;
          }
        }
      }
    }
  }
}

void LoopFilter::xSetMaxFilterLengthPQForCodingSubBlocks( const DeblockEdgeDir edgeDir, const CodingUnit& cu, const PredictionUnit& currPU, const bool& mvSubBlocks, const int& subBlockSize, const Area& areaPu )
{
    
    
  if ( mvSubBlocks && currPU.Y().valid() )
  {
    
    
    const int cIdx         = 0;
    const ComponentID comp = ComponentID(cIdx);
    const int ctuYOff      = currPU.block(comp).y - m_ctuYLumaSamples; // y offset from top edge of CTU in luma samples
    const int ctuXOff      = currPU.block(comp).x - m_ctuXLumaSamples; // x offset from left edge of CTU in luma samples
    const int minCUWidth   = cu.cs->pcv->minCUWidth;
    const int minCUHeight  = cu.cs->pcv->minCUHeight;
    if ( edgeDir == EDGE_HOR )//如果是水平
    {
    
    
      for ( int y = 0; y < areaPu.height; y += subBlockSize )
      {
    
    
        for ( int x = 0; x < areaPu.width; x += minCUWidth )
        {
    
    
          if ( m_transformEdge[cIdx][ctuXOff+x][ctuYOff+y] )
          {
    
    
            m_maxFilterLengthQ[cIdx][ctuXOff+x][ctuYOff+y] = std::min<int>(m_maxFilterLengthQ[cIdx][ctuXOff+x][ctuYOff+y], 5);
            if ( y > 0 )
            {
    
    
              m_maxFilterLengthP[cIdx][ctuXOff+x][ctuYOff+y] = std::min<int>(m_maxFilterLengthP[cIdx][ctuXOff+x][ctuYOff+y], 5);
            }
          }
          else if (y > 0 && (m_transformEdge[cIdx][ctuXOff + x][ctuYOff + y - 4] || ((y + 4) >= areaPu.height) || m_transformEdge[cIdx][ctuXOff + x][ctuYOff + y + 4])) // adjacent to transform edge  +/- 4
          {
    
    
            m_maxFilterLengthQ[cIdx][ctuXOff + x][ctuYOff + y] = 1;
            m_maxFilterLengthP[cIdx][ctuXOff + x][ctuYOff + y] = 1;
          }
          else if (y > 0 && ( m_transformEdge[cIdx][ctuXOff+x][ctuYOff+y-8] || (( y + 8 ) >= areaPu.height) || m_transformEdge[cIdx][ctuXOff+x][ctuYOff+y+8] )) // adjacent to transform edge on 8x8 grid
          {
    
    
            m_maxFilterLengthQ[cIdx][ctuXOff+x][ctuYOff+y] = 2;
            m_maxFilterLengthP[cIdx][ctuXOff+x][ctuYOff+y] = 2;
          }
          else
          {
    
    
            m_maxFilterLengthQ[cIdx][ctuXOff+x][ctuYOff+y] = 3;
            m_maxFilterLengthP[cIdx][ctuXOff+x][ctuYOff+y] = 3;
          }
        }
      }
    }
    else // edgeDir == EDGE_VER  //如果是垂直
    {
    
    
      for ( int x = 0; x < areaPu.width; x += subBlockSize )
      {
    
    
        for ( int y = 0; y < areaPu.height; y += minCUHeight )
        {
    
    
          if ( m_transformEdge[cIdx][ctuXOff+x][ctuYOff+y] )
          {
    
    
            m_maxFilterLengthQ[cIdx][ctuXOff+x][ctuYOff+y] = std::min<int>(m_maxFilterLengthQ[cIdx][ctuXOff+x][ctuYOff+y], 5);
            if ( x > 0 )
            {
    
    
              m_maxFilterLengthP[cIdx][ctuXOff+x][ctuYOff+y] = std::min<int>(m_maxFilterLengthP[cIdx][ctuXOff+x][ctuYOff+y], 5);
            }
          }
          else if (x > 0 && (m_transformEdge[cIdx][ctuXOff + x - 4][ctuYOff + y] || ((x + 4) >= areaPu.width) || m_transformEdge[cIdx][ctuXOff + x + 4][ctuYOff + y])) // adjacent to transform edge +/- 4
          {
    
    
            m_maxFilterLengthQ[cIdx][ctuXOff + x][ctuYOff + y] = 1;
            m_maxFilterLengthP[cIdx][ctuXOff + x][ctuYOff + y] = 1;
          }
          else if ( x > 0 && ( m_transformEdge[cIdx][ctuXOff+x-8][ctuYOff+y] || ( (x + 8) >= areaPu.width ) || m_transformEdge[cIdx][ctuXOff+x+8][ctuYOff+y] ) ) // adjacent to transform edge on 8x8 grid
          {
    
    
            m_maxFilterLengthQ[cIdx][ctuXOff+x][ctuYOff+y] = 2;
            m_maxFilterLengthP[cIdx][ctuXOff+x][ctuYOff+y] = 2;
          }
          else
          {
    
    
            m_maxFilterLengthQ[cIdx][ctuXOff+x][ctuYOff+y] = 3;
            m_maxFilterLengthP[cIdx][ctuXOff+x][ctuYOff+y] = 3;
          }
        }
      }
    }
  }
}

void LoopFilter::xSetEdgefilterMultiple( const CodingUnit&    cu,
                                         const DeblockEdgeDir edgeDir,
                                         const Area&          area,
                                         const bool           bValue,
                                         const bool           EdgeIdx )
{
    
    
  const PreCalcValues& pcv = *cu.cs->pcv;//获取当前信息

  const unsigned uiAdd     = ( edgeDir == EDGE_VER ) ? pcv.partsInCtuWidth : 1;//垂直滤波往下走1个,水平滤波往右走1个
  const unsigned uiNumElem = ( edgeDir == EDGE_VER ) ? ( area.height / pcv.minCUHeight ) : ( area.width / pcv.minCUWidth );//垂直看有几条4x4,水平看有几条4x4,划分成网格
  unsigned uiBsIdx         = getRasterIdx( area, pcv );//当前块是第几个4x4网格,得到一个索引值

  for( int ui = 0; ui < uiNumElem; ui++ )//遍历每个长条网格
  {
    
    
    m_aapbEdgeFilter[edgeDir][uiBsIdx] = bValue;//设置是否使用滤波器?
#if JVET_P0043_DEBLOCKING_CLEANUP 
    if ( m_aapucBS[edgeDir][uiBsIdx] && bValue ) 
    {
    
    
      m_aapucBS[edgeDir][uiBsIdx] = 3;  // 这时候既是TU边界也是PU边界 —— both the TU and PU edge
    }
    else 
    {
    
    
#endif 
      if( ! EdgeIdx )
      {
    
    
        m_aapucBS[edgeDir][uiBsIdx] = bValue;
      }
#if JVET_P0043_DEBLOCKING_CLEANUP 
    }
#endif 
    uiBsIdx += uiAdd;//处理下一个像素
  }
}
void LoopFilter::xSetLoopfilterParam( const CodingUnit& cu )
{
    
    
  const Slice& slice = *cu.slice;//slice信息
  const PPS&   pps   = *cu.cs->pps;//pps信息

  if( slice.getDeblockingFilterDisable() )//如果DBF不可用
  {
    
    
    m_stLFCUParam.leftEdge = m_stLFCUParam.topEdge = m_stLFCUParam.internalEdge = false;//左边上边和内部都不可用
    return;
  }

  const Position& pos = cu.blocks[cu.chType].pos();

  m_stLFCUParam.internalEdge = true;//内部可用
  m_stLFCUParam.leftEdge     = ( 0 < pos.x ) && isAvailableLeft ( cu, *cu.cs->getCU( pos.offset( -1,  0 ), cu.chType ), !slice.getLFCrossSliceBoundaryFlag(), !pps.getLoopFilterAcrossBricksEnabledFlag() );//左可用
  m_stLFCUParam.topEdge      = ( 0 < pos.y ) && isAvailableAbove( cu, *cu.cs->getCU( pos.offset(  0, -1 ), cu.chType ), !slice.getLFCrossSliceBoundaryFlag(), !pps.getLoopFilterAcrossBricksEnabledFlag() );//上可用
}

unsigned LoopFilter::xGetBoundaryStrengthSingle ( const CodingUnit& cu, const DeblockEdgeDir edgeDir, const Position& localPos ) const
{
    
    
  // The boundary strength that is output by the function xGetBoundaryStrengthSingle is a multi component boundary strength that contains boundary strength for luma (bits 0 to 1), cb (bits 2 to 3) and cr (bits 4 to 5).
  /******************* 该函数输出的边界强度是一个多分量BS,其中包含亮度(位0到1),cb(位2到3)和cr(位4到5)的边界强度 ********************/
  const Slice& sliceQ = *cu.slice;

  int shiftHor = cu.Y().valid() ? 0 : ::getComponentScaleX(COMPONENT_Cb, cu.firstPU->chromaFormat);//水平色度缩放
  int shiftVer = cu.Y().valid() ? 0 : ::getComponentScaleY(COMPONENT_Cb, cu.firstPU->chromaFormat);//垂直色度缩放
  const Position& posQ = Position{
    
     localPos.x >> shiftHor,  localPos.y >> shiftVer };
  const Position  posP  = ( edgeDir == EDGE_VER ) ? posQ.offset( -1, 0 ) : posQ.offset( 0, -1 );

  const CodingUnit& cuQ = cu;
  const CodingUnit& cuP = *cu.cs->getCU( posP, cu.chType );

#if !JVET_P0571_FIX_BS_BDPCM_CHROMA
  if( ( MODE_INTRA == cuP.predMode && cuP.bdpcmMode ) && ( MODE_INTRA == cuQ.predMode && cuQ.bdpcmMode ) )
  {
    
    
    return 0;
  }
#endif

  /************************* 帧内:至少一个边是帧内(CIIP) *********************/
  //-- Set BS for Intra MB : BS = 4 or 3
  if( ( MODE_INTRA == cuP.predMode ) || ( MODE_INTRA == cuQ.predMode ) )//如果边缘两个CU都是帧内,
  {
    
    
#if JVET_P0571_FIX_BS_BDPCM_CHROMA
    int bsY = (MODE_INTRA == cuP.predMode && cuP.bdpcmMode) && (MODE_INTRA == cuQ.predMode && cuQ.bdpcmMode) ? 0 : 2;//考虑BDPCM
#if JVET_P0059_CHROMA_BDPCM
    int bsC = (MODE_INTRA == cuP.predMode && cuP.bdpcmModeChroma) && (MODE_INTRA == cuQ.predMode && cuQ.bdpcmModeChroma) ? 0 : 2;
#else
    int bsC = 2;
#endif
    return (BsSet(bsY, COMPONENT_Y) + BsSet(bsC, COMPONENT_Cb) + BsSet(bsC, COMPONENT_Cr));//边界设为亮度2,色度2和2
#else
    return (BsSet(2, COMPONENT_Y) + BsSet(2, COMPONENT_Cb) + BsSet(2, COMPONENT_Cr));
#endif
  }

  const TransformUnit& tuQ = *cuQ.cs->getTU(posQ, cuQ.chType);
  const TransformUnit& tuP = *cuP.cs->getTU(posP, cuQ.chType); //based on chType of the current cu, because cuQ.chType and cuP.chType are not the same when local dual-tree is applied
  const PreCalcValues& pcv = *cu.cs->pcv;
  const unsigned rasterIdx = getRasterIdx( Position{
    
     localPos.x,  localPos.y }, pcv );
  if (m_aapucBS[edgeDir][rasterIdx] && (cuP.firstPU->mhIntraFlag || cuQ.firstPU->mhIntraFlag))//如果是CIIP
  {
    
    
     return (BsSet(2, COMPONENT_Y) + BsSet(2, COMPONENT_Cb) + BsSet(2, COMPONENT_Cr));//边界设为亮度2,色度2和2
  }

  unsigned tmpBs = 0;// 变换系数得到的BS

  /************************* 帧内:如果一边有非零变换系数 *********************/
  //-- Set BS for not Intra MB : BS = 2 or 1 or 0
  // Y
  if (m_aapucBS[edgeDir][rasterIdx] && (TU::getCbf(tuQ, COMPONENT_Y) || TU::getCbf(tuP, COMPONENT_Y)))
  {
    
    
    tmpBs += BsSet(1, COMPONENT_Y);
  }
  // U
  if (m_aapucBS[edgeDir][rasterIdx] && (TU::getCbf(tuQ, COMPONENT_Cb) || TU::getCbf(tuP, COMPONENT_Cb) || tuQ.jointCbCr || tuP.jointCbCr))
  {
    
    
    tmpBs += BsSet(1, COMPONENT_Cb);
  }
  // V
  if (m_aapucBS[edgeDir][rasterIdx] && (TU::getCbf(tuQ, COMPONENT_Cr) || TU::getCbf(tuP, COMPONENT_Cr) || tuQ.jointCbCr || tuP.jointCbCr))
  {
    
    
    tmpBs += BsSet(1, COMPONENT_Cr);
  }
  if (BsGet(tmpBs, COMPONENT_Y) == 1)
  {
    
    
    return tmpBs;
  }
  if ((cuP.firstPU->mhIntraFlag || cuQ.firstPU->mhIntraFlag))
  {
    
    
    return 1;
  }

  if ( !cu.Y().valid() )
  {
    
    
    return tmpBs;
  }

  // and now the pred
#if JVET_P0043_DEBLOCKING_CLEANUP 
  if ( m_aapucBS[edgeDir][rasterIdx] != 0 && m_aapucBS[edgeDir][rasterIdx] != 3 ) return tmpBs;
#endif
  const Position& lumaPosQ  = Position{
    
     localPos.x,  localPos.y };
  const Position  lumaPosP  = ( edgeDir == EDGE_VER ) ? lumaPosQ.offset( -1, 0 ) : lumaPosQ.offset( 0, -1 );
  const MotionInfo&     miQ = cuQ.cs->getMotionInfo( lumaPosQ );
  const MotionInfo&     miP = cuP.cs->getMotionInfo( lumaPosP );
  const Slice&       sliceP = *cuP.slice;

  /************************* 帧间:如果MVD大于半像素精度 *********************/
  if (sliceQ.isInterB() || sliceP.isInterB())
  {
    
    
    const Picture *piRefP0 = (CU::isIBC(cuP) ? sliceP.getPic() : ((0 > miP.refIdx[0]) ? NULL : sliceP.getRefPic(REF_PIC_LIST_0, miP.refIdx[0])));
    const Picture *piRefP1 = (CU::isIBC(cuP) ? NULL            : ((0 > miP.refIdx[1]) ? NULL : sliceP.getRefPic(REF_PIC_LIST_1, miP.refIdx[1])));
    const Picture *piRefQ0 = (CU::isIBC(cuQ) ? sliceQ.getPic() : ((0 > miQ.refIdx[0]) ? NULL : sliceQ.getRefPic(REF_PIC_LIST_0, miQ.refIdx[0])));
    const Picture *piRefQ1 = (CU::isIBC(cuQ) ? NULL            : ((0 > miQ.refIdx[1]) ? NULL : sliceQ.getRefPic(REF_PIC_LIST_1, miQ.refIdx[1])));
    Mv mvP0, mvP1, mvQ0, mvQ1;

    if( 0 <= miP.refIdx[0] ) {
    
     mvP0 = miP.mv[0]; }
    if( 0 <= miP.refIdx[1] ) {
    
     mvP1 = miP.mv[1]; }
    if( 0 <= miQ.refIdx[0] ) {
    
     mvQ0 = miQ.mv[0]; }
    if( 0 <= miQ.refIdx[1] ) {
    
     mvQ1 = miQ.mv[1]; }

    int nThreshold = (1 << MV_FRACTIONAL_BITS_INTERNAL) >> 1;
    unsigned uiBs = 0;

    //th can be optimized 可以优化
    if ( ((piRefP0==piRefQ0)&&(piRefP1==piRefQ1)) || ((piRefP0==piRefQ1)&&(piRefP1==piRefQ0)) )//参考帧是相同的帧
    {
    
    
      if ( piRefP0 != piRefP1 )   // Different L0 & L1 如果参考帧不同,其中一个MVD大于半像素精度,则BS为1
      {
    
    
        if ( piRefP0 == piRefQ0 )
        {
    
    
          uiBs  = ((abs(mvQ0.getHor() - mvP0.getHor()) >= nThreshold) || (abs(mvQ0.getVer() - mvP0.getVer()) >= nThreshold) ||
                   (abs(mvQ1.getHor() - mvP1.getHor()) >= nThreshold) || (abs(mvQ1.getVer() - mvP1.getVer()) >= nThreshold))
                  ? 1 : 0;
        }
        else
        {
    
    
          uiBs  = ((abs(mvQ1.getHor() - mvP0.getHor()) >= nThreshold) || (abs(mvQ1.getVer() - mvP0.getVer()) >= nThreshold) ||
                   (abs(mvQ0.getHor() - mvP1.getHor()) >= nThreshold) || (abs(mvQ0.getVer() - mvP1.getVer()) >= nThreshold))
                  ? 1 : 0;
        }
      }
      else    // Same L0 & L1 如果参考帧相同,同时满足两个MVD大于半像素精度,则BS为1
      {
    
    
        uiBs  = ((abs(mvQ0.getHor() - mvP0.getHor()) >= nThreshold) || (abs(mvQ0.getVer() - mvP0.getVer()) >= nThreshold) ||
                 (abs(mvQ1.getHor() - mvP1.getHor()) >= nThreshold) || (abs(mvQ1.getVer() - mvP1.getVer()) >= nThreshold))
              &&
                ((abs(mvQ1.getHor() - mvP0.getHor()) >= nThreshold) || (abs(mvQ1.getVer() - mvP0.getVer()) >= nThreshold) ||
                 (abs(mvQ0.getHor() - mvP1.getHor()) >= nThreshold) || (abs(mvQ0.getVer() - mvP1.getVer()) >= nThreshold))
              ? 1 : 0;
      }
    }
    else // for all different Ref_Idx //参考帧是不同的帧,则设置滤波强度为1
    {
    
    
      uiBs = 1;
    }
    return uiBs + tmpBs;
  }

  /************************* 帧间:如果参考帧不同,BS为1 *********************/
  // pcSlice->isInterP()
  CHECK(CU::isInter(cuP) && 0 > miP.refIdx[0], "Invalid reference picture list index");
  CHECK(CU::isInter(cuP) && 0 > miQ.refIdx[0], "Invalid reference picture list index");
  const Picture *piRefP0 = (CU::isIBC(cuP) ? sliceP.getPic() : sliceP.getRefPic(REF_PIC_LIST_0, miP.refIdx[0]));
  const Picture *piRefQ0 = (CU::isIBC(cuQ) ? sliceQ.getPic() : sliceQ.getRefPic(REF_PIC_LIST_0, miQ.refIdx[0]));
  if (piRefP0 != piRefQ0)
  {
    
    
    return tmpBs + 1;
  }

  Mv mvP0 = miP.mv[0];
  Mv mvQ0 = miQ.mv[0];

  int nThreshold = (1 << MV_FRACTIONAL_BITS_INTERNAL) >> 1;
  return ( ( abs( mvQ0.getHor() - mvP0.getHor() ) >= nThreshold ) || ( abs( mvQ0.getVer() - mvP0.getVer() ) >= nThreshold ) ) ? (tmpBs + 1) : tmpBs;
}

#if LUMA_ADAPTIVE_DEBLOCKING_FILTER_QP_OFFSET
void LoopFilter::deriveLADFShift( const Pel* src, const int stride, int& shift, const DeblockEdgeDir edgeDir, const SPS sps )
{
    
    
  uint32_t lumaLevel = 0;
  shift = sps.getLadfQpOffset(0);//在sps层进行初始化设置

  if (edgeDir == EDGE_VER)//计算亮度水平
  {
    
    
    lumaLevel = (src[0] + src[3*stride] + src[-1] + src[3*stride - 1]) >> 2;
  }
  else // (edgeDir == EDGE_HOR)
  {
    
    
    lumaLevel = (src[0] + src[3] + src[-stride] + src[-stride + 3]) >> 2;
  }

  for ( int k = 1; k < sps.getLadfNumIntervals(); k++ )
  {
    
    
    const int th = sps.getLadfIntervalLowerBound( k );
    if ( lumaLevel > th )//如果亮度水平超过了,则改变shift的值
    {
    
    
      shift = sps.getLadfQpOffset( k );
    }
    else
    {
    
    
      break;
    }
  }
}
#endif

void LoopFilter::xEdgeFilterLuma( const CodingUnit& cu, const DeblockEdgeDir edgeDir, const int iEdge )
{
    
    
  /********************************************* 初始化 **********************************************/
  const CompArea&  lumaArea = cu.block(COMPONENT_Y);//获取亮度区域
  const PreCalcValues& pcv = *cu.cs->pcv;//cs信息

  PelBuf        picYuvRec = m_enc ? m_encPicYuvBuffer.getBuf( lumaArea ) : cu.cs->getRecoBuf( lumaArea );//编码过程则获取缓存,解码过程获取重构信息
  Pel           *piSrc    = picYuvRec.buf;//编码时的像素缓存
  const int     iStride   = picYuvRec.stride;
  Pel           *piTmpSrc = piSrc;//临时缓存
  const PPS     &pps      = *(cu.cs->pps);
  const SPS     &sps      = *(cu.cs->sps);
  const Slice   &slice    = *(cu.slice);
  const bool    ppsTransquantBypassEnabledFlag = pps.getTransquantBypassEnabledFlag();//是不是TS模式
  const bool    spsPaletteEnabledFlag          = sps.getPLTMode();//是不是PLT模式
  const int     bitDepthLuma                   = sps.getBitDepth(CHANNEL_TYPE_LUMA);//比特深度
  const ClpRng& clpRng( cu.cs->slice->clpRng(COMPONENT_Y) );

  int          iQP          = 0;
  unsigned     uiNumParts   = ( ( ( edgeDir == EDGE_VER ) ? lumaArea.height / pcv.minCUHeight : lumaArea.width / pcv.minCUWidth ) );
  int          pelsInPart   = pcv.minCUWidth;//CU宽度——下一行
  unsigned     uiBsAbsIdx   = 0, uiBs = 0;
  int          iOffset, iSrcStep;

  bool  bPartPNoFilter  = false;
  bool  bPartQNoFilter  = false;
  int   betaOffsetDiv2  = slice.getDeblockingFilterBetaOffsetDiv2();	//beta
  int   tcOffsetDiv2    = slice.getDeblockingFilterTcOffsetDiv2();		//tc
  int   xoffset, yoffset;

  Position pos;

  if (edgeDir == EDGE_VER)//垂直滤波所需信息
  {
    
    
    xoffset   = 0;
    yoffset   = pelsInPart;
    iOffset   = 1;
    iSrcStep  = iStride;
    piTmpSrc += iEdge * pelsInPart;
    pos       = Position{
    
     lumaArea.x + iEdge * pelsInPart, lumaArea.y - yoffset };
  }
  else  // (edgeDir == EDGE_HOR) //水平滤波所需信息
  {
    
    
    xoffset   = pelsInPart;
    yoffset   = 0;
    iOffset   = iStride;
    iSrcStep  = 1;
    piTmpSrc += iEdge*pelsInPart*iStride;
    pos       = Position{
    
     lumaArea.x - xoffset, lumaArea.y + iEdge * pelsInPart };
  }

  const int iBitdepthScale = 1 << (bitDepthLuma - 8);

  /****************************************** 滤波决策及滤波过程 **************************************************/
  // dec pos since within the loop we first calc the pos 首先计算位置
  for( int iIdx = 0; iIdx < uiNumParts; iIdx++ )/第一层遍历:遍历每个块?
  {
    
    
    pos.x += xoffset;
    pos.y += yoffset;

    // Deblock luma boundaries on 4x4 grid only 只以亮度4x4网格作为边界,否则不滤波
    if (edgeDir == EDGE_HOR && (pos.y % 4) != 0)
    {
    
    
      continue;
    }
    if (edgeDir == EDGE_VER && (pos.x % 4) != 0)
    {
    
    
      continue;
    }
    uiBsAbsIdx = getRasterIdx( pos, pcv );//获取网格索引
    uiBs = BsGet(m_aapucBS[edgeDir][uiBsAbsIdx], COMPONENT_Y);//获取这个网格的BS

    if( uiBs )//如果存在滤波强度BS,则进行DBF滤波
    {
    
    
      const CodingUnit& cuQ =  cu;
      const CodingUnit& cuP = *cu.cs->getCU(pos.offset(xoffset - pelsInPart, yoffset - pelsInPart), cu.chType);
      // Derive neighboring PU index
      if (edgeDir == EDGE_VER)
      {
    
    
        CHECK( !isAvailableLeft( cu, cuP, !slice.getLFCrossSliceBoundaryFlag(), !pps.getLoopFilterAcrossBricksEnabledFlag() ), "Neighbour not available" );
      }
      else  // (iDir == EDGE_HOR)
      {
    
    
        CHECK( !isAvailableAbove( cu, cuP, !slice.getLFCrossSliceBoundaryFlag(), !pps.getLoopFilterAcrossBricksEnabledFlag() ), "Neighbour not available" );
      }

	  /****************** 计算QP确定查表索引 ******************/
      iQP = (cuP.qp + cuQ.qp + 1) >> 1;//获取QP作为第一维索引,后面还要加一个shift

#if LUMA_ADAPTIVE_DEBLOCKING_FILTER_QP_OFFSET
      if ( sps.getLadfEnabled() )//获取QP的shift ================
      {
    
    
        int iShift = 0;//offset
        deriveLADFShift( piTmpSrc + iSrcStep * (iIdx*pelsInPart), iStride, iShift, edgeDir, sps );//计算ishift的值
        iQP += iShift;//加进来
      }
#endif

      bool sidePisLarge   = false;//大块判定
      bool sideQisLarge   = false;//大块判定
      int maxFilterLengthP = m_maxFilterLengthP[COMPONENT_Y][pos.x-m_ctuXLumaSamples][pos.y-m_ctuYLumaSamples];//滤波抽头长度
      int maxFilterLengthQ = m_maxFilterLengthQ[COMPONENT_Y][pos.x-m_ctuXLumaSamples][pos.y-m_ctuYLumaSamples];//滤波抽头长度
      if (maxFilterLengthP > 3)//滤波抽头长度大于3
      {
    
    
        sidePisLarge = true;
        if ( maxFilterLengthP > 5 )//滤波抽头长度大于5,判断是否为affine
        {
    
    
          // restrict filter length if sub-blocks are used (e.g affine or ATMVP)  如果使用子块,则限制过滤器长度(例如,affine或ATMVP)
          if (cuP.affine)
          {
    
    
            maxFilterLengthP = std::min(maxFilterLengthP, 5);
          }
        }
      }
      if (maxFilterLengthQ > 3)//滤波抽头长度大于3,则有大块判断
      {
    
    
        sideQisLarge = true;
      }

      if (edgeDir == EDGE_HOR && pos.y % slice.getSPS()->getCTUSize() == 0)
      {
    
    
        sidePisLarge = false;
      }

	  /**************** 获取查表得到的tc,beta数值 ****************/
      const int iIndexTC  = Clip3(0, MAX_QP + DEFAULT_INTRA_TC_OFFSET, int(iQP + DEFAULT_INTRA_TC_OFFSET*(uiBs - 1) + (tcOffsetDiv2 << 1)));//tc索引
      const int iIndexB   = Clip3(0, MAX_QP, iQP + (betaOffsetDiv2 << 1));//beta索引

      const int iTc = bitDepthLuma < 10 ? ((sm_tcTable[iIndexTC] + 2) >> (10 - bitDepthLuma)) : ((sm_tcTable[iIndexTC]) << (bitDepthLuma - 10));//tc值
      const int iBeta     = sm_betaTable[iIndexB ] * iBitdepthScale;//beta值
      const int iSideThreshold = ( iBeta + ( iBeta >> 1 ) ) >> 3;
      const int iThrCut   = iTc * 10;

      const unsigned uiBlocksInPart = pelsInPart / 4 ? pelsInPart / 4 : 1;//当前CU中一行有几个4x4块

      for( int iBlkIdx = 0; iBlkIdx < uiBlocksInPart; iBlkIdx++ )/第二层遍历:遍历4x4的块?
      {
    
    
		/****************** 1.2 根据像素的变化率,来进行一次滤波开关决策,与QP找到的beta有关 ******************/
        const int dp0 = xCalcDP(piTmpSrc + iSrcStep*(iIdx*pelsInPart + iBlkIdx * 4 + 0), iOffset);//P块首行像素变化率  * * * *DP0 DQ0* * * *
        const int dq0 = xCalcDQ(piTmpSrc + iSrcStep*(iIdx*pelsInPart + iBlkIdx * 4 + 0), iOffset);//Q块首行像素变化率  * * * *       * * * *
        const int dp3 = xCalcDP(piTmpSrc + iSrcStep*(iIdx*pelsInPart + iBlkIdx * 4 + 3), iOffset);//P块末行像素变化率  * * * *       * * * *
        const int dq3 = xCalcDQ(piTmpSrc + iSrcStep*(iIdx*pelsInPart + iBlkIdx * 4 + 3), iOffset);//Q块末行像素变化率  * * * *DP3 DQ3* * * * 
        int dp0L = dp0;//对大块变化率的初始化
        int dq0L = dq0;
        int dp3L = dp3;
        int dq3L = dq3;

        if (sidePisLarge)//如果P是大块
        {
    
    
          dp0L = (dp0L + xCalcDP(piTmpSrc + iSrcStep*(iIdx*pelsInPart + iBlkIdx * 4 + 0) - 3 * iOffset, iOffset) + 1) >> 1;//P块首行像素变化率
          dp3L = (dp3L + xCalcDP(piTmpSrc + iSrcStep*(iIdx*pelsInPart + iBlkIdx * 4 + 3) - 3 * iOffset, iOffset) + 1) >> 1;//P块末行像素变化率
        }
        if (sideQisLarge)//如果Q是大块
        {
    
    
          dq0L = (dq0L + xCalcDQ(piTmpSrc + iSrcStep*(iIdx*pelsInPart + iBlkIdx * 4 + 0) + 3 * iOffset, iOffset) + 1) >> 1;//Q块首行像素变化率
          dq3L = (dq3L + xCalcDQ(piTmpSrc + iSrcStep*(iIdx*pelsInPart + iBlkIdx * 4 + 3) + 3 * iOffset, iOffset) + 1) >> 1;//Q块末行像素变化率
        }
        bool useLongtapFilter = false;

		/****************** 大块:双线性强滤波 ******************/
        if (sidePisLarge || sideQisLarge)//如果其中一个是大块
        {
    
    
          int d0L = dp0L + dq0L;
          int d3L = dp3L + dq3L;

          int dpL = dp0L + dp3L;
          int dqL = dq0L + dq3L;

          int dL = d0L + d3L;//总变化率

		  /*** 判断是否不能滤波 ***/
          bPartPNoFilter = bPartQNoFilter = false;
          if (ppsTransquantBypassEnabledFlag)
          {
    
    
            // check if each of PUs is lossless coded
            bPartPNoFilter = bPartPNoFilter || cuP.transQuantBypass;
            bPartQNoFilter = bPartQNoFilter || cuQ.transQuantBypass;
          }
          if (spsPaletteEnabledFlag)
          {
    
    
            // check if each of PUs is palette coded
            bPartPNoFilter = bPartPNoFilter || CU::isPLT(cuP);
            bPartQNoFilter = bPartQNoFilter || CU::isPLT(cuQ);
          }
		  /****************** 1.3 根据像素的变化率,来进行一次滤波强弱选择,与beta和tc都有关 ******************/
          if (dL < iBeta)//条件2:d需要小于β
          {
    
    
            const bool filterP = (dpL < iSideThreshold);
            const bool filterQ = (dqL < iSideThreshold);

            Pel* src0 = piTmpSrc + iSrcStep * (iIdx*pelsInPart + iBlkIdx * 4 + 0);
            Pel* src3 = piTmpSrc + iSrcStep * (iIdx*pelsInPart + iBlkIdx * 4 + 3);

            // adjust decision so that it is not read beyond p5 is maxFilterLengthP is 5 and q5 if maxFilterLengthQ is 5
            const bool swL = xUseStrongFiltering(src0, iOffset, 2 * d0L, iBeta, iTc, sidePisLarge, sideQisLarge, maxFilterLengthP, maxFilterLengthQ)
              && xUseStrongFiltering(src3, iOffset, 2 * d3L, iBeta, iTc, sidePisLarge, sideQisLarge, maxFilterLengthP, maxFilterLengthQ);
            if (swL)//如果是强滤波
            {
    
    
              useLongtapFilter = true;
              for (int i = 0; i < DEBLOCK_SMALLEST_BLOCK / 2; i++)//亮度像素级的滤波
              {
    
    
                xPelFilterLuma(piTmpSrc + iSrcStep*(iIdx*pelsInPart + iBlkIdx * 4 + i), iOffset, iTc, swL, bPartPNoFilter, bPartQNoFilter, iThrCut, filterP, filterQ, clpRng, sidePisLarge, sideQisLarge, maxFilterLengthP, maxFilterLengthQ);
              }
            }

          }
        }

		/************** 对不进行强滤波的大块和普通的小块进行如下滤波 *************/
        if (!useLongtapFilter)//不进行强滤波的大块
        {
    
    
        const int d0 = dp0 + dq0;
        const int d3 = dp3 + dq3;

        const int dp = dp0 + dp3;
        const int dq = dq0 + dq3;
        const int d  = d0  + d3;//总变化率

		/*** 判断是否不能滤波 ***/
        bPartPNoFilter = bPartQNoFilter = false; //检查是否不能进行滤波
        if( ppsTransquantBypassEnabledFlag )
        {
    
    
          // check if each of PUs is lossless coded
          bPartPNoFilter = bPartPNoFilter || cuP.transQuantBypass;
          bPartQNoFilter = bPartQNoFilter || cuQ.transQuantBypass;
        }
        if( spsPaletteEnabledFlag)
        {
    
    
          // check if each of PUs is palette coded
          bPartPNoFilter = bPartPNoFilter || CU::isPLT(cuP);
          bPartQNoFilter = bPartQNoFilter || CU::isPLT(cuQ);
        }

		/*** 进行亮度DBF滤波 ***/
        if( d < iBeta )//条件2:d需要小于beta
        {
    
    
          bool bFilterP = false;
          bool bFilterQ = false;
          if (maxFilterLengthP > 1 && maxFilterLengthQ > 1)
          {
    
    
            bFilterP = (dp < iSideThreshold);//是否对左边缘滤波
            bFilterQ = (dq < iSideThreshold);//是否对右边缘滤波
          }
          bool sw = false;
          if (maxFilterLengthP > 2 && maxFilterLengthQ > 2)//是否使用更强的滤波
          {
    
    
            sw = xUseStrongFiltering(piTmpSrc + iSrcStep * (iIdx*pelsInPart + iBlkIdx * 4 + 0), iOffset, 2 * d0, iBeta, iTc)
              && xUseStrongFiltering(piTmpSrc + iSrcStep * (iIdx*pelsInPart + iBlkIdx * 4 + 3), iOffset, 2 * d3, iBeta, iTc);
          }
          for( int i = 0; i < DEBLOCK_SMALLEST_BLOCK / 2; i++ )//进行像素级的滤波
          {
    
    
            xPelFilterLuma( piTmpSrc + iSrcStep*( iIdx*pelsInPart + iBlkIdx * 4 + i ), iOffset, iTc, sw, bPartPNoFilter, bPartQNoFilter, iThrCut, bFilterP, bFilterQ, clpRng );
          }
        }
        }
      }
    }
  }
}


void LoopFilter::xEdgeFilterChroma(const CodingUnit& cu, const DeblockEdgeDir edgeDir, const int iEdge)
{
    
    
	/********************************* 初始化 *********************************/
  const Position lumaPos   = cu.Y().valid() ? cu.Y().pos() : recalcPosition( cu.chromaFormat, cu.chType, CHANNEL_TYPE_LUMA, cu.blocks[cu.chType].pos() );
  const Size     lumaSize  = cu.Y().valid() ? cu.Y().size() : recalcSize( cu.chromaFormat, cu.chType, CHANNEL_TYPE_LUMA, cu.blocks[cu.chType].size() );

  const PreCalcValues& pcv = *cu.cs->pcv;
  unsigned  rasterIdx      = getRasterIdx( lumaPos, pcv );//获取网格索引
  PelBuf     picYuvRecCb = m_enc ? m_encPicYuvBuffer.getBuf(cu.block(COMPONENT_Cb)) : cu.cs->getRecoBuf(cu.block(COMPONENT_Cb));
  PelBuf     picYuvRecCr = m_enc ? m_encPicYuvBuffer.getBuf(cu.block(COMPONENT_Cr)) : cu.cs->getRecoBuf(cu.block(COMPONENT_Cr));
  Pel       *piSrcCb       = picYuvRecCb.buf;
  Pel       *piSrcCr       = picYuvRecCr.buf;
  const int  iStride       = picYuvRecCb.stride;
  const SPS &sps           = *cu.cs->sps;
  const PPS &pps           = *cu.cs->pps;
  const Slice  &slice      = *cu.slice;
  const ChromaFormat nChromaFormat   = sps.getChromaFormatIdc();
  const unsigned uiPelsInPartChromaH = pcv.minCUWidth  >> ::getComponentScaleX(COMPONENT_Cb, nChromaFormat);
  const unsigned uiPelsInPartChromaV = pcv.minCUHeight >> ::getComponentScaleY(COMPONENT_Cb, nChromaFormat);

  int       iOffset, iSrcStep;
  unsigned  uiLoopLength;

  bool      bPartPNoFilter  = false;
  bool      bPartQNoFilter  = false;
  const int tcOffsetDiv2    = slice.getDeblockingFilterTcOffsetDiv2();
  const int betaOffsetDiv2  = slice.getDeblockingFilterBetaOffsetDiv2();

  // Vertical Position 垂直位置
  unsigned uiEdgeNumInCtuVert = rasterIdx % pcv.partsInCtuWidth + iEdge;
  unsigned uiEdgeNumInCtuHor  = rasterIdx / pcv.partsInCtuWidth + iEdge;

  if( ( uiPelsInPartChromaH < DEBLOCK_SMALLEST_BLOCK ) && ( uiPelsInPartChromaV < DEBLOCK_SMALLEST_BLOCK ) &&
      (
        ( ( uiEdgeNumInCtuVert % ( DEBLOCK_SMALLEST_BLOCK / uiPelsInPartChromaH ) ) && ( edgeDir == EDGE_VER ) ) ||
        ( ( uiEdgeNumInCtuHor  % ( DEBLOCK_SMALLEST_BLOCK / uiPelsInPartChromaV ) ) && ( edgeDir == EDGE_HOR ) )
      )
    )
  {
    
    
    return;
  }

  unsigned uiNumParts =  ( edgeDir == EDGE_VER ) ? lumaSize.height / pcv.minCUHeight : lumaSize.width / pcv.minCUWidth ;
  int   uiNumPelsLuma = pcv.minCUWidth;
  unsigned uiBsAbsIdx;
  unsigned bS[2];

  Pel* piTmpSrcCb = piSrcCb;
  Pel* piTmpSrcCr = piSrcCr;
  int xoffset, yoffset;
  Position pos( lumaPos.x, lumaPos.y );

  if( edgeDir == EDGE_VER )
  {
    
    
    xoffset      = 0;
    yoffset      = uiNumPelsLuma;
    iOffset      = 1;
    iSrcStep     = iStride;
    piTmpSrcCb  += iEdge*uiPelsInPartChromaH;
    piTmpSrcCr  += iEdge*uiPelsInPartChromaH;
    uiLoopLength = uiPelsInPartChromaV;
    pos          = Position{
    
     lumaPos.x + iEdge*uiNumPelsLuma, lumaPos.y - yoffset };
  }
  else  // (edgeDir == EDGE_HOR)
  {
    
    
    xoffset      = uiNumPelsLuma;
    yoffset      = 0;
    iOffset      = iStride;
    iSrcStep     = 1;
    piTmpSrcCb  += iEdge*iStride*uiPelsInPartChromaV;
    piTmpSrcCr  += iEdge*iStride*uiPelsInPartChromaV;
    uiLoopLength = uiPelsInPartChromaH;
    pos          = Position{
    
     lumaPos.x - xoffset, lumaPos.y + iEdge*uiNumPelsLuma };
  }

  const int iBitdepthScale = 1 << (sps.getBitDepth(CHANNEL_TYPE_CHROMA) - 8);

  for( int iIdx = 0; iIdx < uiNumParts; iIdx++ )
  {
    
    
    pos.x += xoffset;
    pos.y += yoffset;

    uiBsAbsIdx = getRasterIdx( pos, pcv );
    unsigned tmpBs = m_aapucBS[edgeDir][uiBsAbsIdx];

    tmpBs = m_aapucBS[edgeDir][uiBsAbsIdx];
    bS[0] = BsGet(tmpBs, COMPONENT_Cb);
    bS[1] = BsGet(tmpBs, COMPONENT_Cr);

    if (bS[0] > 0 || bS[1] > 0)
    {
    
    
      const CodingUnit& cuQ =  cu;
      CodingUnit& cuP1 = *cu.cs->getCU( recalcPosition( cu.chromaFormat, CHANNEL_TYPE_LUMA, cu.chType, pos.offset( xoffset - uiNumPelsLuma, yoffset - uiNumPelsLuma ) ), cu.chType );
      CodingUnit& cuP  = *cu.cs->getCU( recalcPosition( cu.chromaFormat, CHANNEL_TYPE_LUMA, ((!cuP1.cs->pcv->ISingleTree && cuP1.slice->isIntra()) ? CHANNEL_TYPE_CHROMA : cu.chType), pos.offset( xoffset - uiNumPelsLuma, yoffset - uiNumPelsLuma ) ), ((!cuP1.cs->pcv->ISingleTree && cuP1.slice->isIntra()) ? CHANNEL_TYPE_CHROMA : cu.chType));

      if (edgeDir == EDGE_VER)
      {
    
    
        CHECK(!isAvailableLeft(cu, cuP, !slice.getLFCrossSliceBoundaryFlag(), !pps.getLoopFilterAcrossBricksEnabledFlag()), "Neighbour not available");
      }
      else  // (iDir == EDGE_HOR)
      {
    
    
        CHECK(!isAvailableAbove(cu, cuP, !slice.getLFCrossSliceBoundaryFlag(), !pps.getLoopFilterAcrossBricksEnabledFlag()), "Neighbour not available");
      }

      bPartPNoFilter = bPartQNoFilter = false;
      if( pps.getTransquantBypassEnabledFlag() )
      {
    
    
        // check if each of PUs is lossless coded
        bPartPNoFilter = bPartPNoFilter || cuP.transQuantBypass;
        bPartQNoFilter = bPartQNoFilter || cuQ.transQuantBypass;
      }
      if ( sps.getPLTMode())
      {
    
    
        // check if each of PUs is palette coded
        bPartPNoFilter = bPartPNoFilter || CU::isPLT(cuP);
        bPartQNoFilter = bPartQNoFilter || CU::isPLT(cuQ);
      }

      const int maxFilterLengthP = m_maxFilterLengthP[COMPONENT_Cb][(pos.x-m_ctuXLumaSamples)>>m_shiftHor][(pos.y-m_ctuYLumaSamples)>>m_shiftVer];
      const int maxFilterLengthQ = m_maxFilterLengthQ[COMPONENT_Cb][(pos.x-m_ctuXLumaSamples)>>m_shiftHor][(pos.y-m_ctuYLumaSamples)>>m_shiftVer];
      bool largeBoundary         = false;
#if JVET_P0081_CHROMA_LONG_DEBLOCKING_FIX
      bool isChromaHorCTBBoundary = false;
#endif
      if ( maxFilterLengthP >= 3 && maxFilterLengthQ >= 3 )
      {
    
    
        largeBoundary = true;
      }

      if (edgeDir == EDGE_HOR && pos.y % cuP.slice->getSPS()->getCTUSize() == 0)
      {
    
    
#if JVET_P0081_CHROMA_LONG_DEBLOCKING_FIX
        isChromaHorCTBBoundary = true;
#else
        largeBoundary = false;
#endif 
      }

      for( int chromaIdx = 0; chromaIdx < 2; chromaIdx++ )//遍历两个色度通道,和亮度基本相同的方式进行滤波
      {
    
    
        if ((bS[chromaIdx] == 2) || (largeBoundary && (bS[chromaIdx] == 1)))//滤波条件:BS为2 或 大块的BS为1
        {
    
    
        const ClpRng& clpRng( cu.cs->slice->clpRng( ComponentID( chromaIdx + 1 )) );
#if !JVET_P1001_DEBLOCKING_CHROMAQP_FIX
        const int chromaQPOffset = pps.getQpOffset( ComponentID( chromaIdx + 1 ) );
#endif
        Pel* piTmpSrcChroma = (chromaIdx == 0) ? piTmpSrcCb : piTmpSrcCr;

#if JVET_P1001_DEBLOCKING_CHROMAQP_FIX
        int shiftHor = cu.Y().valid() ? 0 : ::getComponentScaleX(COMPONENT_Cb, cu.firstPU->chromaFormat);
        int shiftVer = cu.Y().valid() ? 0 : ::getComponentScaleY(COMPONENT_Cb, cu.firstPU->chromaFormat);
        const Position& posQ = Position{
    
     pos.x >> shiftHor,  pos.y >> shiftVer };
        const Position  posP = (edgeDir == EDGE_VER) ? posQ.offset(-1, 0) : posQ.offset(0, -1);

        const TransformUnit& tuQ = *cuQ.cs->getTU(posQ, cuQ.chType);
        const TransformUnit& tuP = *cuP.cs->getTU(posP, cuQ.chType); //based on chType of the current cu, because cuQ.chType and cuP.chType are not the same when local dual-tree is applied

        const QpParam cQP(tuP, ComponentID(chromaIdx + 1));
        const QpParam cQQ(tuQ, ComponentID(chromaIdx + 1));
        const int qpBdOffset = tuP.cs->sps->getQpBDOffset(toChannelType(ComponentID(chromaIdx + 1)));
        int baseQp_P = cQP.Qp(0) - qpBdOffset;
        int baseQp_Q = cQQ.Qp(0) - qpBdOffset;
        int iQP = ((baseQp_Q + baseQp_P + 1) >> 1);
#else
        int iQP = sps.getMappedChromaQpValue(ComponentID(chromaIdx + 1), ((cuP.qp + cuQ.qp + 1) >> 1));
        iQP = Clip3(0, MAX_QP, iQP + chromaQPOffset);
#endif


        const int iIndexTC = Clip3<int>(0, MAX_QP + DEFAULT_INTRA_TC_OFFSET, iQP + DEFAULT_INTRA_TC_OFFSET * (bS[chromaIdx] - 1) + (tcOffsetDiv2 << 1));
        const int iTc = sps.getBitDepth(CHANNEL_TYPE_CHROMA) < 10 ? ((sm_tcTable[iIndexTC] + 2) >> (10 - sps.getBitDepth(CHANNEL_TYPE_CHROMA))) : ((sm_tcTable[iIndexTC]) << (sps.getBitDepth(CHANNEL_TYPE_CHROMA) - 10));
        bool useLongFilter = false;
        if (largeBoundary)//如果是大块,判断滤波
        {
    
    
        const int indexB = Clip3<int>(0, MAX_QP, iQP + (betaOffsetDiv2 << 1));
        const int beta = sm_betaTable[indexB] * iBitdepthScale;

#if JVET_P0081_CHROMA_LONG_DEBLOCKING_FIX
        const int dp0 = xCalcDP(piTmpSrcChroma + iSrcStep*(iIdx*uiLoopLength + 0), iOffset, isChromaHorCTBBoundary);
#else
        const int dp0 = xCalcDP(piTmpSrcChroma + iSrcStep*(iIdx*uiLoopLength + 0), iOffset);
#endif 
        const int dq0 = xCalcDQ(piTmpSrcChroma + iSrcStep*(iIdx*uiLoopLength + 0), iOffset);
        const int subSamplingShift = ( edgeDir == EDGE_VER ) ? m_shiftVer : m_shiftHor;
#if JVET_P0081_CHROMA_LONG_DEBLOCKING_FIX
        const int dp3 = (subSamplingShift == 1) ? xCalcDP(piTmpSrcChroma + iSrcStep*(iIdx*uiLoopLength + 1), iOffset, isChromaHorCTBBoundary) : xCalcDP(piTmpSrcChroma + iSrcStep*(iIdx*uiLoopLength + 3), iOffset, isChromaHorCTBBoundary);
#else
        const int dp3 = (subSamplingShift == 1) ? xCalcDP(piTmpSrcChroma + iSrcStep*(iIdx*uiLoopLength + 1), iOffset) : xCalcDP(piTmpSrcChroma + iSrcStep*(iIdx*uiLoopLength + 3), iOffset);
#endif
        const int dq3 = ( subSamplingShift == 1 ) ? xCalcDQ(piTmpSrcChroma + iSrcStep*(iIdx*uiLoopLength + 1), iOffset) : xCalcDQ(piTmpSrcChroma + iSrcStep*(iIdx*uiLoopLength + 3), iOffset);

        const int d0 = dp0 + dq0;
        const int d3 = dp3 + dq3;
        const int d = d0 + d3;

          if (d < beta)//d需要小于beta才可以滤波
          {
    
    
            useLongFilter = true;
#if JVET_P0081_CHROMA_LONG_DEBLOCKING_FIX
            const bool sw = xUseStrongFiltering(piTmpSrcChroma + iSrcStep*(iIdx*uiLoopLength + 0), iOffset, 2 * d0, beta, iTc, false, false, 7, 7, isChromaHorCTBBoundary)
#else
            const bool sw = xUseStrongFiltering(piTmpSrcChroma + iSrcStep*(iIdx*uiLoopLength + 0), iOffset, 2 * d0, beta, iTc)
#endif
#if JVET_P0081_CHROMA_LONG_DEBLOCKING_FIX
              && xUseStrongFiltering(piTmpSrcChroma + iSrcStep*(iIdx*uiLoopLength + ((subSamplingShift == 1) ? 1 : 3)), iOffset, 2 * d3, beta, iTc, false, false, 7, 7, isChromaHorCTBBoundary);
#else
              && xUseStrongFiltering(piTmpSrcChroma + iSrcStep*(iIdx*uiLoopLength + ((subSamplingShift == 1) ? 1 : 3)), iOffset, 2 * d3, beta, iTc);
#endif  

            for (unsigned step = 0; step < uiLoopLength; step++)
            {
    
    
#if JVET_P0081_CHROMA_LONG_DEBLOCKING_FIX
              xPelFilterChroma(piTmpSrcChroma + iSrcStep*(step + iIdx*uiLoopLength), iOffset, iTc, sw, bPartPNoFilter, bPartQNoFilter, clpRng, largeBoundary, isChromaHorCTBBoundary);
#else
              xPelFilterChroma(piTmpSrcChroma + iSrcStep*(step + iIdx*uiLoopLength), iOffset, iTc, sw, bPartPNoFilter, bPartQNoFilter, clpRng, largeBoundary);
#endif 
            }
          }
        }
        if ( !useLongFilter )//如果是普通的滤波
        {
    
    
          for (unsigned step = 0; step < uiLoopLength; step++)
          {
    
    
#if JVET_P0081_CHROMA_LONG_DEBLOCKING_FIX
            xPelFilterChroma(piTmpSrcChroma + iSrcStep*(step + iIdx*uiLoopLength), iOffset, iTc, false, bPartPNoFilter, bPartQNoFilter, clpRng, largeBoundary, isChromaHorCTBBoundary);
#else
            xPelFilterChroma(piTmpSrcChroma + iSrcStep*(step + iIdx*uiLoopLength), iOffset, iTc, false, bPartPNoFilter, bPartQNoFilter, clpRng, largeBoundary);
#endif
          }
        }
        }
      }
    }
  }
}



/**
 - Deblocking for the luminance component with strong or weak filter 对于亮度分量的进行去块滤波,(使用判断强弱)
 .
 \param piSrc           pointer to picture data
 \param iOffset         offset value for picture data
 \param tc              tc value
 \param sw              decision strong/weak filter
 \param bPartPNoFilter  indicator to disable filtering on partP
 \param bPartQNoFilter  indicator to disable filtering on partQ
 \param iThrCut         threshold value for weak filter decision
 \param bFilterSecondP  decision weak filter/no filter for partP
 \param bFilterSecondQ  decision weak filter/no filter for partQ
 \param bitDepthLuma    luma bit depth
*/
inline void LoopFilter::xBilinearFilter(Pel* srcP, Pel* srcQ, int offset, int refMiddle, int refP, int refQ, int numberPSide, int numberQSide, const int* dbCoeffsP, const int* dbCoeffsQ, int tc) const
{
    
    //亮度双线性滤波
    int src;
    const char tc7[7] = {
    
     6, 5, 4, 3, 2, 1, 1};
    const char tc3[3] = {
    
     6, 4, 2 };
    const char *tcP  = (numberPSide == 3) ? tc3 : tc7;
    const char *tcQ  = (numberQSide == 3) ? tc3 : tc7;
    for (int pos = 0; pos < numberPSide; pos++)
    {
    
    
      src = srcP[-offset*pos];
      int cvalue = (tc * tcP[pos]) >>1;
      srcP[-offset * pos] = Clip3(src - cvalue, src + cvalue, ((refMiddle*dbCoeffsP[pos] + refP * (64 - dbCoeffsP[pos]) + 32) >> 6));
    }
    for (int pos = 0; pos < numberQSide; pos++)
    {
    
    
      src = srcQ[offset*pos];
      int cvalue = (tc * tcQ[pos]) >> 1;
      srcQ[offset*pos] = Clip3(src - cvalue, src + cvalue, ((refMiddle*dbCoeffsQ[pos] + refQ * (64 - dbCoeffsQ[pos]) + 32) >> 6));
    }
}

inline void LoopFilter::xFilteringPandQ(Pel* src, int offset, int numberPSide, int numberQSide, int tc) const//大块强滤波函数
{
    
    //看2002的那个表格
  CHECK(numberPSide <= 3 && numberQSide <= 3, "Short filtering in long filtering function");
  Pel* srcP = src-offset;
  Pel* srcQ = src;

  int refP = 0;
  int refQ = 0;
  int refMiddle = 0;

  const int dbCoeffs7[7] = {
    
     59, 50, 41,32,23,14,5 };
  const int dbCoeffs3[3] = {
    
     53, 32, 11 };
  const int dbCoeffs5[5] = {
    
     58, 45, 32,19,6};
  const int* dbCoeffsP   = numberPSide == 7 ? dbCoeffs7 : (numberPSide==5) ? dbCoeffs5 : dbCoeffs3;
  const int* dbCoeffsQ   = numberQSide == 7 ? dbCoeffs7 : (numberQSide==5) ? dbCoeffs5 : dbCoeffs3;

  switch (numberPSide)//对应2002的那个表格
  {
    
    
    case 7: refP = (srcP[-6*offset]   + srcP[-7 * offset] + 1) >> 1; break;
    case 3: refP = (srcP[-2 * offset] + srcP[-3 * offset] + 1) >> 1; break;
    case 5: refP = (srcP[-4 * offset] + srcP[-5 * offset] + 1) >> 1; break;
  }

  switch (numberQSide)//对应2002的那个表格
  {
    
    
    case 7: refQ = (srcQ[6 * offset] + srcQ[7 * offset] + 1) >> 1; break;
    case 3: refQ = (srcQ[2 * offset] + srcQ[3 * offset] + 1) >> 1; break;
    case 5: refQ = (srcQ[4 * offset] + srcQ[5 * offset] + 1) >> 1; break;
  }
  /* 如果P和Q相等 */
  if (numberPSide == numberQSide)
  {
    
    
    if (numberPSide == 5)
    {
    
    
      refMiddle = (2 * (srcP[0] + srcQ[0] + srcP[-offset] + srcQ[offset] + srcP[-2 * offset] + srcQ[2 * offset]) + srcP[-3 * offset] + srcQ[3 * offset] + srcP[-4 * offset] + srcQ[4 * offset] + 8) >> 4;
    }
    else
    {
    
    
      refMiddle = (2 * (srcP[0] + srcQ[0]) + srcP[-offset] + srcQ[offset] + srcP[-2 * offset] + srcQ[2 * offset] + srcP[-3 * offset] + srcQ[3 * offset] + srcP[-4 * offset] + srcQ[4 * offset] + srcP[-5 * offset] + srcQ[5 * offset] + +srcP[-6 * offset] + srcQ[6 * offset] + 8) >> 4;
    }
  }
  /* 如果P和Q不相等 */
  else
  {
    
    
    Pel* srcPt = srcP;
    Pel* srcQt = srcQ;
    int offsetP = -offset;
    int offsetQ = offset;

    int newNumberQSide = numberQSide;
    int newNumberPSide = numberPSide;
    if (numberQSide > numberPSide)
    {
    
    
      std::swap(srcPt, srcQt);
      std::swap(offsetP, offsetQ);
      newNumberQSide = numberPSide;
      newNumberPSide = numberQSide;
    }

    if (newNumberPSide == 7 && newNumberQSide == 5)
    {
    
    
      refMiddle = (2 * (srcP[0] + srcQ[0] + srcP[-offset] + srcQ[offset]) + srcP[-2 * offset] + srcQ[2 * offset] + srcP[-3 * offset] + srcQ[3 * offset] + srcP[-4 * offset] + srcQ[4 * offset] + srcP[-5 * offset] + srcQ[5 * offset] + 8) >> 4;
    }
    else if (newNumberPSide == 7 && newNumberQSide == 3)
    {
    
    
      refMiddle = (2 * (srcPt[0] + srcQt[0]) + srcQt[0] + 2 * (srcQt[offsetQ] + srcQt[2 * offsetQ]) + srcPt[offsetP] + srcQt[offsetQ] + srcPt[2 * offsetP] + srcPt[3 * offsetP] + srcPt[4 * offsetP] + srcPt[5 * offsetP] + srcPt[6 * offsetP] + 8) >> 4;
    }
    else //if (newNumberPSide == 5 && newNumberQSide == 3)
    {
    
    
      refMiddle = (srcP[0] + srcQ[0] + srcP[-offset] + srcQ[offset] + srcP[-2 * offset] + srcQ[2 * offset] + srcP[-3 * offset] + srcQ[3 * offset] + 4) >> 3;
    }
  }
  xBilinearFilter(srcP,srcQ,offset,refMiddle,refP,refQ,numberPSide,numberQSide,dbCoeffsP,dbCoeffsQ,tc);//亮度的双线性强滤波
}

inline void LoopFilter::xPelFilterLuma(Pel* piSrc, const int iOffset, const int tc, const bool sw, const bool bPartPNoFilter, const bool bPartQNoFilter, const int iThrCut, const bool bFilterSecondP, const bool bFilterSecondQ, const ClpRng& clpRng, bool sidePisLarge, bool sideQisLarge, int maxFilterLengthP, int maxFilterLengthQ) const
{
    
    
	/******** 初始化像素位置、像素值 *******/
  int delta;

  const Pel m4  = piSrc[ 0          ];
  const Pel m3  = piSrc[-iOffset    ];
  const Pel m5  = piSrc[ iOffset    ];
  const Pel m2  = piSrc[-iOffset * 2];
  const Pel m6  = piSrc[ iOffset * 2];
  const Pel m1  = piSrc[-iOffset * 3];
  const Pel m7  = piSrc[ iOffset * 3];
  const Pel m0  = piSrc[-iOffset * 4];

  const Pel mP1 = piSrc[-iOffset * 5];
  const Pel mP2 = piSrc[-iOffset * 6];
  const Pel mP3 = piSrc[-iOffset * 7];
  const Pel m8  = piSrc[ iOffset * 4];
  const Pel m9  = piSrc[ iOffset * 5];
  const Pel m10 = piSrc[ iOffset * 6];
  const char tc3[3] = {
    
     3, 2, 1};

  /******** 滤波强弱选择,并且进行滤波 *******/
  if (sw)//如果是强滤波
  {
    
    
    if (sidePisLarge || sideQisLarge)//如果是大块,那么进行强滤波
    {
    
    
      xFilteringPandQ(piSrc, iOffset, sidePisLarge ? maxFilterLengthP : 3, sideQisLarge ? maxFilterLengthQ : 3, tc);
    }
    else//否则,只进行普通的DBF:计算方法可以画一下,这里先跳过了
    {
    
    
      piSrc[-iOffset]     = Clip3(m3 - tc3[0] * tc, m3 + tc3[0] * tc, ((m1 + 2 * m2 + 2 * m3 + 2 * m4 + m5 + 4) >> 3));
      piSrc[0]            = Clip3(m4 - tc3[0] * tc, m4 + tc3[0] * tc, ((m2 + 2 * m3 + 2 * m4 + 2 * m5 + m6 + 4) >> 3));
      piSrc[-iOffset * 2] = Clip3(m2 - tc3[1] * tc, m2 + tc3[1] * tc, ((m1 + m2 + m3 + m4 + 2) >> 2));
      piSrc[iOffset]      = Clip3(m5 - tc3[1] * tc, m5 + tc3[1] * tc, ((m3 + m4 + m5 + m6 + 2) >> 2));
      piSrc[-iOffset * 3] = Clip3(m1 - tc3[2] * tc, m1 + tc3[2] * tc, ((2 * m0 + 3 * m1 + m2 + m3 + m4 + 4) >> 3));
      piSrc[iOffset * 2]  = Clip3(m6 - tc3[2] * tc, m6 + tc3[2] * tc, ((m3 + m4 + m5 + 3 * m6 + 2 * m7 + 4) >> 3));
    }
  }
  else
  {
    
    
    /* Weak filter */
    delta = ( 9 * ( m4 - m3 ) - 3 * ( m5 - m2 ) + 8 ) >> 4;//计算出delta值

    if ( abs(delta) < iThrCut )//如果这个derta值达到门限了
    {
    
    
      delta = Clip3( -tc, tc, delta );
      piSrc[-iOffset] = ClipPel( m3 + delta, clpRng);
      piSrc[0]        = ClipPel( m4 - delta, clpRng);

      const int tc2 = tc >> 1;
      if( bFilterSecondP )
      {
    
    
        const int delta1 = Clip3( -tc2, tc2, ( ( ( ( m1 + m3 + 1 ) >> 1 ) - m2 + delta ) >> 1 ) );
        piSrc[-iOffset * 2] = ClipPel( m2 + delta1, clpRng);
      }
      if( bFilterSecondQ )
      {
    
    
        const int delta2 = Clip3( -tc2, tc2, ( ( ( ( m6 + m4 + 1 ) >> 1 ) - m5 - delta ) >> 1 ) );
        piSrc[iOffset] = ClipPel( m5 + delta2, clpRng);
      }
    }
  }

  if(bPartPNoFilter)//如果不滤波,改回
  {
    
    
    piSrc[-iOffset    ] = m3;
    piSrc[-iOffset * 2] = m2;
    piSrc[-iOffset * 3] = m1;
    if (sidePisLarge)
    {
    
    
      piSrc[-iOffset * 4] = m0;
      piSrc[-iOffset * 5] = mP1;
      piSrc[-iOffset * 6] = mP2;
      piSrc[-iOffset * 7] = mP3;
    }
  }

  if(bPartQNoFilter)//如果不滤波,改回
  {
    
    
    piSrc[ 0          ] = m4;
    piSrc[ iOffset    ] = m5;
    piSrc[ iOffset * 2] = m6;
    if (sideQisLarge)
    {
    
    
      piSrc[iOffset * 3] = m7;
      piSrc[iOffset * 4] = m8;
      piSrc[iOffset * 5] = m9;
      piSrc[iOffset * 6] = m10;
    }
  }
}

/**
 - Deblocking of one line/column for the chrominance component  对于色度分量,一行/列的去块滤波
 .
 \param piSrc           pointer to picture data
 \param iOffset         offset value for picture data
 \param tc              tc value
 \param bPartPNoFilter  indicator to disable filtering on partP
 \param bPartQNoFilter  indicator to disable filtering on partQ
 \param bitDepthChroma  chroma bit depth
 */
#if JVET_P0081_CHROMA_LONG_DEBLOCKING_FIX
inline void LoopFilter::xPelFilterChroma(Pel* piSrc, const int iOffset, const int tc, const bool sw, const bool bPartPNoFilter, const bool bPartQNoFilter, const ClpRng& clpRng, const bool largeBoundary, const bool isChromaHorCTBBoundary) const
#else
inline void LoopFilter::xPelFilterChroma(Pel* piSrc, const int iOffset, const int tc, const bool sw, const bool bPartPNoFilter, const bool bPartQNoFilter, const ClpRng& clpRng, const bool largeBoundary) const
#endif
{
    
    
	/******************** 初始化像素位置和像素值 ******************/
  int delta;

  const Pel m0 = piSrc[-iOffset * 4];
  const Pel m1 = piSrc[-iOffset * 3];
  const Pel m2 = piSrc[-iOffset * 2];
  const Pel m3 = piSrc[-iOffset];
  const Pel m4 = piSrc[0];
  const Pel m5 = piSrc[iOffset];
  const Pel m6 = piSrc[iOffset * 2];
  const Pel m7 = piSrc[iOffset * 3];

  if (sw)//如果是强滤波:滤波过程后面画画
  {
    
    
#if JVET_P0081_CHROMA_LONG_DEBLOCKING_FIX
    if (isChromaHorCTBBoundary)
    {
    
    
      piSrc[-iOffset * 1] = Clip3(m3 - tc, m3 + tc, ((3 * m2 + 2 * m3 + m4 + m5 + m6 + 4) >> 3)); // p0
      piSrc[0] = Clip3(m4 - tc, m4 + tc, ((2 * m2 + m3 + 2 * m4 + m5 + m6 + m7 + 4) >> 3)); // q0
      piSrc[iOffset * 1] = Clip3(m5 - tc, m5 + tc, ((m2 + m3 + m4 + 2 * m5 + m6 + 2 * m7 + 4) >> 3));  // q1
      piSrc[iOffset * 2] = Clip3(m6 - tc, m6 + tc, ((m3 + m4 + m5 + 2 * m6 + 3 * m7 + 4) >> 3));       // q2
    }
    else
    {
    
    
      piSrc[-iOffset * 3] = Clip3(m1 - tc, m1 + tc, ((3 * m0 + 2 * m1 + m2 + m3 + m4 + 4) >> 3));       // p2
      piSrc[-iOffset * 2] = Clip3(m2 - tc, m2 + tc, ((2 * m0 + m1 + 2 * m2 + m3 + m4 + m5 + 4) >> 3));  // p1
      piSrc[-iOffset * 1] = Clip3(m3 - tc, m3 + tc, ((m0 + m1 + m2 + 2 * m3 + m4 + m5 + m6 + 4) >> 3)); // p0
      piSrc[0] = Clip3(m4 - tc, m4 + tc, ((m1 + m2 + m3 + 2 * m4 + m5 + m6 + m7 + 4) >> 3)); // q0
      piSrc[iOffset * 1] = Clip3(m5 - tc, m5 + tc, ((m2 + m3 + m4 + 2 * m5 + m6 + 2 * m7 + 4) >> 3));  // q1
      piSrc[iOffset * 2] = Clip3(m6 - tc, m6 + tc, ((m3 + m4 + m5 + 2 * m6 + 3 * m7 + 4) >> 3));       // q2
    }
#else
    piSrc[-iOffset * 3] = Clip3(m1 - tc, m1 + tc, ((3 * m0 + 2 * m1 + m2 + m3 + m4 + 4) >> 3));       // p2
    piSrc[-iOffset * 2] = Clip3(m2 - tc, m2 + tc, ((2 * m0 + m1 + 2 * m2 + m3 + m4 + m5 + 4) >> 3));  // p1
    piSrc[-iOffset * 1] = Clip3(m3 - tc, m3 + tc, ((m0 + m1 + m2 + 2 * m3 + m4 + m5 + m6 + 4) >> 3)); // p0
    piSrc[0] = Clip3(m4 - tc, m4 + tc, ((m1 + m2 + m3 + 2 * m4 + m5 + m6 + m7 + 4) >> 3)); // q0
    piSrc[iOffset * 1] = Clip3(m5 - tc, m5 + tc, ((m2 + m3 + m4 + 2 * m5 + m6 + 2 * m7 + 4) >> 3));  // q1
    piSrc[iOffset * 2] = Clip3(m6 - tc, m6 + tc, ((m3 + m4 + m5 + 2 * m6 + 3 * m7 + 4) >> 3));       // q2
#endif
  }
  else//弱滤波
  {
    
    
      delta = Clip3(-tc, tc, ((((m4 - m3) << 2) + m2 - m5 + 4) >> 3));
      piSrc[-iOffset] = ClipPel(m3 + delta, clpRng);
      piSrc[0] = ClipPel(m4 - delta, clpRng);
  }


  if( bPartPNoFilter )//如果不滤波,改回
  {
    
    
    if (largeBoundary)
    {
    
    
      piSrc[-iOffset * 3] = m1; // p2
      piSrc[-iOffset * 2] = m2; // p1
    }
    piSrc[-iOffset] = m3;
  }
  if( bPartQNoFilter )//如果不滤波,改回
  {
    
    
    if (largeBoundary)
    {
    
    
      piSrc[iOffset * 1] = m5; // q1
      piSrc[iOffset * 2] = m6; // q2
    }
    piSrc[ 0      ] = m4;
  }
}

/**
 - Decision between strong and weak filter 决定使用何种滤波强度的滤波器
 .
 \param offset         offset value for picture data
 \param d               d value
 \param beta            beta value
 \param tc              tc value
 \param piSrc           pointer to picture data
 */
#if JVET_P0081_CHROMA_LONG_DEBLOCKING_FIX
inline bool LoopFilter::xUseStrongFiltering(Pel* piSrc, const int iOffset, const int d, const int beta, const int tc, bool sidePisLarge, bool sideQisLarge, int maxFilterLengthP, int maxFilterLengthQ, bool isChromaHorCTBBoundary) const
#else
inline bool LoopFilter::xUseStrongFiltering(Pel* piSrc, const int iOffset, const int d, const int beta, const int tc, bool sidePisLarge, bool sideQisLarge, int maxFilterLengthP, int maxFilterLengthQ) const
#endif
{
    
    
  const Pel m4 = piSrc[ 0          ];		//
  const Pel m3 = piSrc[-iOffset    ];		//          **             **   **             ** 
  const Pel m7 = piSrc[ iOffset * 3];		//          m0   m1   m2   m3   m4   m5   m6   m7      
  const Pel m0 = piSrc[-iOffset * 4];		//                              |
#if JVET_P0081_CHROMA_LONG_DEBLOCKING_FIX	//                    **    起始位置[0]
  const Pel m2 = piSrc[-iOffset * 2];		//
  int       sp3 = abs(m0 - m3);//最左边的位置减去左侧边缘
  if (isChromaHorCTBBoundary)//如果是色度CTU边缘,则看相邻边缘
  {
    
    
    sp3 = abs(m2 - m3);
  }
#else
  int       sp3 = abs(m0 - m3);
#endif
  int       sq3      = abs(m7 - m4);//最有边的位置减去右侧边缘
  const int d_strong = sp3 + sq3;//两者的差距

  if (sidePisLarge || sideQisLarge)//对于大块,进一步增加长度
  {
    
    
    Pel mP4;
    Pel m11;
    if (maxFilterLengthP == 5)//如果长度是5,引入到-6,即m[-2]
    {
    
    
      mP4 = piSrc[-iOffset * 6];
    }
    else
    {
    
    
      mP4 = piSrc[-iOffset * 8];//如果长度更大,引入到-8,即m[-4]
    }
    if (maxFilterLengthQ == 5)
    {
    
    
      m11 = piSrc[iOffset * 5];//如果长度是5,引入到5,即m[9]
    }
    else
    {
    
    
      m11 = piSrc[iOffset * 7];//如果长度更大,引入到7,即m[11]
    }

    if (sidePisLarge)//如果P是大块:用最左边的m[-4]或m[-2]减去左块右边缘
    {
    
    
      sp3 = (sp3 + abs(m0 - mP4) + 1) >> 1;
    }
    if (sideQisLarge)//如果Q是大块:用最右边的m[11]或m[9]减去右块左边缘
    {
    
    
      sq3 = (sq3 + abs(m11 - m7) + 1) >> 1;
    }
    return ((sp3 + sq3) < (beta*3 >> 5)) && (d < (beta >> 2)) && (abs(m3 - m4) < ((tc * 5 + 1) >> 1));//大块滤波方式
  }
  else
  return ( ( d_strong < ( beta >> 3 ) ) && ( d < ( beta >> 2 ) ) && ( abs( m3 - m4 ) < ( ( tc * 5 + 1 ) >> 1 ) ) );//小块滤波方式
}

#if JVET_P0081_CHROMA_LONG_DEBLOCKING_FIX
inline int LoopFilter::xCalcDP(Pel* piSrc, const int iOffset, const bool isChromaHorCTBBoundary) const
#else
inline int LoopFilter::xCalcDP(Pel* piSrc, const int iOffset) const
#endif
{
    
    
#if JVET_P0081_CHROMA_LONG_DEBLOCKING_FIX
  if (isChromaHorCTBBoundary)//如果是色度CTU边界:则左块最右边的两个相邻像素使用-1:1滤波
  {
    
    
    return abs(piSrc[-iOffset * 2] - 2 * piSrc[-iOffset * 2] + piSrc[-iOffset]);
  }
  else//一般情况使用:则隔左块最右边的三个相邻像素使用1:-2:1滤波
  {
    
    
    return abs(piSrc[-iOffset * 3] - 2 * piSrc[-iOffset * 2] + piSrc[-iOffset]);
  }
#else
  return abs(piSrc[-iOffset * 3] - 2 * piSrc[-iOffset * 2] + piSrc[-iOffset]);
#endif 
}

inline int LoopFilter::xCalcDQ( Pel* piSrc, const int iOffset ) const//使用则右块最左边的三个相邻像素使用1:-2:1滤波
{
    
    
  return abs( piSrc[0] - 2 * piSrc[iOffset] + piSrc[iOffset * 2] );
}

inline unsigned LoopFilter::BsSet(unsigned val, const ComponentID compIdx) const {
    
     return (val << (compIdx << 1)); }
inline unsigned LoopFilter::BsGet(unsigned val, const ComponentID compIdx) const {
    
     return ((val >> (compIdx << 1)) & 3); }

//! \}

猜你喜欢

转载自blog.csdn.net/weixin_42979679/article/details/103079076