简介
帧内共有67种预测模式,包括 65种角度+DC+Planar
模式,VTM中调用predIntraAng()
生成相应的预测信号。predIntraAng()
主要功能是调用相关预测函数,生成67中模式的预测信号,并对满足 PDPC条件 的预测模式进行预测信号矫正。PDPC的计算公式如下:
p r e d ( x , y ) = [ w L × R − 1 , y + w T × R x , − 1 – w T L × R − 1 , − 1 + ( 64 – w L – w T + w T L ) × p r e d ( x , y ) + 32 ] > > 6 pred(x,y) = [ wL×R_{-1,y} + wT×R_{x,-1} – wTL ×R_{-1,-1} + (64 – wL – wT+wTL) × pred(x,y) + 32 ]>>6 pred(x,y)=[wL×R−1,y+wT×Rx,−1–wTL×R−1,−1+(64–wL–wT+wTL)×pred(x,y)+32]>>6
其中 p r e d ( x , y ) pred(x,y) pred(x,y) 为最终预测值。 R x , − 1 R_{x,-1} Rx,−1和 R − 1 , y R_{-1,y} R−1,y当前像素(x,y)上方和左侧的相邻参考像素, R − 1 , − 1 R_{-1,-1} R−1,−1表示当前块左上角的参考像素。PDPC的权重依赖于帧内预测模式,具体如下
预测模式 | wT | wL | wTL |
---|---|---|---|
右上对角线模式 | 16 >> ( ( y’<<1 ) >> shift) | 16 >> ( ( x’<<1 ) >> shift) | 0 |
下左对角线模式 | 16 >> ( ( y’<<1 ) >> shift ) | 16 >> ( ( x’<<1 ) >> shift ) | 0 |
右上对角线模式相邻角度 | 32 >> ( ( y’<<1 ) >> shift ) | 0 | 0 |
下左对角线模式相邻角度 | 0 | 32 >> ( ( x’<<1 ) >> shift ) | 0 |
其中生成预测信号时,主要分为DC、Planar和角度预测信号生成。其中角度预测需要注意多行预测以及是否为广角预测。预测信号生成调用情况如下:
- DC模式:
xPredIntraDc()
- Planar模式:
xPredIntraPlanar()
- 其他角度模式:
xPredIntraAng()
predIntraAng()
void IntraPrediction::predIntraAng( const ComponentID compId, PelBuf &piPred, const PredictionUnit &pu, const bool useFilteredPredSamples )
{
const ComponentID compID = MAP_CHROMA( compId );
const ChannelType channelType = toChannelType( compID );
const int iWidth = piPred.width;
const int iHeight = piPred.height;
#if JVET_M0102_INTRA_SUBPARTITIONS
const Size cuSize = Size( pu.cu->blocks[compId].width, pu.cu->blocks[compId].height );
#endif
const uint32_t uiDirMode = PU::getFinalIntraMode( pu, channelType ); //如果是色度,进行预测模式转换:0~66
CHECK( g_aucLog2[iWidth] < 2 && pu.cs->pcv->noChroma2x2, "Size not allowed" );
CHECK( g_aucLog2[iWidth] > 7, "Size not allowed" );
const int multiRefIdx = (compID == COMPONENT_Y) ? pu.multiRefIdx : 0; //MRL参考行索引
const bool useISP = pu.cu->ispMode && isLuma( compID ); //是否ISP预测
const int whRatio = useISP ? std::max( unsigned( 1 ), cuSize.width / cuSize.height ) : std::max( 1, iWidth / iHeight );
const int hwRatio = useISP ? std::max( unsigned( 1 ), cuSize.height / cuSize.width ) : std::max( 1, iHeight / iWidth );
const int srcStride = m_topRefLength + 1 + (whRatio + 1) * multiRefIdx;
const int srcHStride = m_leftRefLength + 1 + (hwRatio + 1) * multiRefIdx;
Pel *ptrSrc = getPredictorPtr(compID, useFilteredPredSamples); //帧内参考样本首地址
const ClpRng& clpRng(pu.cu->cs->slice->clpRng(compID));
switch (uiDirMode) // 生成预测信号
{
case(PLANAR_IDX): xPredIntraPlanar(CPelBuf(ptrSrc, srcStride, srcHStride), piPred, *pu.cs->sps); break;
case(DC_IDX): xPredIntraDc(CPelBuf(ptrSrc, srcStride, srcHStride), piPred, channelType, false); break;
case(2):
case(DIA_IDX):
case(VDIA_IDX):
if (getWideAngle(useISP ? cuSize.width : iWidth, useISP ? cuSize.height : iHeight, uiDirMode) == static_cast<int>(uiDirMode)) // check if uiDirMode is not wide-angle
{
xPredIntraAng(CPelBuf(ptrSrc, srcStride, srcHStride), piPred, channelType, uiDirMode, clpRng, *pu.cs->sps, multiRefIdx, useFilteredPredSamples, useISP, cuSize );
break;
}
default: xPredIntraAng(CPelBuf(getPredictorPtr(compID, false), srcStride, srcHStride), piPred, channelType, uiDirMode, clpRng, *pu.cs->sps, multiRefIdx, useFilteredPredSamples, useISP, cuSize); break;
}
bool pdpcCondition = (uiDirMode == PLANAR_IDX || uiDirMode == DC_IDX || uiDirMode == HOR_IDX || uiDirMode == VER_IDX);
#if JVET_M0102_INTRA_SUBPARTITIONS
if( pdpcCondition && multiRefIdx == 0 && !useISP ) // 满足PDPC角度模式,且参考行不是扩展行,同时也不是ISP预测时,进行PDPC矫正
#else
if (pdpcCondition && multiRefIdx == 0)
#endif
{
const CPelBuf srcBuf = CPelBuf(ptrSrc, srcStride, srcStride);
PelBuf dstBuf = piPred;
const int scale = ((g_aucLog2[iWidth] - 2 + g_aucLog2[iHeight] - 2 + 2) >> 2);
CHECK(scale < 0 || scale > 31, "PDPC: scale < 0 || scale > 31");
if (uiDirMode == PLANAR_IDX)
{
for (int y = 0; y < iHeight; y++)
{
int wT = 32 >> std::min(31, ((y << 1) >> scale));
const Pel left = srcBuf.at(0, y + 1);
for (int x = 0; x < iWidth; x++)
{
const Pel top = srcBuf.at(x + 1, 0);
int wL = 32 >> std::min(31, ((x << 1) >> scale));
dstBuf.at(x, y) = ClipPel((wL * left + wT * top + (64 - wL - wT) * dstBuf.at(x, y) + 32) >> 6, clpRng);
}
}
}
else if (uiDirMode == DC_IDX)
{
const Pel topLeft = srcBuf.at(0, 0);
for (int y = 0; y < iHeight; y++)
{
int wT = 32 >> std::min(31, ((y << 1) >> scale));
const Pel left = srcBuf.at(0, y + 1);
for (int x = 0; x < iWidth; x++)
{
const Pel top = srcBuf.at(x + 1, 0);
int wL = 32 >> std::min(31, ((x << 1) >> scale));
int wTL = (wL >> 4) + (wT >> 4);
dstBuf.at(x, y) = ClipPel((wL * left + wT * top - wTL * topLeft + (64 - wL - wT + wTL) * dstBuf.at(x, y) + 32) >> 6, clpRng);
}
}
}
else if (uiDirMode == HOR_IDX)
{
const Pel topLeft = srcBuf.at(0, 0);
for (int y = 0; y < iHeight; y++)
{
int wT = 32 >> std::min(31, ((y << 1) >> scale));
for (int x = 0; x < iWidth; x++)
{
const Pel top = srcBuf.at(x + 1, 0);
int wTL = wT;
dstBuf.at(x, y) = ClipPel((wT * top - wTL * topLeft + (64 - wT + wTL) * dstBuf.at(x, y) + 32) >> 6, clpRng);
}
}
}
else if (uiDirMode == VER_IDX)
{
const Pel topLeft = srcBuf.at(0, 0);
for (int y = 0; y < iHeight; y++)
{
const Pel left = srcBuf.at(0, y + 1);
for (int x = 0; x < iWidth; x++)
{
int wL = 32 >> std::min(31, ((x << 1) >> scale));
int wTL = wL;
dstBuf.at(x, y) = ClipPel((wL * left - wTL * topLeft + (64 - wL + wTL) * dstBuf.at(x, y) + 32) >> 6, clpRng);
}
}
}
}
}
xPredIntraDc()
其中DC模式的预测信号调用xPredIntraDc()
,代码如下:
void IntraPrediction::xPredIntraDc( const CPelBuf &pSrc, PelBuf &pDst, const ChannelType channelType, const bool enableBoundaryFilter )
{
const Pel dcval = xGetPredValDc( pSrc, pDst ); // 生成DC值
pDst.fill( dcval );
#if HEVC_USE_DC_PREDFILTERING // 默认关闭
if( enableBoundaryFilter )
{
xDCPredFiltering( pSrc, pDst, channelType );
}
#endif
}
DC值生成,需要注意对于非正方形预测单元,只用一侧的参考像素计算DC(正上方或者正左侧),代码如下
Pel IntraPrediction::xGetPredValDc( const CPelBuf &pSrc, const Size &dstSize )
{
CHECK( dstSize.width == 0 || dstSize.height == 0, "Empty area provided" );
int idx, sum = 0;
Pel dcVal;
const int width = dstSize.width;
const int height = dstSize.height;
const auto denom = (width == height) ? (width << 1) : std::max(width,height); // 用来计算DC值,所用参考样点个数
const auto divShift = g_aucLog2[denom];
const auto divOffset = (denom >> 1);
if ( width >= height ) // 宽大于高,只用上方参考
{
for( idx = 0; idx < width; idx++ )
{
sum += pSrc.at( 1 + idx, 0 );
}
}
if ( width <= height )// 宽小于高,只用左侧参考
{
for( idx = 0; idx < height; idx++ )
{
sum += pSrc.at( 0, 1 + idx );
}
}
dcVal = (sum + divOffset) >> divShift;
return dcVal;
}
xPredIntraPlanar()
Planar 模式预测信号,Planar图像渐变预测,使用右上填充右边一列参考像素,使用左下填充下一列参考像素,然后四个像素和的平均作为预测值。代码如下:
/**
* pSrc : 参考像素缓存区
* pDst : 重建像素缓存区
**/
void IntraPrediction::xPredIntraPlanar( const CPelBuf &pSrc, PelBuf &pDst, const SPS& sps )
{
const uint32_t width = pDst.width; //重建宽
const uint32_t height = pDst.height; //重建高
#if JVET_M0102_INTRA_SUBPARTITIONS
const uint32_t log2W = g_aucLog2[width < 2 ? 2 : width];
const uint32_t log2H = g_aucLog2[height < 2 ? 2 : height];
#else
const uint32_t log2W = g_aucLog2[ width ];
const uint32_t log2H = g_aucLog2[ height ];
#endif
// 左列、上行、右列、下行
int leftColumn[MAX_CU_SIZE + 1], topRow[MAX_CU_SIZE + 1], bottomRow[MAX_CU_SIZE], rightColumn[MAX_CU_SIZE];
#if JVET_M0102_INTRA_SUBPARTITIONS
const uint32_t offset = 1 << (log2W + log2H);
#else
const uint32_t offset = width * height;
#endif
// Get left and above reference column and row
for( int k = 0; k < width + 1; k++ ) //上边界参考像素,长度width+1:p[1][0],p[2][0],p[3][0]...p[width+1][0]
{
topRow[k] = pSrc.at( k + 1, 0 );
}
for( int k = 0; k < height + 1; k++ )//左边界参考像素,长度height+1,p[0][1],p[0][2],p[0][3]...p[0][height+1]
{
leftColumn[k] = pSrc.at( 0, k + 1 );
}
/**
* Planar模式横向的预测值为 pred_h(x,y) = (width-x)*leftcolumn(y) + x*topRight
* Planar模式宗向的预测值为 pred_v(x,y) = (height-y)*topRow(x) + y*bottomLeft
* 最终预测值为:pred(x,y) = (pred_h + pred_v + offset) >> shift
*
* 代码里将上式调整为:pred_h(x,y) = width*leftcolumn(y) + x*[topRight-leftcolumn(y)]
* pred_v(x,y) = height*topRow(x) + y*[bottomLeft-topRow(x)]
**/
// Prepare intermediate variables used in interpolation
int bottomLeft = leftColumn[height]; // 左边界向下一个
int topRight = topRow[width]; // 上边界向右一位
for( int k = 0; k < width; k++ )
{
bottomRow[k] = bottomLeft - topRow[k];
topRow[k] = topRow[k] << log2H;
}
for( int k = 0; k < height; k++ )
{
rightColumn[k] = topRight - leftColumn[k];
leftColumn[k] = leftColumn[k] << log2W;
}
const uint32_t finalShift = 1 + log2W + log2H;
const uint32_t stride = pDst.stride;
Pel* pred = pDst.buf; // 预测像素首地址
for( int y = 0; y < height; y++, pred += stride ) // 逐行
{
int horPred = leftColumn[y];
for( int x = 0; x < width; x++ )
{
horPred += rightColumn[y];
topRow[x] += bottomRow[x];
int vertPred = topRow[x];
pred[x] = ( ( horPred << log2H ) + ( vertPred << log2W ) + offset ) >> finalShift;
}
}
}
xPredIntraAng()
角度预测之前,首先需要判断是否为广角预测模式,广角模式的获取方式代码如下:
int IntraPrediction::getWideAngle( int width, int height, int predMode )
{
if ( predMode > DC_IDX && predMode <= VDIA_IDX )
{
int modeShift[] = { 0, 6, 10, 12, 14, 15 };
int deltaSize = abs(g_aucLog2[width] - g_aucLog2[height]); // log2(width/height) 获取偏移量
if (width > height && predMode < 2 + modeShift[deltaSize])
{
predMode += (VDIA_IDX - 1);
}
else if (height > width && predMode > VDIA_IDX - modeShift[deltaSize])
{
predMode -= (VDIA_IDX - 1);
}
}
return predMode;
}
即,如果getWideAngle()
返回模式与预测模式相符,则为广角预测,否则为正常角度预测。
角度预测信号生成,代码如下
/** Function for deriving the simplified angular intra predictions.
*
* This function derives the prediction samples for the angular mode based on the prediction direction indicated by
* the prediction mode index. The prediction direction is given by the displacement of the bottom row of the block and
* the reference row above the block in the case of vertical prediction or displacement of the rightmost column
* of the block and reference column left from the block in the case of the horizontal prediction. The displacement
* is signalled at 1/32 pixel accuracy. When projection of the predicted pixel falls inbetween reference samples,
* the predicted value for the pixel is linearly interpolated from the reference samples. All reference samples are taken
* from the extended main reference.
*/
void IntraPrediction::xPredIntraAng( const CPelBuf &pSrc, PelBuf &pDst, const ChannelType channelType, const uint32_t dirMode, const ClpRng& clpRng, const SPS& sps,
int multiRefIdx,
const bool useFilteredPredSamples ,
const bool useISP,
const Size cuSize )
{
int width =int(pDst.width); // 待预测宽
int height=int(pDst.height); // 待预测长
CHECK( !( dirMode > DC_IDX && dirMode < NUM_LUMA_MODE ), "Invalid intra dir" );
int predMode = useISP ? getWideAngle( cuSize.width, cuSize.height, dirMode ) : getWideAngle( width, height, dirMode ); // 模式映射为广角之后的模式
const bool bIsModeVer = predMode >= DIA_IDX; // 垂直34~66,水平2~33
const int intraPredAngleMode = (bIsModeVer) ? predMode - VER_IDX : -(predMode - HOR_IDX);
const int absAngMode = abs(intraPredAngleMode);
const int signAng = intraPredAngleMode < 0 ? -1 : 1;
// Set bitshifts and scale the angle parameter to block size
// 先乘以256(左移8位),运算出结果再右移8位,以定点实现浮点运算精度
static const int angTable[32] = { 0, 1, 2, 3, 4, 6, 8, 10, 12, 14, 16, 18, 20, 23, 26, 29, 32, 35, 39, 45, 51, 57, 64, 73, 86, 102, 128, 171, 256, 341, 512, 1024 };
static const int invAngTable[32] = { 0, 8192, 4096, 2731, 2048, 1365, 1024, 819, 683, 585, 512, 455, 410, 356, 315, 282, 256, 234, 210, 182, 160, 144, 128, 112, 95, 80, 64, 48, 32, 24, 16, 8 }; // (256 * 32) / Angle
int invAngle = invAngTable[absAngMode];
int absAng = angTable [absAngMode];
int intraPredAngle = signAng * absAng; // 预测模式转化为预测角度
/**
* 为了减少代码量,所有角度当做垂直计算,如果是水平预测模式,在最后做翻转
**/
Pel* refMain; // main referecnes
Pel* refSide; // side references
Pel refAbove[2 * MAX_CU_SIZE + 3 + 33 * MAX_REF_LINE_IDX];
Pel refLeft [2 * MAX_CU_SIZE + 3 + 33 * MAX_REF_LINE_IDX];
const int whRatio = useISP ? std::max( unsigned( 1 ), cuSize.width / cuSize.height ) : std::max( 1, width / height );
const int hwRatio = useISP ? std::max( unsigned( 1 ), cuSize.height / cuSize.width ) : std::max( 1, height / width );
// Initialize the Main and Left reference array. 初始化参考像素
if (intraPredAngle < 0) // 预测角度为负
{
auto width = int(pDst.width) +1;
auto height = int(pDst.height)+1;
auto lastIdx = (bIsModeVer ? width : height) + multiRefIdx;
auto firstIdx = ( ((bIsModeVer ? height : width) -1) * intraPredAngle ) >> 5;
for (int x = 0; x < width + 1 + multiRefIdx; x++) // multiref + aboveleft + above = width+1+multiref个点(多一个点是什么?)
{
refAbove[x + height - 1] = pSrc.at( x, 0 );
}
for (int y = 0; y < height + 1 + multiRefIdx; y++) // multiref + aboveleft + left = height+1+multiref个点
{
refLeft[y + width - 1] = pSrc.at( 0, y );
}
// modeVer = true: refMain = refAbove + height- 1; refSide = refLeft + width - 1
// modeVer = false: refMain = refLeft + width- 1; refSide = refAbove + height- 1
// refmain 和 refSide 添加偏移量,是指针指向ref[0]
refMain = (bIsModeVer ? refAbove + height : refLeft + width ) - 1;
refSide = (bIsModeVer ? refLeft + width : refAbove + height) - 1;
// Extend the Main reference to the left. // 复制side中的references到main references
int invAngleSum = 128; // rounding for (shift by 8) ---- draft 8-131
for( int k = -1; k > firstIdx; k-- ) // draft 8-131: ref[ x ] = p[ −1 − refIdx ][ −1 − refIdx + ( ( x * invAngle + 128 ) >> 8 ) ], with x = −1..( nTbH * intraPredAngle ) >> 5
{
invAngleSum += invAngle;
refMain[k] = refSide[invAngleSum>>8];
}
refMain[lastIdx] = refMain[lastIdx-1]; // draft 8-133: ref[ nTbW + 1 + refIdx ] = ref[ nTbW + refIdx ]
refMain[firstIdx] = refMain[firstIdx+1]; // draft 8-132: ref[ ( ( nTbH * intraPredAngle ) >> 5 ) − 1 ] = ref[ ( nTbH * intraPredAngle ) >> 5 ]
}
else // 角度为正式,垂直预测只参考上方参考样点,水平预测只参考左边参考样点
{
for (int x = 0; x < m_topRefLength + 1 + (whRatio + 1) * multiRefIdx; x++)
{
refAbove[x+1] = pSrc.at(x, 0);
}
for (int y = 0; y < m_leftRefLength + 1 + (hwRatio + 1) * multiRefIdx; y++)
{
refLeft[y+1] = pSrc.at(0, y);
}
refMain = bIsModeVer ? refAbove : refLeft ;
refSide = bIsModeVer ? refLeft : refAbove;
refMain++;
refSide++;
refMain[-1] = refMain[0];
auto lastIdx = 1 + ((bIsModeVer) ? m_topRefLength + (whRatio + 1) * multiRefIdx : m_leftRefLength + (hwRatio + 1) * multiRefIdx);
refMain[lastIdx] = refMain[lastIdx-1];
}
// swap width/height if we are doing a horizontal mode: 如果水平预测,交换长宽
Pel tempArray[MAX_CU_SIZE*MAX_CU_SIZE];
const int dstStride = bIsModeVer ? pDst.stride : MAX_CU_SIZE;
Pel *pDstBuf = bIsModeVer ? pDst.buf : tempArray;
if (!bIsModeVer) //
{
std::swap(width, height);
}
// compensate for line offset in reference line buffers
refMain += multiRefIdx;
refSide += multiRefIdx;
if( intraPredAngle == 0 ) // pure vertical or pure horizontal 水平垂直预测,直接copy赋值
{
for( int y = 0; y < height; y++ )
{
for( int x = 0; x < width; x++ )
{
pDstBuf[y*dstStride + x] = refMain[x + 1];
}
}
}
else
{
Pel *pDsty=pDstBuf;
for (int y = 0, deltaPos = intraPredAngle * (1 + multiRefIdx); y<height; y++, deltaPos += intraPredAngle, pDsty += dstStride)
{
const int deltaInt = deltaPos >> 5; // 整数坐标
const int deltaFract = deltaPos & (32 - 1); // 分数坐标
if (absAng != 0 && absAng != 32)
{
if( isLuma(channelType) ) // 亮度-四抽头插值滤波
{
Pel p[4];
const bool useCubicFilter = useISP ? ( width <= 8 ) : ( !useFilteredPredSamples || multiRefIdx > 0 );
TFilterCoeff const * const f = (useCubicFilter) ? InterpolationFilter::getChromaFilterTable(deltaFract) : g_intraGaussFilter[deltaFract];
int refMainIndex = deltaInt + 1;
for( int x = 0; x < width; x++, refMainIndex++ )
{
p[0] = refMain[refMainIndex - 1];
p[1] = refMain[refMainIndex];
p[2] = refMain[refMainIndex + 1];
p[3] = f[3] != 0 ? refMain[refMainIndex + 2] : 0;
pDstBuf[y*dstStride + x] = static_cast<Pel>((static_cast<int>(f[0] * p[0]) + static_cast<int>(f[1] * p[1]) + static_cast<int>(f[2] * p[2]) + static_cast<int>(f[3] * p[3]) + 32) >> 6);
if( useCubicFilter ) // only cubic filter has negative coefficients and requires clipping
{
pDstBuf[y*dstStride + x] = ClipPel( pDstBuf[y*dstStride + x], clpRng );
}
}
}
else // 色度-线性滤波
{
// Do linear filtering
const Pel *pRM = refMain + deltaInt + 1;
int lastRefMainPel = *pRM++;
for( int x = 0; x < width; pRM++, x++ )
{
int thisRefMainPel = *pRM;
pDsty[x + 0] = ( Pel ) ( ( ( 32 - deltaFract )*lastRefMainPel + deltaFract*thisRefMainPel + 16 ) >> 5 );
lastRefMainPel = thisRefMainPel;
}
}
}
else
{
// 整数点直接copy,不需要进行插值
// Just copy the integer samples
for( int x = 0; x < width; x++ )
{
pDsty[x] = refMain[x + deltaInt + 1];
}
}
const int numModes = 8;
const int scale = ((g_aucLog2[width] - 2 + g_aucLog2[height] - 2 + 2) >> 2);
CHECK(scale < 0 || scale > 31, "PDPC: scale < 0 || scale > 31");
#if JVET_M0102_INTRA_SUBPARTITIONS
if( !useISP ) // 对于非ISP模式,且非MRL, 对负对角线,及其8个相邻角度进行PDPC
{
#endif
if ((predMode == 2 || predMode == VDIA_IDX) && multiRefIdx == 0)
{
int wT = 16 >> std::min(31, ((y << 1) >> scale));
for (int x = 0; x < width; x++)
{
int wL = 16 >> std::min(31, ((x << 1) >> scale));
if (wT + wL == 0) break;
int c = x + y + 1;
if (c >= 2 * height) { wL = 0; }
if (c >= 2 * width) { wT = 0; }
const Pel left = (wL != 0) ? refSide[c + 1] : 0;
const Pel top = (wT != 0) ? refMain[c + 1] : 0;
pDsty[x] = ClipPel((wL * left + wT * top + (64 - wL - wT) * pDsty[x] + 32) >> 6, clpRng);
}
}
else if (((predMode >= VDIA_IDX - numModes && predMode != VDIA_IDX) || (predMode != 2 && predMode <= (2 + numModes))) && multiRefIdx == 0)
{
int invAngleSum0 = 2;
for (int x = 0; x < width; x++)
{
invAngleSum0 += invAngle;
int deltaPos0 = invAngleSum0 >> 2;
int deltaFrac0 = deltaPos0 & 63;
int deltaInt0 = deltaPos0 >> 6;
int deltay = y + deltaInt0 + 1;
if (deltay >(bIsModeVer ? m_leftRefLength : m_topRefLength) - 1) break;
int wL = 32 >> std::min(31, ((x << 1) >> scale));
if (wL == 0) break;
Pel *p = refSide + deltay;
#if JVET_M0238_PDPC_NO_INTERPOLATION // yes
Pel left = p[deltaFrac0 >> 5];
#else
Pel left = (((64 - deltaFrac0) * p[0] + deltaFrac0 * p[1] + 32) >> 6);
#endif
pDsty[x] = ClipPel((wL * left + (64 - wL) * pDsty[x] + 32) >> 6, clpRng);
}
}
#if JVET_M0102_INTRA_SUBPARTITIONS
}
#endif
}
}
// Flip the block if this is the horizontal mode
if( !bIsModeVer )
{
for( int y = 0; y < height; y++ )
{
for( int x = 0; x < width; x++ )
{
pDst.at( y, x ) = pDstBuf[x];
}
pDstBuf += dstStride;
}
}
}