/* 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-2011, 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     TEncLoopFilter.cpp
 \brief    estimation part of loop filter class
 */

#include "TEncLoopFilter.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>

//=============================================================================================================
// Constants
//=============================================================================================================
#define   EDGE_VER    0
#define   EDGE_HOR    1

#define   QpUV(iQpY)  ( g_aucChromaScale[ max( min( (iQpY), MAX_QP ), MIN_QP ) ] )


#define F143 1
#if F143
#define DEFAULT_INTRA_TC_OFFSET 2 ///< Default intra TC offset
#else
#define DEFAULT_INTRA_TC_OFFSET 4 ///< Default intra TC offset
#endif

const UChar tctable_8x8[56] =
{
  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,5,5,6,6,7,8,9,9,10,10,11,11,12,12,13,13,14,14
};

const UChar betatable_8x8[52] =
{
  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
};

//=============================================================================================================
// Constructor / destructor
//=============================================================================================================
#if PANASONIC_ADAPTIVE_DEBLOCKING
TEncLoopFilter::TEncLoopFilter()
{
  m_pcEntropyCoder = NULL;
  m_pcPicYuvTmp    = NULL;  
}

//=============================================================================================================
// Public member functions
//=============================================================================================================

/**
 \param pcPic           picture (TComPic) pointer
 \param pcEntropyCoder  entropy coder class
 */

Void TEncLoopFilter::startAlphaEnc( TComPic* pcPic, TEncEntropy* pcEntropyCoder )
{
  Int iWidth  = pcPic->getPicYuvOrg()->getWidth();
  Int iHeight = pcPic->getPicYuvOrg()->getHeight();

  m_pcEntropyCoder = pcEntropyCoder;
  
  m_pcPicYuvTmp = new TComPicYuv();
  m_pcPicYuvTmp->create(iWidth, iHeight, g_uiMaxCUWidth, g_uiMaxCUHeight, g_uiMaxCUDepth);
}

Void TEncLoopFilter::endAlphaEnc()
{
  m_pcPicYuvTmp->destroy();   
  delete m_pcPicYuvTmp;
  m_pcPicYuvTmp = NULL; 
   
  m_pcEntropyCoder = NULL;
}

/**
 \param  pcAlfParam          ALF parameter
 \param  dLambda             lambda value for RD cost computation
 \retval ruiDist             distortion
 \retval ruiBits             required bits
 \retval ruiMaxAlfCtrlDepth  optimal partition depth
 */

Void TEncLoopFilter::AlphaFilterProcess( TComPic* pcPic, Double dLambdaLuma )
{ 
  if (m_uiDisableDeblockingFilterIdc == 1)
  {	
    return; 
  }   

  m_dLambdaLuma   = dLambdaLuma;

#if !PANASONIC_ADAPTIVE_DEBLOCKING_SUBJECTIVE_ESTIMATION
  TComPicYuv* pcPicOrg         = pcPic->getPicYuvOrg();
#endif   
  TComPicYuv* pcPicYuvRec      = pcPic->getPicYuvRec();
  TComPicYuv* pcPicYuvRec_save = pcPicYuvRec;

#if !PANASONIC_ADAPTIVE_DEBLOCKING_SUBJECTIVE_ESTIMATION
  UInt64  uiMinRate = MAX_INT;
  UInt64  uiMinDist = MAX_INT;  
  Double  dMinCost  = MAX_DOUBLE;

  UInt64  uiOrigRate;
  UInt64  uiOrigDist;  
  Double  dOrigCost;
#endif

  pcPicYuvRec->copyToPic(m_pcPicYuvTmp);  //Save orginal Rec
   
  for (Int j=0;j<6;j++)
  {
    m_sumAlphaZ_strong[j]=0.0;
    m_sumAlphaN_strong[j]=0.0;
  }
  for (Int j=0;j<4;j++)
  {
    m_sumAlphaZ_weak[j]=0.0;
    m_sumAlphaN_weak[j]=0.0;
  }
   
  for (Int direction=EDGE_VER;direction<=EDGE_HOR;direction++)
  {
    for ( UInt uiCUAddr = 0; uiCUAddr < pcPic->getNumCUsInFrame(); uiCUAddr++ )
    {
      TComDataCU* pcCU = pcPic->getCU( uiCUAddr );
      ::memset( m_aapucBS       [direction], 0, sizeof( UChar ) * m_uiNumPartitions );
      for( Int iPlane = 0; iPlane < 3; iPlane++ )
      {	    
        ::memset( m_aapbEdgeFilter[direction][iPlane], 0, sizeof( bool  ) * m_uiNumPartitions );
      }
      xDeblockCU_estimate( pcCU, 0, 0, direction);
    }
  }
   


  if ( (m_sumAlphaN_strong[0] + m_sumAlphaN_strong[1])!=0.0 )
  {
    if ((m_sumAlphaZ_strong[0] + m_sumAlphaZ_strong[1])/(m_sumAlphaN_strong[0] + m_sumAlphaN_strong[1])*8.0 >1.33)
    {
      pcPic->getSlice(0)->setQAlpha( (Int)( 1.33/8.0 * AD_QUANT+0.5), 0, 0);
    }
    else
    {	  
      pcPic->getSlice(0)->setQAlpha( (Int)( (m_sumAlphaZ_strong[0] + m_sumAlphaZ_strong[1])/(m_sumAlphaN_strong[0] + m_sumAlphaN_strong[1]) * AD_QUANT+0.5), 0, 0);
    }     
  }
  else
  {
    pcPic->getSlice(0)->setQAlpha( (Int)(0.125 * AD_QUANT+0.5), 0, 0);
  }

  if ( (m_sumAlphaN_strong[2]+ m_sumAlphaN_strong[3])!=0.0 )
  {
    if ((m_sumAlphaZ_strong[2] + m_sumAlphaZ_strong[3])/(m_sumAlphaN_strong[2] + m_sumAlphaN_strong[3])*4.0 > 1.40)
    {
      pcPic->getSlice(0)->setQAlpha( (Int)( 1.40/4.0 * AD_QUANT+0.5), 0, 1);	  
    }
    else
    {	  
      pcPic->getSlice(0)->setQAlpha( (Int)( (m_sumAlphaZ_strong[2] + m_sumAlphaZ_strong[3])/(m_sumAlphaN_strong[2] + m_sumAlphaN_strong[3]) * AD_QUANT+0.5), 0, 1);
    }     
  }
  else
  {
    pcPic->getSlice(0)->setQAlpha( (Int)(0.25 * AD_QUANT+0.5), 0, 1);
  }

  if ( (m_sumAlphaN_strong[4] + m_sumAlphaN_strong[5])!=0.0 )
  {
    if ((m_sumAlphaZ_strong[4] + m_sumAlphaZ_strong[5])/(m_sumAlphaN_strong[4] + m_sumAlphaN_strong[5])*8.0 > 1.55)
    {
      pcPic->getSlice(0)->setQAlpha( (Int)( 1.55/8.0 * AD_QUANT+0.5), 0, 2);       
    }
    else
    {
      pcPic->getSlice(0)->setQAlpha( (Int)( (m_sumAlphaZ_strong[4] + m_sumAlphaZ_strong[5])/(m_sumAlphaN_strong[4] + m_sumAlphaN_strong[5]) * AD_QUANT+0.5), 0, 2);	  
    }     
  }
  else
  {
    pcPic->getSlice(0)->setQAlpha( (Int)(0.125 * AD_QUANT+0.5), 0, 2);
  }

  if ( (m_sumAlphaN_weak[0] + m_sumAlphaN_weak[1])!=0.0 )
  {
    if ((m_sumAlphaZ_weak[0] + m_sumAlphaZ_weak[1])/(m_sumAlphaN_weak[0] + m_sumAlphaN_weak[1]) > 1.20)
    {
      pcPic->getSlice(0)->setQAlpha( (Int)( 1.20 * AD_QUANT+0.5), 1, 0);	  
    }
    else
    {
      pcPic->getSlice(0)->setQAlpha( (Int)( (m_sumAlphaZ_weak[0] + m_sumAlphaZ_weak[1])/(m_sumAlphaN_weak[0] + m_sumAlphaN_weak[1]) * AD_QUANT+0.5), 1, 0);	  
    }    
  }
  else
  {
    pcPic->getSlice(0)->setQAlpha( (Int)(1.0 * AD_QUANT+0.5), 1, 0);
  }

  if ( (m_sumAlphaN_weak[2] + m_sumAlphaN_weak[3])!=0.0 )
  {
    if ((m_sumAlphaZ_weak[2] + m_sumAlphaZ_weak[3])/(m_sumAlphaN_weak[2] + m_sumAlphaN_weak[3]) > 1.40)
    {
      pcPic->getSlice(0)->setQAlpha( (Int)( 1.40 * AD_QUANT+0.5), 1, 1);
    }
    else
    {
      pcPic->getSlice(0)->setQAlpha( (Int)( (m_sumAlphaZ_weak[2] + m_sumAlphaZ_weak[3])/(m_sumAlphaN_weak[2] + m_sumAlphaN_weak[3]) * AD_QUANT+0.5), 1, 1);
    }       
  }
  else
  {
    pcPic->getSlice(0)->setQAlpha( (Int)(1.0 * AD_QUANT+0.5), 1, 1);
  }   

  pcPic->getSlice(0)->setAlphaFlag(0);
   
#if !PANASONIC_ADAPTIVE_DEBLOCKING_SUBJECTIVE_ESTIMATION
  xCalcRDCost( pcPicOrg, pcPicYuvRec, uiOrigRate, uiOrigDist, dOrigCost, pcPic );
#endif
   
  pcPic->setPicYuvRec(m_pcPicYuvTmp);             //reset Rec
  pcPicYuvRec         = m_pcPicYuvTmp;

  pcPic->getSlice(0)->setAlphaFlag(1);
  loopFilterPic( pcPic );

#if !PANASONIC_ADAPTIVE_DEBLOCKING_SUBJECTIVE_ESTIMATION
  xCalcRDCost( pcPicOrg, pcPicYuvRec, uiMinRate, uiMinDist, dMinCost, pcPic );
#endif
   
  pcPicYuvRec = pcPicYuvRec_save;                  //reset pointer
  pcPic->setPicYuvRec(pcPicYuvRec);
   
#if !PANASONIC_ADAPTIVE_DEBLOCKING_SUBJECTIVE_ESTIMATION
  if( dOrigCost < dMinCost )
  {
    pcPic->getSlice(0)->setAlphaFlag (0);
  }
  else
#endif     
  {
    m_pcPicYuvTmp->copyToPicLuma(pcPic->getPicYuvRec());
  }
 
}

//=============================================================================================================
// Protected member functions
//=============================================================================================================

//rdCost
Void TEncLoopFilter::xCalcRDCost(TComPicYuv* pcPicOrg, TComPicYuv* pcPicCmp, UInt64& ruiRate, UInt64& ruiDist, Double& rdCost, TComPic* pcPic )
{ 

   if( pcPic->getSlice(0)->getAlphaFlag() )
   {
      m_pcEntropyCoder->resetEntropy();
      m_pcEntropyCoder->resetBits();

      m_pcEntropyCoder->encodeAlphas( pcPic );

      ruiRate = 1 + m_pcEntropyCoder->getNumberOfWrittenBits();
   }
   else
   {
      ruiRate = 1;
   }
   
   ruiDist     = xCalcSSD(pcPicOrg->getLumaAddr(), pcPicCmp->getLumaAddr(), pcPicOrg->getWidth(), pcPicOrg->getHeight(), pcPicOrg->getStride());
   rdCost      = (Double)(ruiRate) * m_dLambdaLuma + (Double)(ruiDist);
}

UInt64 TEncLoopFilter::xCalcSSD(Pel* pOrg, Pel* pCmp, Int iWidth, Int iHeight, Int iStride )
{
  UInt64 uiSSD = 0;
  Int x, y;
  
  UInt uiShift = g_uiBitIncrement<<1;
  Int iTemp = 0;
  
  for( y = 0; y < iHeight; y++ )
  {
    for( x = 0; x < iWidth; x++ )
    {
      iTemp = pOrg[x] - pCmp[x]; 
      uiSSD += ( iTemp * iTemp ) >> uiShift;
    }
   pOrg += iStride;
   pCmp += iStride;
  }
  
  return uiSSD;;
}



// estimation
Void TEncLoopFilter::xDeblockCU_estimate( TComDataCU* pcCU, UInt uiAbsZorderIdx, UInt uiDepth, Int Edge )
{
  TComPic* pcPic     = pcCU->getPic();
  UInt uiCurNumParts = pcPic->getNumPartInCU() >> (uiDepth<<1);
  UInt uiQNumParts   = uiCurNumParts>>2;
   
  if( pcCU->getDepth(uiAbsZorderIdx) > uiDepth )
  {	
    for ( UInt uiPartIdx = 0; uiPartIdx < 4; uiPartIdx++, uiAbsZorderIdx+=uiQNumParts )
    {	     
      UInt uiLPelX   = pcCU->getCUPelX() + g_auiRasterToPelX[ g_auiZscanToRaster[uiAbsZorderIdx] ];
      UInt uiTPelY   = pcCU->getCUPelY() + g_auiRasterToPelY[ g_auiZscanToRaster[uiAbsZorderIdx] ];
      if( ( uiLPelX < pcCU->getSlice()->getSPS()->getWidth() ) && ( uiTPelY < pcCU->getSlice()->getSPS()->getHeight() ) )
      {		  
        xDeblockCU_estimate( pcCU, uiAbsZorderIdx, uiDepth+1, Edge );
      }
    }
    return;
  }

  xSetLoopfilterParam( pcCU, uiAbsZorderIdx );
   
#if NSQT
  xSetEdgefilterTU   ( pcCU, g_auiZscanToRaster[ uiAbsZorderIdx ], uiAbsZorderIdx, uiDepth );
#else
  xSetEdgefilterTU   ( pcCU, uiAbsZorderIdx, uiDepth );
#endif
  xSetEdgefilterPU   ( pcCU, uiAbsZorderIdx );

  Int iDir = Edge;
  {	
    for( UInt uiPartIdx = uiAbsZorderIdx; uiPartIdx < uiAbsZorderIdx + uiCurNumParts; uiPartIdx++ )
    {	     
#if BS_DISABLE_INSIDE_8x8
      UInt uiBSCheck = ((iDir == EDGE_VER && uiPartIdx%2 == 0) || (iDir == EDGE_HOR && (uiPartIdx-((uiPartIdx>>2)<<2))/2 == 0));
      if ( m_aapbEdgeFilter[iDir][0][uiPartIdx] && uiBSCheck )
#else
      if ( m_aapbEdgeFilter[iDir][0][uiPartIdx] )
#endif
      {		    
        xGetBoundaryStrengthSingle ( pcCU, uiAbsZorderIdx, iDir, uiPartIdx );
      }
    }
  }

  UInt uiPelsInPart = g_uiMaxCUWidth >> g_uiMaxCUDepth;
  UInt PartIdxIncr = DEBLOCK_SMALLEST_BLOCK / uiPelsInPart ? DEBLOCK_SMALLEST_BLOCK / uiPelsInPart : 1 ;
   
  UInt uiSizeInPU = pcPic->getNumPartInWidth()>>(uiDepth);   
   
  for ( UInt iEdge = 0; iEdge < uiSizeInPU ; iEdge+=PartIdxIncr)
  {	
    xEdgeFilterLuma_estimate     ( pcCU, uiAbsZorderIdx, uiDepth, iDir, iEdge );
     
    if ( (iEdge % ( (DEBLOCK_SMALLEST_BLOCK<<1)/uiPelsInPart ) ) == 0 )
    {	     
      xEdgeFilterChroma   ( pcCU, uiAbsZorderIdx, uiDepth, iDir, iEdge );
    }
  }
}     



Void TEncLoopFilter::xEdgeFilterLuma_estimate( TComDataCU* pcCU, UInt uiAbsZorderIdx, UInt uiDepth, Int iDir, Int iEdge)
{  
  TComPicYuv* pcPicYuvRec = pcCU->getPic()->getPicYuvRec();
  Pel* piSrc    = pcPicYuvRec->getLumaAddr( pcCU->getAddr(), uiAbsZorderIdx );
  Pel* piTmpSrc = piSrc;
   
  TComPicYuv* pcPicYuvOrg = pcCU->getPic()->getPicYuvOrg();
  Pel* piOrg    = pcPicYuvOrg->getLumaAddr( pcCU->getAddr(), uiAbsZorderIdx );
  Pel* piTmpOrg = piOrg;
   
  Int  iStride = pcPicYuvRec->getStride();

  Int iQP = 0;
  Int iQP_P = 0;
  Int iQP_Q = 0;
   
  UInt uiNumParts = pcCU->getPic()->getNumPartInWidth()>>uiDepth;
   
  UInt  uiPelsInPart = g_uiMaxCUWidth >> g_uiMaxCUDepth;
  UInt  PartIdxIncr = DEBLOCK_SMALLEST_BLOCK / uiPelsInPart ? DEBLOCK_SMALLEST_BLOCK / uiPelsInPart : 1;
  UInt  uiBlocksInPart = uiPelsInPart / DEBLOCK_SMALLEST_BLOCK ? uiPelsInPart / DEBLOCK_SMALLEST_BLOCK : 1;

  UInt  uiBsAbsIdx = 0, uiBs = 0;
  Int   iOffset, iSrcStep;

  Bool  bPCMFilter = (pcCU->getSlice()->getSPS()->getPCMFilterDisableFlag() && ((1<<pcCU->getSlice()->getSPS()->getPCMLog2MinSize()) <= g_uiMaxCUWidth))? true : false;
   
  Bool  bPartPNoFilter = false;
  Bool  bPartQNoFilter = false;

  UInt  uiPartPIdx = 0;
  UInt  uiPartQIdx = 0;
  TComDataCU* pcCUP = pcCU;
  TComDataCU* pcCUQ = pcCU;
   
  if (iDir == EDGE_VER)
  {	
    iOffset = 1;
    iSrcStep = iStride;
    piTmpSrc += iEdge*uiPelsInPart;
    piTmpOrg += iEdge*uiPelsInPart;
  }
  else  // (iDir == EDGE_HOR)
  {	
    iOffset = iStride;
    iSrcStep = 1;
    piTmpSrc += iEdge*uiPelsInPart*iStride;
    piTmpOrg += iEdge*uiPelsInPart*iStride;
  }
   
  for ( UInt iIdx = 0; iIdx < uiNumParts; iIdx+=PartIdxIncr )
  {
    uiBs = 0;
    for (UInt iIdxInside = 0; iIdxInside<PartIdxIncr; iIdxInside++)
    {	     
      uiBsAbsIdx = xCalcBsIdx( pcCU, uiAbsZorderIdx, iDir, iEdge, iIdx+iIdxInside);
      if (uiBs < m_aapucBS[iDir][uiBsAbsIdx])
      {
        uiBs = m_aapucBS[iDir][uiBsAbsIdx];
      }
    }
	
    if ( uiBs )
    {
      iQP_Q = pcCU->getQP( uiBsAbsIdx );
      uiPartQIdx = uiBsAbsIdx;
      if (iDir == EDGE_VER)
      {	    
        pcCUP = pcCUQ->getPULeft (uiPartPIdx, uiPartQIdx);
      }
      else  // (iDir == EDGE_HOR)
      {    
        pcCUP = pcCUQ->getPUAbove(uiPartPIdx, uiPartQIdx);
      }       
      iQP_P = pcCUP->getQP(uiPartPIdx);
       
      if(pcCU->getIPCMFlag(uiPartQIdx))
      {	    
        iQP_Q = 0;
      }       
      if(pcCUP->getIPCMFlag(uiPartPIdx))
      {	    
        iQP_P = 0;
      }
      iQP = (iQP_P + iQP_Q + 1) >> 1;
       
      Int iBitdepthScale = (1<<(g_uiBitIncrement+g_uiBitDepth-8));
#if G174_DF_OFFSET
      Int iIndexTC = Clip3(0, MAX_QP+DEFAULT_INTRA_TC_OFFSET, Int(iQP + DEFAULT_INTRA_TC_OFFSET*(uiBs-1) + (m_iTcOffset_div2 << 1)));
#else
      Int iIndexTC = Clip3(0, MAX_QP+DEFAULT_INTRA_TC_OFFSET, Int(iQP + DEFAULT_INTRA_TC_OFFSET*(uiBs-1)));
#endif
#if G174_DF_OFFSET
      Int iIndexB = Clip3(0, MAX_QP, iQP + (m_iBetaOffset_div2 << 1));
#else
      Int iIndexB = Clip3(0, MAX_QP, iQP );
#endif
       
      Int iTc =  tctable_8x8[iIndexTC]*iBitdepthScale;
      Int iBeta = betatable_8x8[iIndexB]*iBitdepthScale;
      Int iSideThreshold = (iBeta+(iBeta>>1))>>3;
      Int iThrCut = iTc*10;
       

      for (UInt iBlkIdx = 0; iBlkIdx< uiBlocksInPart; iBlkIdx ++)
      {
        Int dp0 = xCalcDP( piTmpSrc+iSrcStep*(iIdx*uiPelsInPart+iBlkIdx*DEBLOCK_SMALLEST_BLOCK+0), iOffset);
        Int dq0 = xCalcDQ( piTmpSrc+iSrcStep*(iIdx*uiPelsInPart+iBlkIdx*DEBLOCK_SMALLEST_BLOCK+0), iOffset);
        Int dp3 = xCalcDP( piTmpSrc+iSrcStep*(iIdx*uiPelsInPart+iBlkIdx*DEBLOCK_SMALLEST_BLOCK+3), iOffset);
        Int dq3 = xCalcDQ( piTmpSrc+iSrcStep*(iIdx*uiPelsInPart+iBlkIdx*DEBLOCK_SMALLEST_BLOCK+3), iOffset);
        Int d0 = dp0 + dq0;
        Int d3 = dp3 + dq3;
	 
        Int dp4 = xCalcDP( piTmpSrc+iSrcStep*(iIdx*uiPelsInPart+iBlkIdx*DEBLOCK_SMALLEST_BLOCK+4), iOffset);
        Int dq4 = xCalcDQ( piTmpSrc+iSrcStep*(iIdx*uiPelsInPart+iBlkIdx*DEBLOCK_SMALLEST_BLOCK+4), iOffset);
        Int dp7 = xCalcDP( piTmpSrc+iSrcStep*(iIdx*uiPelsInPart+iBlkIdx*DEBLOCK_SMALLEST_BLOCK+7), iOffset);
        Int dq7 = xCalcDQ( piTmpSrc+iSrcStep*(iIdx*uiPelsInPart+iBlkIdx*DEBLOCK_SMALLEST_BLOCK+7), iOffset);
        Int d4 = dp4 + dq4;
        Int d7 = dp7 + dq7;
	 
        if (bPCMFilter)
        {
          // Check if each of PUs is I_PCM
          bPartPNoFilter = (pcCUP->getIPCMFlag(uiPartPIdx));
          bPartQNoFilter = (pcCUQ->getIPCMFlag(uiPartQIdx));
	}
	 
	if (d0+d3 < iBeta)
	{	      
          Bool bFilterP = (dp0+dp3 < iSideThreshold);
          Bool bFilterQ = (dq0+dq3 < iSideThreshold);
	      
          Bool sw =  xUseStrongFiltering( iOffset, 2*d0, iBeta, iTc , piTmpSrc+iSrcStep*(iIdx*uiPelsInPart+iBlkIdx*DEBLOCK_SMALLEST_BLOCK+0))
                  && xUseStrongFiltering( iOffset, 2*d3, iBeta, iTc , piTmpSrc+iSrcStep*(iIdx*uiPelsInPart+iBlkIdx*DEBLOCK_SMALLEST_BLOCK+3));

	  for ( Int i = 0; i < DEBLOCK_SMALLEST_BLOCK/2; i++)
	  {		   
	    xCalcAlphaLuma( piTmpSrc+iSrcStep*(iIdx*uiPelsInPart+iBlkIdx*DEBLOCK_SMALLEST_BLOCK+i), piTmpOrg+iSrcStep*(iIdx*uiPelsInPart+iBlkIdx*DEBLOCK_SMALLEST_BLOCK+i), iOffset, d0+d3, iBeta, iTc, sw, bPartPNoFilter, bPartQNoFilter, iThrCut, bFilterP, bFilterQ);
	  }
	}
        if (d4+d7 < iBeta)
        {	      
          Bool bFilterP = (dp4+dp7 < iSideThreshold);
          Bool bFilterQ = (dq4+dq7 < iSideThreshold);
	      

          Bool sw =  xUseStrongFiltering( iOffset, 2*d4, iBeta, iTc , piTmpSrc+iSrcStep*(iIdx*uiPelsInPart+iBlkIdx*DEBLOCK_SMALLEST_BLOCK+4))
                  && xUseStrongFiltering( iOffset, 2*d7, iBeta, iTc , piTmpSrc+iSrcStep*(iIdx*uiPelsInPart+iBlkIdx*DEBLOCK_SMALLEST_BLOCK+7));

          for ( Int i = DEBLOCK_SMALLEST_BLOCK/2; i < DEBLOCK_SMALLEST_BLOCK; i++)
          {		   
            xCalcAlphaLuma( piTmpSrc+iSrcStep*(iIdx*uiPelsInPart+iBlkIdx*DEBLOCK_SMALLEST_BLOCK+i), piTmpOrg+iSrcStep*(iIdx*uiPelsInPart+iBlkIdx*DEBLOCK_SMALLEST_BLOCK+i), iOffset, d4+d7, iBeta, iTc, sw, bPartPNoFilter, bPartQNoFilter, iThrCut, bFilterP, bFilterQ);
          }
        }
      }       
    }     
  }   
}

__inline Void TEncLoopFilter::xCalcAlphaLuma( Pel* piSrc, Pel* piOrg, Int iOffset, Int d, Int beta, Int tc , Bool sw, Bool bPartPNoFilter, Bool bPartQNoFilter, Int iThrCut, Bool bFilterSecondP, Bool bFilterSecondQ)
{  
  Int delta;
  
  Int alpha1_8=(Int)(0.125 * AD_QUANT+0.5);
  Int alpha1_4=(Int)(0.250 * AD_QUANT+0.5);
  Int alpha1_1=(Int)(1.000 * AD_QUANT+0.5);

  Int AD_QUANT_H = AD_QUANT/2;
  Int idelta;
  Int iqerror;

  Pel m4_org  = piOrg[0];
  Pel m3_org  = piOrg[-iOffset];
  Pel m5_org  = piOrg[ iOffset];
  Pel m2_org  = piOrg[-iOffset*2];
  Pel m6_org  = piOrg[ iOffset*2];
  Pel m1_org  = piOrg[-iOffset*3];
   
  Pel m4  = piSrc[0];
  Pel m3  = piSrc[-iOffset];
  Pel m5  = piSrc[ iOffset];
  Pel m2  = piSrc[-iOffset*2];
  Pel m6  = piSrc[ iOffset*2];
  Pel m1  = piSrc[-iOffset*3];
  Pel m7  = piSrc[ iOffset*3];
  Pel m0  = piSrc[-iOffset*4];
  
#if PANASONIC_ADAPTIVE_DEBLOCKING_SUBJECTIVE_ESTIMATION
  Pel m0_org  = piOrg[-iOffset*4];
  Pel m7_org  = piOrg[ iOffset*3];

  Double org_c4= (m0_org + m1_org + m2_org + m3_org - m4_org - m5_org - m6_org - m7_org);   
  Double c4    = (m0 + m1 + m2 + m3 - m4 - m5 - m6 - m7);
  Double weight=(c4*c4-1.2*org_c4*org_c4)>0.0 ? 1.0:0.0;
#else
  Double weight=1.0;
#endif
   
  if (sw)
  {
    idelta = (m1 + 2*m2 - 6*m3 + 2*m4 + m5);
    piSrc[ -iOffset ] = Clip(((m3<<AD_QUANT_BITS) + idelta*alpha1_8 + AD_QUANT_H)>>AD_QUANT_BITS); //execute
    iqerror = m3_org-m3;    
    m_sumAlphaZ_strong[0]+=(Double)(idelta*iqerror)*weight;
    m_sumAlphaN_strong[0]+=(Double)(idelta*idelta)*weight;
    
    idelta = (m2 + 2*m3 - 6*m4 + 2*m5 + m6);
    piSrc[         0] = Clip(((m4<<AD_QUANT_BITS) + idelta*alpha1_8 + AD_QUANT_H)>>AD_QUANT_BITS);
    iqerror = m4_org-m4;
    m_sumAlphaZ_strong[1]+=(Double)(idelta*iqerror)*weight;
    m_sumAlphaN_strong[1]+=(Double)(idelta*idelta)*weight;
  
    idelta = (m1 - 3*m2 + m3 + m4);
    piSrc[-iOffset*2] = Clip(((m2<<AD_QUANT_BITS) + idelta*alpha1_4 + AD_QUANT_H)>>AD_QUANT_BITS);
    iqerror = m2_org-m2;
    m_sumAlphaZ_strong[2]+=(Double)(idelta*iqerror);
    m_sumAlphaN_strong[2]+=(Double)(idelta*idelta);
    
    idelta = (m3 + m4 - 3*m5 + m6);
    piSrc[   iOffset] = Clip(((m5<<AD_QUANT_BITS) + idelta*alpha1_4 + AD_QUANT_H)>>AD_QUANT_BITS);
    iqerror = m5_org-m5;
    m_sumAlphaZ_strong[3]+=(Double)(idelta*iqerror);
    m_sumAlphaN_strong[3]+=(Double)(idelta*idelta);
  
    idelta = (2*m0 - 5*m1 + m2 + m3 + m4);
    piSrc[-iOffset*3] = Clip(((m1<<AD_QUANT_BITS) + idelta*alpha1_8 + AD_QUANT_H)>>AD_QUANT_BITS);
    iqerror = m1_org-m1;
    m_sumAlphaZ_strong[4]+=(Double)(idelta*iqerror);
    m_sumAlphaN_strong[4]+=(Double)(idelta*idelta);
    
    idelta = (m3 + m4 + m5 - 5*m6 + 2*m7);
    piSrc[ iOffset*2] = Clip(((m6<<AD_QUANT_BITS) + idelta*alpha1_8 + AD_QUANT_H)>>AD_QUANT_BITS);
    iqerror = m6_org-m6;
    m_sumAlphaZ_strong[5]+=(Double)(idelta*iqerror);
    m_sumAlphaN_strong[5]+=(Double)(idelta*idelta);
  }
  else //weak filtering
  { 
    delta = (9*(m4-m3) -3*(m5-m2) + 8)>>4;

    if ( abs(delta) < iThrCut )
    {
      delta = Clip3(-tc, tc, delta);  

      idelta = delta;
      iqerror = m3_org-m3;
      m_sumAlphaZ_weak[0]+=(Double)(idelta*iqerror)*weight;
      m_sumAlphaN_weak[0]+=(Double)(idelta*idelta)*weight;

      idelta = -delta;
      iqerror = m4_org-m4;
      m_sumAlphaZ_weak[1]+=(Double)(idelta*iqerror)*weight;
      m_sumAlphaN_weak[1]+=(Double)(idelta*idelta)*weight;

      Int tc2 = tc>>1;
      if(bFilterSecondP)
      {
        Int delta1 = Clip3(-tc2, tc2, (( ((m1+m3+1)>>1)- m2+delta)>>1));
        idelta = delta1;
        iqerror = m2_org-m2;
        m_sumAlphaZ_weak[2]+=(Double)(idelta*iqerror);
        m_sumAlphaN_weak[2]+=(Double)(idelta*idelta);
      }
      if(bFilterSecondQ)
      {
        Int delta2 = Clip3(-tc2, tc2, (( ((m6+m4+1)>>1)- m5-delta)>>1));
        idelta = delta2;
        iqerror = m5_org-m5;
        m_sumAlphaZ_weak[3]+=(Double)(idelta*iqerror);
        m_sumAlphaN_weak[3]+=(Double)(idelta*idelta);
      }
    }
//Execute
    delta = (9*(m4-m3) -3*(m5-m2) + 8)>>4;

    if ( abs(delta) < iThrCut )
    {
      delta = Clip3(-tc, tc, delta);    
      piSrc[-iOffset] = Clip(((m3<<AD_QUANT_BITS) + delta*alpha1_1 + AD_QUANT_H)>>AD_QUANT_BITS);
      piSrc[       0] = Clip(((m4<<AD_QUANT_BITS) - delta*alpha1_1 + AD_QUANT_H)>>AD_QUANT_BITS);

      Int tc2 = tc>>1;
      if(bFilterSecondP)
      {
        Int delta1 = Clip3(-tc2, tc2, (( ((m1+m3+1)>>1)- m2+delta)>>1));
        piSrc[-iOffset*2] = Clip(((m2<<AD_QUANT_BITS) + delta1*alpha1_1 + AD_QUANT_H)>>AD_QUANT_BITS);
      }
      if(bFilterSecondQ)
      {
        Int delta2 = Clip3(-tc2, tc2, (( ((m6+m4+1)>>1)- m5-delta)>>1));
        piSrc[ iOffset  ] = Clip(((m5<<AD_QUANT_BITS) + delta2*alpha1_1 + AD_QUANT_H)>>AD_QUANT_BITS);
      }
    }
  }

  if(bPartPNoFilter)
  {
    piSrc[-iOffset] = m3;
    piSrc[-iOffset*2] = m2;
    piSrc[-iOffset*3] = m1;
  }
  if(bPartQNoFilter)
  {
    piSrc[0] = m4;
    piSrc[ iOffset] = m5;
    piSrc[ iOffset*2] = m6;
  }
}

__inline Int TComLoopFilter::xCalcDP( Pel* piSrc, Int iOffset)
{
  return abs( piSrc[-iOffset*3] - 2*piSrc[-iOffset*2] + piSrc[-iOffset] ) ;
}
__inline Int TComLoopFilter::xCalcDQ( Pel* piSrc, Int iOffset)
{
  return abs( piSrc[0] - 2*piSrc[iOffset] + piSrc[iOffset*2] );
}

__inline Bool TComLoopFilter::xUseStrongFiltering( Int offset, Int d, Int beta, Int tc, Pel* piSrc)
{   
  Pel m4  = piSrc[0];
  Pel m3  = piSrc[-offset];
  Pel m7  = piSrc[ offset*3];
  Pel m0  = piSrc[-offset*4];
   
  Int d_strong = abs(m0-m3) + abs(m7-m4);
   
  return ( (d_strong < (beta>>3)) && (d<(beta>>2)) && ( abs(m3-m4) < ((tc*5+1)>>1)) );
}




#endif
