/* 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-2012, 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     TComYuv.cpp
    \brief    general YUV buffer class
    \todo     this should be merged with TComPicYuv
*/

#include <stdlib.h>
#include <memory.h>
#include <assert.h>
#include <math.h>

#include "CommonDef.h"
#include "TComYuv.h"
#include "TComInterpolationFilter.h"

//! \ingroup TLibCommon
//! \{

TComYuv::TComYuv()
{
  m_apiBufY = NULL;
  m_apiBufU = NULL;
  m_apiBufV = NULL;
}

TComYuv::~TComYuv()
{
}

Void TComYuv::create( UInt iWidth, UInt iHeight )
{
  // memory allocation
  m_apiBufY  = (Pel*)xMalloc( Pel, iWidth*iHeight    );
  m_apiBufU  = (Pel*)xMalloc( Pel, iWidth*iHeight >> 2 );
  m_apiBufV  = (Pel*)xMalloc( Pel, iWidth*iHeight >> 2 );
  
  // set width and height
  m_iWidth   = iWidth;
  m_iHeight  = iHeight;
  m_iCWidth  = iWidth  >> 1;
  m_iCHeight = iHeight >> 1;
}

Void TComYuv::destroy()
{
  // memory free
  xFree( m_apiBufY ); m_apiBufY = NULL;
  xFree( m_apiBufU ); m_apiBufU = NULL;
  xFree( m_apiBufV ); m_apiBufV = NULL;
}

Void TComYuv::clear()
{
  ::memset( m_apiBufY, 0, ( m_iWidth  * m_iHeight  )*sizeof(Pel) );
  ::memset( m_apiBufU, 0, ( m_iCWidth * m_iCHeight )*sizeof(Pel) );
  ::memset( m_apiBufV, 0, ( m_iCWidth * m_iCHeight )*sizeof(Pel) );
}

Void TComYuv::copyToPicYuv   ( TComPicYuv* pcPicYuvDst, UInt iCuAddr, UInt uiAbsZorderIdx, UInt uiPartDepth, UInt uiPartIdx )
{
  copyToPicLuma  ( pcPicYuvDst, iCuAddr, uiAbsZorderIdx, uiPartDepth, uiPartIdx );
  copyToPicChroma( pcPicYuvDst, iCuAddr, uiAbsZorderIdx, uiPartDepth, uiPartIdx );
}

Void TComYuv::copyToPicLuma  ( TComPicYuv* pcPicYuvDst, UInt iCuAddr, UInt uiAbsZorderIdx, UInt uiPartDepth, UInt uiPartIdx )
{
  Int  y, iWidth, iHeight;
  iWidth  = m_iWidth >>uiPartDepth;
  iHeight = m_iHeight>>uiPartDepth;
  
  Pel* pSrc     = getLumaAddr(uiPartIdx, iWidth);
  Pel* pDst     = pcPicYuvDst->getLumaAddr ( iCuAddr, uiAbsZorderIdx );
  
  UInt  iSrcStride  = getStride();
  UInt  iDstStride  = pcPicYuvDst->getStride();
  
  for ( y = iHeight; y != 0; y-- )
  {
    ::memcpy( pDst, pSrc, sizeof(Pel)*iWidth);
    pDst += iDstStride;
    pSrc += iSrcStride;
  }
}

Void TComYuv::copyToPicChroma( TComPicYuv* pcPicYuvDst, UInt iCuAddr, UInt uiAbsZorderIdx, UInt uiPartDepth, UInt uiPartIdx )
{
  Int  y, iWidth, iHeight;
  iWidth  = m_iCWidth >>uiPartDepth;
  iHeight = m_iCHeight>>uiPartDepth;
  
  Pel* pSrcU      = getCbAddr(uiPartIdx, iWidth);
  Pel* pSrcV      = getCrAddr(uiPartIdx, iWidth);
  Pel* pDstU      = pcPicYuvDst->getCbAddr( iCuAddr, uiAbsZorderIdx );
  Pel* pDstV      = pcPicYuvDst->getCrAddr( iCuAddr, uiAbsZorderIdx );
  
  UInt  iSrcStride = getCStride();
  UInt  iDstStride = pcPicYuvDst->getCStride();
  for ( y = iHeight; y != 0; y-- )
  {
    ::memcpy( pDstU, pSrcU, sizeof(Pel)*(iWidth) );
    ::memcpy( pDstV, pSrcV, sizeof(Pel)*(iWidth) );
    pSrcU += iSrcStride;
    pSrcV += iSrcStride;
    pDstU += iDstStride;
    pDstV += iDstStride;
  }
}

Void TComYuv::copyFromPicYuv   ( TComPicYuv* pcPicYuvSrc, UInt iCuAddr, UInt uiAbsZorderIdx )
{
  copyFromPicLuma  ( pcPicYuvSrc, iCuAddr, uiAbsZorderIdx );
  copyFromPicChroma( pcPicYuvSrc, iCuAddr, uiAbsZorderIdx );
}

Void TComYuv::copyFromPicLuma  ( TComPicYuv* pcPicYuvSrc, UInt iCuAddr, UInt uiAbsZorderIdx )
{
  Int  y;
  
  Pel* pDst     = m_apiBufY;
  Pel* pSrc     = pcPicYuvSrc->getLumaAddr ( iCuAddr, uiAbsZorderIdx );
  
  UInt  iDstStride  = getStride();
  UInt  iSrcStride  = pcPicYuvSrc->getStride();
  for ( y = m_iHeight; y != 0; y-- )
  {
    ::memcpy( pDst, pSrc, sizeof(Pel)*m_iWidth);
    pDst += iDstStride;
    pSrc += iSrcStride;
  }
}

Void TComYuv::copyFromPicChroma( TComPicYuv* pcPicYuvSrc, UInt iCuAddr, UInt uiAbsZorderIdx )
{
  Int  y;
  
  Pel* pDstU      = m_apiBufU;
  Pel* pDstV      = m_apiBufV;
  Pel* pSrcU      = pcPicYuvSrc->getCbAddr( iCuAddr, uiAbsZorderIdx );
  Pel* pSrcV      = pcPicYuvSrc->getCrAddr( iCuAddr, uiAbsZorderIdx );
  
  UInt  iDstStride = getCStride();
  UInt  iSrcStride = pcPicYuvSrc->getCStride();
  for ( y = m_iCHeight; y != 0; y-- )
  {
    ::memcpy( pDstU, pSrcU, sizeof(Pel)*(m_iCWidth) );
    ::memcpy( pDstV, pSrcV, sizeof(Pel)*(m_iCWidth) );
    pSrcU += iSrcStride;
    pSrcV += iSrcStride;
    pDstU += iDstStride;
    pDstV += iDstStride;
  }
}

Void TComYuv::copyToPartYuv( TComYuv* pcYuvDst, UInt uiDstPartIdx )
{
  copyToPartLuma  ( pcYuvDst, uiDstPartIdx );
  copyToPartChroma( pcYuvDst, uiDstPartIdx );
}

Void TComYuv::copyToPartLuma( TComYuv* pcYuvDst, UInt uiDstPartIdx )
{
  Int  y;
  
  Pel* pSrc     = m_apiBufY;
  Pel* pDst     = pcYuvDst->getLumaAddr( uiDstPartIdx );
  
  UInt  iSrcStride  = getStride();
  UInt  iDstStride  = pcYuvDst->getStride();
  for ( y = m_iHeight; y != 0; y-- )
  {
    ::memcpy( pDst, pSrc, sizeof(Pel)*m_iWidth);
    pDst += iDstStride;
    pSrc += iSrcStride;
  }
}

Void TComYuv::copyToPartChroma( TComYuv* pcYuvDst, UInt uiDstPartIdx )
{
  Int  y;
  
  Pel* pSrcU      = m_apiBufU;
  Pel* pSrcV      = m_apiBufV;
  Pel* pDstU      = pcYuvDst->getCbAddr( uiDstPartIdx );
  Pel* pDstV      = pcYuvDst->getCrAddr( uiDstPartIdx );
  
  UInt  iSrcStride = getCStride();
  UInt  iDstStride = pcYuvDst->getCStride();
  for ( y = m_iCHeight; y != 0; y-- )
  {
    ::memcpy( pDstU, pSrcU, sizeof(Pel)*(m_iCWidth) );
    ::memcpy( pDstV, pSrcV, sizeof(Pel)*(m_iCWidth) );
    pSrcU += iSrcStride;
    pSrcV += iSrcStride;
    pDstU += iDstStride;
    pDstV += iDstStride;
  }
}

Void TComYuv::copyPartToYuv( TComYuv* pcYuvDst, UInt uiSrcPartIdx )
{
  copyPartToLuma  ( pcYuvDst, uiSrcPartIdx );
  copyPartToChroma( pcYuvDst, uiSrcPartIdx );
}

Void TComYuv::copyPartToLuma( TComYuv* pcYuvDst, UInt uiSrcPartIdx )
{
  Int  y;
  
  Pel* pSrc     = getLumaAddr(uiSrcPartIdx);
  Pel* pDst     = pcYuvDst->getLumaAddr( 0 );
  
  UInt  iSrcStride  = getStride();
  UInt  iDstStride  = pcYuvDst->getStride();
  
  UInt uiHeight = pcYuvDst->getHeight();
  UInt uiWidth = pcYuvDst->getWidth();
  
  for ( y = uiHeight; y != 0; y-- )
  {
    ::memcpy( pDst, pSrc, sizeof(Pel)*uiWidth);
    pDst += iDstStride;
    pSrc += iSrcStride;
  }
}

Void TComYuv::copyPartToChroma( TComYuv* pcYuvDst, UInt uiSrcPartIdx )
{
  Int  y;
  
  Pel* pSrcU      = getCbAddr( uiSrcPartIdx );
  Pel* pSrcV      = getCrAddr( uiSrcPartIdx );
  Pel* pDstU      = pcYuvDst->getCbAddr( 0 );
  Pel* pDstV      = pcYuvDst->getCrAddr( 0 );
  
  UInt  iSrcStride = getCStride();
  UInt  iDstStride = pcYuvDst->getCStride();
  
  UInt uiCHeight = pcYuvDst->getCHeight();
  UInt uiCWidth = pcYuvDst->getCWidth();
  
  for ( y = uiCHeight; y != 0; y-- )
  {
    ::memcpy( pDstU, pSrcU, sizeof(Pel)*(uiCWidth) );
    ::memcpy( pDstV, pSrcV, sizeof(Pel)*(uiCWidth) );
    pSrcU += iSrcStride;
    pSrcV += iSrcStride;
    pDstU += iDstStride;
    pDstV += iDstStride;
  }
}

#if DEPTH_MAP_GENERATION
Void TComYuv::copyPartToPartYuvPdm   ( TComYuv* pcYuvDst, UInt uiPartIdx, UInt iWidth, UInt iHeight, UInt uiSubSampExpX, UInt uiSubSampExpY )
{
  copyPartToPartLumaPdm   (pcYuvDst, uiPartIdx, iWidth, iHeight, uiSubSampExpX, uiSubSampExpY );
}
#endif

Void TComYuv::copyPartToPartYuv   ( TComYuv* pcYuvDst, UInt uiPartIdx, UInt iWidth, UInt iHeight )
{
  copyPartToPartLuma   (pcYuvDst, uiPartIdx, iWidth, iHeight );
  copyPartToPartChroma (pcYuvDst, uiPartIdx, iWidth>>1, iHeight>>1 );
}

#if DEPTH_MAP_GENERATION
Void TComYuv::copyPartToPartLumaPdm  ( TComYuv* pcYuvDst, UInt uiPartIdx, UInt iWidth, UInt iHeight, UInt uiSubSampExpX, UInt uiSubSampExpY )
{
  UInt uiBlkX = g_auiRasterToPelX[ g_auiZscanToRaster[ uiPartIdx ] ] >> uiSubSampExpX;
  UInt uiBlkY = g_auiRasterToPelY[ g_auiZscanToRaster[ uiPartIdx ] ] >> uiSubSampExpY;
  Pel* pSrc   = getLumaAddr(uiPartIdx);
  Pel* pDst   = pcYuvDst->getLumaAddr() + uiBlkY * pcYuvDst->getStride() + uiBlkX;

  if( pSrc == pDst )
  {
    //th not a good idea
    //th best would be to fix the caller 
    return ;
  }

  UInt  iSrcStride = getStride();
  UInt  iDstStride = pcYuvDst->getStride();
  for ( UInt y = iHeight; y != 0; y-- )
  {
    ::memcpy( pDst, pSrc, iWidth * sizeof(Pel) );
    pSrc += iSrcStride;
    pDst += iDstStride;
  }
}
#endif

Void TComYuv::copyPartToPartLuma  ( TComYuv* pcYuvDst, UInt uiPartIdx, UInt iWidth, UInt iHeight )
{
  Pel* pSrc =           getLumaAddr(uiPartIdx);
  Pel* pDst = pcYuvDst->getLumaAddr(uiPartIdx);
  if( pSrc == pDst )
  {
    //th not a good idea
    //th best would be to fix the caller 
    return ;
  }
  
  UInt  iSrcStride = getStride();
  UInt  iDstStride = pcYuvDst->getStride();
  for ( UInt y = iHeight; y != 0; y-- )
  {
    ::memcpy( pDst, pSrc, iWidth * sizeof(Pel) );
    pSrc += iSrcStride;
    pDst += iDstStride;
  }
}

Void TComYuv::copyPartToPartChroma( TComYuv* pcYuvDst, UInt uiPartIdx, UInt iWidth, UInt iHeight )
{
  Pel*  pSrcU =           getCbAddr(uiPartIdx);
  Pel*  pSrcV =           getCrAddr(uiPartIdx);
  Pel*  pDstU = pcYuvDst->getCbAddr(uiPartIdx);
  Pel*  pDstV = pcYuvDst->getCrAddr(uiPartIdx);
  
  if( pSrcU == pDstU && pSrcV == pDstV)
  {
    //th not a good idea
    //th best would be to fix the caller 
    return ;
  }
  
  UInt   iSrcStride = getCStride();
  UInt   iDstStride = pcYuvDst->getCStride();
  for ( UInt y = iHeight; y != 0; y-- )
  {
    ::memcpy( pDstU, pSrcU, iWidth * sizeof(Pel) );
    ::memcpy( pDstV, pSrcV, iWidth * sizeof(Pel) );
    pSrcU += iSrcStride;
    pSrcV += iSrcStride;
    pDstU += iDstStride;
    pDstV += iDstStride;
  }
}

Void TComYuv::addClipPartLuma( TComYuv* pcYuvSrc0, TComYuv* pcYuvSrc1, UInt uiTrUnitIdx, UInt uiPartSize )
{
  Int x, y;

  Pel* pSrc0 = pcYuvSrc0->getLumaAddr( uiTrUnitIdx);
  Pel* pSrc1 = pcYuvSrc1->getLumaAddr( uiTrUnitIdx);
  Pel* pDst  = getLumaAddr( uiTrUnitIdx);

  UInt iSrc0Stride = pcYuvSrc0->getStride();
  UInt iSrc1Stride = pcYuvSrc1->getStride();
  UInt iDstStride  = getStride();
  for ( y = uiPartSize-1; y >= 0; y-- )
  {
    for ( x = uiPartSize-1; x >= 0; x-- )
    {
      pDst[x] = Clip( pSrc0[x] + pSrc1[x] );      
    }
    pSrc0 += iSrc0Stride;
    pSrc1 += iSrc1Stride;
    pDst  += iDstStride;
  }
}

#if LG_RESTRICTEDRESPRED_M24766
Void
TComYuv::add(Int *iPUResiPredShift, PartSize uhPartitionSize,  TComYuv* pcYuvAdd, Int iWidth, Int iHeight, Bool bSubtract )
{
  addLuma   (iPUResiPredShift, uhPartitionSize, pcYuvAdd, iWidth,    iHeight,    bSubtract );
  addChroma (iPUResiPredShift, uhPartitionSize, pcYuvAdd, iWidth>>1, iHeight>>1, bSubtract );
}
#else
Void
TComYuv::add( TComYuv* pcYuvAdd, Int iWidth, Int iHeight, Bool bSubtract )
{
  addLuma   ( pcYuvAdd, iWidth,    iHeight,    bSubtract );
  addChroma ( pcYuvAdd, iWidth>>1, iHeight>>1, bSubtract );
}
#endif

#if LG_RESTRICTEDRESPRED_M24766
Void
TComYuv::getPUXYOffset(PartSize uhPartitionSize, Int iWidth, Int iHeight, Int &iXOffset, Int &iYOffset)
{
  switch(uhPartitionSize)
  {    
  case SIZE_2NxN:
    iXOffset = iWidth;      iYOffset = iHeight >> 1;   break;
  case SIZE_2NxnU:
    iXOffset = iWidth;      iYOffset = iHeight >> 2;   break;
  case SIZE_2NxnD: 
    iXOffset = iWidth;      iYOffset = (iHeight >> 1) + (iHeight >> 2);   break;
  case SIZE_Nx2N:
    iXOffset = iWidth >> 1; iYOffset = iHeight; break;
  case SIZE_nLx2N:
    iXOffset = iWidth >> 2; iYOffset = iHeight; break;
  case SIZE_nRx2N:
    iXOffset = (iWidth >> 1) + (iWidth >> 2); iYOffset = iHeight; break;
  case SIZE_NxN:
    iXOffset = iWidth >> 1; iYOffset = iHeight >> 1;  break;
  default:
    assert(uhPartitionSize == SIZE_2Nx2N);
    iXOffset = iWidth;     iYOffset = iHeight;    break;
  }
}
#endif

#if LG_RESTRICTEDRESPRED_M24766
Void 
TComYuv::addLuma(Int *iPUResiPredShift, PartSize uhPartitionSize, TComYuv* pcYuvAdd, Int iWidth, Int iHeight, Bool bSubtract )
#else
Void
TComYuv::addLuma( TComYuv* pcYuvAdd, Int iWidth, Int iHeight, Bool bSubtract )
#endif
{
  Int   iScale      = ( bSubtract ? -1 : 1 );
  Int   iAddStride  = pcYuvAdd->getStride();
  Int   iDstStride  = getStride();
  Pel*  pAddSamples = pcYuvAdd->getLumaAddr();
  Pel*  pDstSamples = getLumaAddr();

#if LG_RESTRICTEDRESPRED_M24766
  Int iXOffset, iYOffset;

  getPUXYOffset(uhPartitionSize, iWidth, iHeight, iXOffset, iYOffset);

  for( Int iY = 0; iY < iYOffset; iY++, pDstSamples += iDstStride, pAddSamples += iAddStride )
  {
    if(iPUResiPredShift[0] >= 0)
    {
      for( Int iX = 0; iX < iXOffset; iX++ )
      {
        pDstSamples[iX] += iScale * (pAddSamples[iX] >> iPUResiPredShift[0]);
      }
    }

    if(iPUResiPredShift[1] >= 0)
    {
      for( Int iX = iXOffset; iX < iWidth; iX++ )
      {
        pDstSamples[iX] += iScale * (pAddSamples[iX] >> iPUResiPredShift[1]);
      }
    }
  }

  for( Int iY = iYOffset; iY < iHeight; iY++, pDstSamples += iDstStride, pAddSamples += iAddStride )
  {
    if(iPUResiPredShift[2] >= 0)
    {
      for( Int iX = 0; iX < iXOffset; iX++ )
      {
        pDstSamples[iX] += iScale * (pAddSamples[iX] >> iPUResiPredShift[2]);
      }
    }

    if(iPUResiPredShift[3] >= 0)
    {
      for( Int iX = iXOffset; iX < iWidth; iX++ )
      {
        pDstSamples[iX] += iScale * (pAddSamples[iX] >> iPUResiPredShift[3]);
      }
    }
  }
#else
  for( Int iY = 0; iY < iHeight; iY++, pDstSamples += iDstStride, pAddSamples += iAddStride )
  {
    for( Int iX = 0; iX < iWidth; iX++ )
    {
      pDstSamples[iX] += iScale * pAddSamples[iX];
    }
  }
#endif
}

#if LG_RESTRICTEDRESPRED_M24766
Void 
TComYuv::addChroma(Int *iPUResiPredShift, PartSize uhPartitionSize, TComYuv* pcYuvAdd, Int iWidth, Int iHeight, Bool bSubtract )
#else
Void
TComYuv::addChroma( TComYuv* pcYuvAdd, Int iWidth, Int iHeight, Bool bSubtract )
#endif
{
  Int   iScale        = ( bSubtract ? -1 : 1 );
  Int   iAddStride    = pcYuvAdd->getCStride();
  Int   iDstStride    = getCStride();
  Pel*  pAddSamplesCb = pcYuvAdd->getCbAddr();
  Pel*  pAddSamplesCr = pcYuvAdd->getCrAddr();
  Pel*  pDstSamplesCb = getCbAddr();
  Pel*  pDstSamplesCr = getCrAddr();

#if LG_RESTRICTEDRESPRED_M24766
  Int iXOffset, iYOffset;
 
  getPUXYOffset(uhPartitionSize, iWidth, iHeight, iXOffset, iYOffset);

  for( Int iY = 0; iY < iYOffset; iY++, pDstSamplesCb += iDstStride, pAddSamplesCb += iAddStride,
    pDstSamplesCr += iDstStride, pAddSamplesCr += iAddStride  )
  {
    if(iPUResiPredShift[0] >= 0)
    {
      for( Int iX = 0; iX < iXOffset; iX++ )
      {
        pDstSamplesCb[iX] += iScale * (pAddSamplesCb[iX] >> iPUResiPredShift[0]);
        pDstSamplesCr[iX] += iScale * (pAddSamplesCr[iX] >> iPUResiPredShift[0]);
      }
    }

    if(iPUResiPredShift[1] >= 0)
    {
      for( Int iX = iXOffset; iX < iWidth; iX++ )
      {
        pDstSamplesCb[iX] += iScale * (pAddSamplesCb[iX] >> iPUResiPredShift[1]);
        pDstSamplesCr[iX] += iScale * (pAddSamplesCr[iX] >> iPUResiPredShift[1]);
      }
    }
  }

  for( Int iY = iYOffset; iY < iHeight; iY++, pDstSamplesCb += iDstStride, pAddSamplesCb += iAddStride,
    pDstSamplesCr += iDstStride, pAddSamplesCr += iAddStride  )
  {
    if(iPUResiPredShift[2] >= 0)
    {
      for( Int iX = 0; iX < iXOffset; iX++ )
      {
        pDstSamplesCb[iX] += iScale * (pAddSamplesCb[iX] >> iPUResiPredShift[2]);
        pDstSamplesCr[iX] += iScale * (pAddSamplesCr[iX] >> iPUResiPredShift[2]);
      }
    }

    if(iPUResiPredShift[3] >= 0)
    {
      for( Int iX = iXOffset; iX < iWidth; iX++ )
      {
        pDstSamplesCb[iX] += iScale * (pAddSamplesCb[iX] >> iPUResiPredShift[3]);
        pDstSamplesCr[iX] += iScale * (pAddSamplesCr[iX] >> iPUResiPredShift[3]);
      }
    }
  }
#else
  for( Int iY = 0; iY < iHeight; iY++, pDstSamplesCb += iDstStride, pAddSamplesCb += iAddStride,
                                       pDstSamplesCr += iDstStride, pAddSamplesCr += iAddStride  )
  {
    for( Int iX = 0; iX < iWidth; iX++ )
    {
      pDstSamplesCb[iX] += iScale * pAddSamplesCb[iX];
      pDstSamplesCr[iX] += iScale * pAddSamplesCr[iX];
    }
  }
#endif
}

Void
TComYuv::clip( Int iWidth, Int iHeight )
{
  clipLuma   ( iWidth,    iHeight    );
  clipChroma ( iWidth>>1, iHeight>>1 );
}

Void
TComYuv::clipLuma( Int iWidth, Int iHeight )
{
  Int   iStride  = getStride();
  Pel*  pSamples = getLumaAddr();
  for( Int iY = 0; iY < iHeight; iY++, pSamples += iStride )
  {
    for( Int iX = 0; iX < iWidth; iX++ )
    {
      pSamples[iX] = xClip( pSamples[iX] );
    }
  }
}

Void
TComYuv::clipChroma( Int iWidth, Int iHeight )
{
  Int   iStride    = getCStride();
  Pel*  pSamplesCb = getCbAddr();
  Pel*  pSamplesCr = getCrAddr();
  for( Int iY = 0; iY < iHeight; iY++, pSamplesCb += iStride, pSamplesCr += iStride )
  {
    for( Int iX = 0; iX < iWidth; iX++ )
    {
      pSamplesCb[iX] = xClip( pSamplesCb[iX] );
      pSamplesCr[iX] = xClip( pSamplesCr[iX] );
    }
  }
}

Void TComYuv::addClip( TComYuv* pcYuvSrc0, TComYuv* pcYuvSrc1, UInt uiTrUnitIdx, UInt uiPartSize )
{
  addClipLuma   ( pcYuvSrc0, pcYuvSrc1, uiTrUnitIdx, uiPartSize     );
  addClipChroma ( pcYuvSrc0, pcYuvSrc1, uiTrUnitIdx, uiPartSize>>1  );
}

Void TComYuv::addClipLuma( TComYuv* pcYuvSrc0, TComYuv* pcYuvSrc1, UInt uiTrUnitIdx, UInt uiPartSize )
{
  Int x, y;
  
  Pel* pSrc0 = pcYuvSrc0->getLumaAddr( uiTrUnitIdx, uiPartSize );
  Pel* pSrc1 = pcYuvSrc1->getLumaAddr( uiTrUnitIdx, uiPartSize );
  Pel* pDst  = getLumaAddr( uiTrUnitIdx, uiPartSize );
  
  UInt iSrc0Stride = pcYuvSrc0->getStride();
  UInt iSrc1Stride = pcYuvSrc1->getStride();
  UInt iDstStride  = getStride();
  for ( y = uiPartSize-1; y >= 0; y-- )
  {
    for ( x = uiPartSize-1; x >= 0; x-- )
    {
      pDst[x] = Clip( pSrc0[x] + pSrc1[x] );
    }
    pSrc0 += iSrc0Stride;
    pSrc1 += iSrc1Stride;
    pDst  += iDstStride;
  }
}

Void TComYuv::addClipChroma( TComYuv* pcYuvSrc0, TComYuv* pcYuvSrc1, UInt uiTrUnitIdx, UInt uiPartSize )
{
  Int x, y;
  
  Pel* pSrcU0 = pcYuvSrc0->getCbAddr( uiTrUnitIdx, uiPartSize );
  Pel* pSrcU1 = pcYuvSrc1->getCbAddr( uiTrUnitIdx, uiPartSize );
  Pel* pSrcV0 = pcYuvSrc0->getCrAddr( uiTrUnitIdx, uiPartSize );
  Pel* pSrcV1 = pcYuvSrc1->getCrAddr( uiTrUnitIdx, uiPartSize );
  Pel* pDstU = getCbAddr( uiTrUnitIdx, uiPartSize );
  Pel* pDstV = getCrAddr( uiTrUnitIdx, uiPartSize );
  
  UInt  iSrc0Stride = pcYuvSrc0->getCStride();
  UInt  iSrc1Stride = pcYuvSrc1->getCStride();
  UInt  iDstStride  = getCStride();
  for ( y = uiPartSize-1; y >= 0; y-- )
  {
    for ( x = uiPartSize-1; x >= 0; x-- )
    {
      pDstU[x] = Clip( pSrcU0[x] + pSrcU1[x] );
      pDstV[x] = Clip( pSrcV0[x] + pSrcV1[x] );
    }
    
    pSrcU0 += iSrc0Stride;
    pSrcU1 += iSrc1Stride;
    pSrcV0 += iSrc0Stride;
    pSrcV1 += iSrc1Stride;
    pDstU  += iDstStride;
    pDstV  += iDstStride;
  }
}

#if LG_RESTRICTEDRESPRED_M24766
Void TComYuv::subtract(Int *iPUResiPredShift, PartSize uhPartitionSize, TComYuv* pcYuvSrc0, TComYuv* pcYuvSrc1, UInt uiTrUnitIdx, UInt uiPartSize )
{
  subtractLuma  (iPUResiPredShift, uhPartitionSize, pcYuvSrc0, pcYuvSrc1,  uiTrUnitIdx, uiPartSize    );
  subtractChroma(iPUResiPredShift, uhPartitionSize, pcYuvSrc0, pcYuvSrc1,  uiTrUnitIdx, uiPartSize>>1 );
}
#else
Void TComYuv::subtract( TComYuv* pcYuvSrc0, TComYuv* pcYuvSrc1, UInt uiTrUnitIdx, UInt uiPartSize )
{
  subtractLuma  ( pcYuvSrc0, pcYuvSrc1,  uiTrUnitIdx, uiPartSize    );
  subtractChroma( pcYuvSrc0, pcYuvSrc1,  uiTrUnitIdx, uiPartSize>>1 );
}
#endif

#if LG_RESTRICTEDRESPRED_M24766
Void TComYuv::subtractLuma(Int *iPUResiPredShift, PartSize uhPartitionSize, TComYuv* pcYuvSrc0, TComYuv* pcYuvSrc1, UInt uiTrUnitIdx, UInt uiPartSize )
#else
Void TComYuv::subtractLuma( TComYuv* pcYuvSrc0, TComYuv* pcYuvSrc1, UInt uiTrUnitIdx, UInt uiPartSize )
#endif
{
  Int x, y;
  
  Pel* pSrc0 = pcYuvSrc0->getLumaAddr( uiTrUnitIdx, uiPartSize );
  Pel* pSrc1 = pcYuvSrc1->getLumaAddr( uiTrUnitIdx, uiPartSize );
  Pel* pDst  = getLumaAddr( uiTrUnitIdx, uiPartSize );
  
  Int  iSrc0Stride = pcYuvSrc0->getStride();
  Int  iSrc1Stride = pcYuvSrc1->getStride();
  Int  iDstStride  = getStride();

#if LG_RESTRICTEDRESPRED_M24766
  Int iXOffset, iYOffset;

  getPUXYOffset(uhPartitionSize, uiPartSize, uiPartSize, iXOffset, iYOffset);

#if FIX_LG_RESTRICTEDRESPRED_M24766
  for ( y = 0; y < iYOffset; y++ )
  {
    if(iPUResiPredShift[0] >= 0)
    {
      for ( x = 0; x < iXOffset; x++ )
      {
        pDst[x] = pSrc0[x] - (pSrc1[x] >> iPUResiPredShift[0]);
      }
    }

    if(iPUResiPredShift[1] >= 0)
    {
      for ( x = iXOffset; x < uiPartSize; x++ )
      {
        pDst[x] = pSrc0[x] - (pSrc1[x] >> iPUResiPredShift[1]);
      }
    }
    pSrc0 += iSrc0Stride;
    pSrc1 += iSrc1Stride;
    pDst  += iDstStride;
  }

  for ( y = iYOffset; y < uiPartSize; y++ )
  {
    if(iPUResiPredShift[2] >= 0)
    {
      for ( x = 0; x < iXOffset; x++ )
      {
        pDst[x] = pSrc0[x] - (pSrc1[x] >> iPUResiPredShift[2]);
      }
    }

    if(iPUResiPredShift[3] >= 0)
    {
      for ( x = iXOffset; x < uiPartSize; x++ )
      {
        pDst[x] = pSrc0[x] - (pSrc1[x] >> iPUResiPredShift[3]);
      }
    }
    pSrc0 += iSrc0Stride;
    pSrc1 += iSrc1Stride;
    pDst  += iDstStride;
  }
#else
  for ( y = uiPartSize-1; y >= iYOffset; y-- )
  {
    if(iPUResiPredShift[3] >= 0)
    {
      for ( x = uiPartSize-1; x >= iXOffset; x-- )
      {
        pDst[x] = pSrc0[x] - (pSrc1[x] >> iPUResiPredShift[3]);
      }
    }

    if(iPUResiPredShift[2] >= 0)
    {
      for ( x = iXOffset-1; x >= 0; x-- )
      {
        pDst[x] = pSrc0[x] - (pSrc1[x] >> iPUResiPredShift[2]);
      }
    }
    pSrc0 += iSrc0Stride;
    pSrc1 += iSrc1Stride;
    pDst  += iDstStride;
  }

  for ( y = iYOffset-1; y >= 0; y-- )
  {
    if(iPUResiPredShift[1] >= 0)
    {
      for ( x = uiPartSize-1; x >= iXOffset; x-- )
      {
        pDst[x] = pSrc0[x] - (pSrc1[x] >> iPUResiPredShift[3]);
      }
    }

    if(iPUResiPredShift[0] >= 0)
    {
      for ( x = iXOffset-1; x >= 0; x-- )
      {
        pDst[x] = pSrc0[x] - (pSrc1[x] >> iPUResiPredShift[2]);
      }
    }
    pSrc0 += iSrc0Stride;
    pSrc1 += iSrc1Stride;
    pDst  += iDstStride;
  }
#endif
#else
  for ( y = uiPartSize-1; y >= 0; y-- )
  {
    for ( x = uiPartSize-1; x >= 0; x-- )
    {
      pDst[x] = pSrc0[x] - pSrc1[x];
    }
    pSrc0 += iSrc0Stride;
    pSrc1 += iSrc1Stride;
    pDst  += iDstStride;
  }
#endif
}

#if LG_RESTRICTEDRESPRED_M24766
Void TComYuv::subtractChroma(Int *iPUResiPredShift, PartSize uhPartitionSize, TComYuv* pcYuvSrc0, TComYuv* pcYuvSrc1, UInt uiTrUnitIdx, UInt uiPartSize )
#else
Void TComYuv::subtractChroma( TComYuv* pcYuvSrc0, TComYuv* pcYuvSrc1, UInt uiTrUnitIdx, UInt uiPartSize )
#endif
{
  Int x, y;
  
  Pel* pSrcU0 = pcYuvSrc0->getCbAddr( uiTrUnitIdx, uiPartSize );
  Pel* pSrcU1 = pcYuvSrc1->getCbAddr( uiTrUnitIdx, uiPartSize );
  Pel* pSrcV0 = pcYuvSrc0->getCrAddr( uiTrUnitIdx, uiPartSize );
  Pel* pSrcV1 = pcYuvSrc1->getCrAddr( uiTrUnitIdx, uiPartSize );
  Pel* pDstU  = getCbAddr( uiTrUnitIdx, uiPartSize );
  Pel* pDstV  = getCrAddr( uiTrUnitIdx, uiPartSize );
  
  Int  iSrc0Stride = pcYuvSrc0->getCStride();
  Int  iSrc1Stride = pcYuvSrc1->getCStride();
  Int  iDstStride  = getCStride();
#if LG_RESTRICTEDRESPRED_M24766
  Int iXOffset, iYOffset;
 
  getPUXYOffset(uhPartitionSize, uiPartSize, uiPartSize, iXOffset, iYOffset);

#if FIX_LG_RESTRICTEDRESPRED_M24766
  for ( y = 0; y < iYOffset; y++ )
  {
    if(iPUResiPredShift[0] >= 0)
    {
      for ( x = 0; x < iXOffset; x++ )
      {
        pDstU[x] = pSrcU0[x] - (pSrcU1[x]>>iPUResiPredShift[0]);
        pDstV[x] = pSrcV0[x] - (pSrcV1[x]>>iPUResiPredShift[0]);
      }
    }

    if(iPUResiPredShift[1] >= 0)
    {
      for ( x = iXOffset; x < uiPartSize; x++ )
      {
        pDstU[x] = pSrcU0[x] - (pSrcU1[x]>>iPUResiPredShift[1]);
        pDstV[x] = pSrcV0[x] - (pSrcV1[x]>>iPUResiPredShift[1]);
      }
    }
    pSrcU0 += iSrc0Stride;
    pSrcU1 += iSrc1Stride;
    pSrcV0 += iSrc0Stride;
    pSrcV1 += iSrc1Stride;
    pDstU  += iDstStride;
    pDstV  += iDstStride;
  }

  for ( y = iYOffset; y < uiPartSize; y++ )
  {
    if(iPUResiPredShift[2] >= 0)
    {
      for ( x = 0; x < iXOffset; x++ )
      {
        pDstU[x] = pSrcU0[x] - (pSrcU1[x]>>iPUResiPredShift[2]);
        pDstV[x] = pSrcV0[x] - (pSrcV1[x]>>iPUResiPredShift[2]);
      }
    }

    if(iPUResiPredShift[3] >= 0)
    {
      for ( x = iXOffset; x < uiPartSize; x++ )
      {
        pDstU[x] = pSrcU0[x] - (pSrcU1[x]>>iPUResiPredShift[3]);
        pDstV[x] = pSrcV0[x] - (pSrcV1[x]>>iPUResiPredShift[3]);
      }
    }
    pSrcU0 += iSrc0Stride;
    pSrcU1 += iSrc1Stride;
    pSrcV0 += iSrc0Stride;
    pSrcV1 += iSrc1Stride;
    pDstU  += iDstStride;
    pDstV  += iDstStride;
  }
#else
  for ( y = uiPartSize-1; y >= iYOffset; y-- )
  {
    if(iPUResiPredShift[3] >= 0)
    {
      for ( x = uiPartSize-1; x >= iXOffset; x-- )
      {
        pDstU[x] = pSrcU0[x] - (pSrcU1[x]>>iPUResiPredShift[3]);
        pDstV[x] = pSrcV0[x] - (pSrcV1[x]>>iPUResiPredShift[3]);
      }
    }

    if(iPUResiPredShift[2] >= 0)
    {
      for ( x = iXOffset-1; x >= 0; x-- )
      {
        pDstU[x] = pSrcU0[x] - (pSrcU1[x]>>iPUResiPredShift[2]);
        pDstV[x] = pSrcV0[x] - (pSrcV1[x]>>iPUResiPredShift[2]);
      }
    }
    pSrcU0 += iSrc0Stride;
    pSrcU1 += iSrc1Stride;
    pSrcV0 += iSrc0Stride;
    pSrcV1 += iSrc1Stride;
    pDstU  += iDstStride;
    pDstV  += iDstStride;
  }

  for ( y = iYOffset-1; y >= 0; y-- )
  {
    if(iPUResiPredShift[1] >= 0)
    {
      for ( x = uiPartSize-1; x >= iXOffset; x-- )
      {
        pDstU[x] = pSrcU0[x] - (pSrcU1[x]>>iPUResiPredShift[1]);
        pDstV[x] = pSrcV0[x] - (pSrcV1[x]>>iPUResiPredShift[1]);
      }
    }

    if(iPUResiPredShift[0] >= 0)
    {
      for ( x = iXOffset-1; x >= 0; x-- )
      {
        pDstU[x] = pSrcU0[x] - (pSrcU1[x]>>iPUResiPredShift[0]);
        pDstV[x] = pSrcV0[x] - (pSrcV1[x]>>iPUResiPredShift[0]);
      }
    }
    pSrcU0 += iSrc0Stride;
    pSrcU1 += iSrc1Stride;
    pSrcV0 += iSrc0Stride;
    pSrcV1 += iSrc1Stride;
    pDstU  += iDstStride;
    pDstV  += iDstStride;
  }
#endif
#else
  for ( y = uiPartSize-1; y >= 0; y-- )
  {
    for ( x = uiPartSize-1; x >= 0; x-- )
    {
      pDstU[x] = pSrcU0[x] - pSrcU1[x];
      pDstV[x] = pSrcV0[x] - pSrcV1[x];
    }
    pSrcU0 += iSrc0Stride;
    pSrcU1 += iSrc1Stride;
    pSrcV0 += iSrc0Stride;
    pSrcV1 += iSrc1Stride;
    pDstU  += iDstStride;
    pDstV  += iDstStride;
  }
#endif
}

Void TComYuv::addAvg( TComYuv* pcYuvSrc0, TComYuv* pcYuvSrc1, UInt iPartUnitIdx, UInt iWidth, UInt iHeight )
{
  Int x, y;
  
  Pel* pSrcY0  = pcYuvSrc0->getLumaAddr( iPartUnitIdx );
  Pel* pSrcU0  = pcYuvSrc0->getCbAddr  ( iPartUnitIdx );
  Pel* pSrcV0  = pcYuvSrc0->getCrAddr  ( iPartUnitIdx );
  
  Pel* pSrcY1  = pcYuvSrc1->getLumaAddr( iPartUnitIdx );
  Pel* pSrcU1  = pcYuvSrc1->getCbAddr  ( iPartUnitIdx );
  Pel* pSrcV1  = pcYuvSrc1->getCrAddr  ( iPartUnitIdx );
  
  Pel* pDstY   = getLumaAddr( iPartUnitIdx );
  Pel* pDstU   = getCbAddr  ( iPartUnitIdx );
  Pel* pDstV   = getCrAddr  ( iPartUnitIdx );
  
  UInt  iSrc0Stride = pcYuvSrc0->getStride();
  UInt  iSrc1Stride = pcYuvSrc1->getStride();
  UInt  iDstStride  = getStride();
  Int shiftNum = IF_INTERNAL_PREC + 1 - ( g_uiBitDepth + g_uiBitIncrement );
  Int offset = ( 1 << ( shiftNum - 1 ) ) + 2 * IF_INTERNAL_OFFS;
  
  for ( y = 0; y < iHeight; y++ )
  {
    for ( x = 0; x < iWidth; x += 4 )
    {
      pDstY[ x + 0 ] = Clip( ( pSrcY0[ x + 0 ] + pSrcY1[ x + 0 ] + offset ) >> shiftNum );
      pDstY[ x + 1 ] = Clip( ( pSrcY0[ x + 1 ] + pSrcY1[ x + 1 ] + offset ) >> shiftNum );
      pDstY[ x + 2 ] = Clip( ( pSrcY0[ x + 2 ] + pSrcY1[ x + 2 ] + offset ) >> shiftNum );
      pDstY[ x + 3 ] = Clip( ( pSrcY0[ x + 3 ] + pSrcY1[ x + 3 ] + offset ) >> shiftNum );
    }
    pSrcY0 += iSrc0Stride;
    pSrcY1 += iSrc1Stride;
    pDstY  += iDstStride;
  }
  
  iSrc0Stride = pcYuvSrc0->getCStride();
  iSrc1Stride = pcYuvSrc1->getCStride();
  iDstStride  = getCStride();
  
  iWidth  >>=1;
  iHeight >>=1;
  
  for ( y = iHeight-1; y >= 0; y-- )
  {
    for ( x = iWidth-1; x >= 0; )
    {
      // note: chroma min width is 2
      pDstU[x] = Clip((pSrcU0[x] + pSrcU1[x] + offset) >> shiftNum);
      pDstV[x] = Clip((pSrcV0[x] + pSrcV1[x] + offset) >> shiftNum); x--;
      pDstU[x] = Clip((pSrcU0[x] + pSrcU1[x] + offset) >> shiftNum);
      pDstV[x] = Clip((pSrcV0[x] + pSrcV1[x] + offset) >> shiftNum); x--;
    }
    
    pSrcU0 += iSrc0Stride;
    pSrcU1 += iSrc1Stride;
    pSrcV0 += iSrc0Stride;
    pSrcV1 += iSrc1Stride;
    pDstU  += iDstStride;
    pDstV  += iDstStride;
  }
}

#if DEPTH_MAP_GENERATION
Void TComYuv::addAvgPdm( TComYuv* pcYuvSrc0, TComYuv* pcYuvSrc1, UInt iPartUnitIdx, UInt iWidth, UInt iHeight, UInt uiSubSampExpX, UInt uiSubSampExpY )
{
  Int x, y;

  UInt uiBlkX  = g_auiRasterToPelX[ g_auiZscanToRaster[ iPartUnitIdx ] ] >> uiSubSampExpX;
  UInt uiBlkY  = g_auiRasterToPelY[ g_auiZscanToRaster[ iPartUnitIdx ] ] >> uiSubSampExpY;
  Pel* pSrcY0  = pcYuvSrc0->getLumaAddr( iPartUnitIdx );
  Pel* pSrcY1  = pcYuvSrc1->getLumaAddr( iPartUnitIdx );
  Pel* pDstY   = getLumaAddr() + uiBlkY * getStride() + uiBlkX;

  UInt  iSrc0Stride = pcYuvSrc0->getStride();
  UInt  iSrc1Stride = pcYuvSrc1->getStride();
  UInt  iDstStride  = getStride();

  for ( y = iHeight-1; y >= 0; y-- )
  {
    for ( x = iWidth-1; x >= 0; x-- )
    {
      pDstY[x] = (pSrcY0[x] + pSrcY1[x] + 1) >> 1;
    }
    pSrcY0 += iSrc0Stride;
    pSrcY1 += iSrc1Stride;
    pDstY  += iDstStride;
  }
}
#endif

Void TComYuv::removeHighFreq( TComYuv* pcYuvSrc, UInt uiPartIdx, UInt uiWidht, UInt uiHeight )
{
  Int x, y;
  
  Pel* pSrc  = pcYuvSrc->getLumaAddr(uiPartIdx);
  Pel* pSrcU = pcYuvSrc->getCbAddr(uiPartIdx);
  Pel* pSrcV = pcYuvSrc->getCrAddr(uiPartIdx);
  
  Pel* pDst  = getLumaAddr(uiPartIdx);
  Pel* pDstU = getCbAddr(uiPartIdx);
  Pel* pDstV = getCrAddr(uiPartIdx);
  
  Int  iSrcStride = pcYuvSrc->getStride();
  Int  iDstStride = getStride();
  
  for ( y = uiHeight-1; y >= 0; y-- )
  {
    for ( x = uiWidht-1; x >= 0; x-- )
    {
#if DISABLING_CLIP_FOR_BIPREDME
      pDst[x ] = (pDst[x ]<<1) - pSrc[x ] ;
#else
      pDst[x ] = Clip( (pDst[x ]<<1) - pSrc[x ] );
#endif
    }
    pSrc += iSrcStride;
    pDst += iDstStride;
  }
  
  iSrcStride = pcYuvSrc->getCStride();
  iDstStride = getCStride();
  
  uiHeight >>= 1;
  uiWidht  >>= 1;
  
  for ( y = uiHeight-1; y >= 0; y-- )
  {
    for ( x = uiWidht-1; x >= 0; x-- )
    {
#if DISABLING_CLIP_FOR_BIPREDME
      pDstU[x ] = (pDstU[x ]<<1) - pSrcU[x ] ;
      pDstV[x ] = (pDstV[x ]<<1) - pSrcV[x ] ;
#else
      pDstU[x ] = Clip( (pDstU[x ]<<1) - pSrcU[x ] );
      pDstV[x ] = Clip( (pDstV[x ]<<1) - pSrcV[x ] );
#endif
    }
    pSrcU += iSrcStride;
    pSrcV += iSrcStride;
    pDstU += iDstStride;
    pDstV += iDstStride;
  }
}
//! \}
