xRecurIntraChromaCodingQT函数主要是由上层函数estIntraPredChromaQT函数调用,利用传来的预测模式进行预测计算残差信号,接着遍历所有可用的变换模式,对残差信号进行变换、量化、重建等,从而可以求出相应的RD Cost。
和亮度不同的是,会在xRecurIntraChromaCodingQT函数中进行色度预测,不需要在xIntraCodingTUBlock函数中计算并保存预测值(即不需要传输并修改default0Save1Load2值)
函数流程如下:
- 初始化(获得TU、PU、当前划分深度等)
- 判断当前的深度是否等于TU的深度
- 如果不等于,则对当前TU进一步划分,循环调用自身进行预测等
- 如果等于,则进行以下操作:
- 根据色度预测模式分别计算Cb Cr的预测值
- 计算原始残差信号(原始值-预测值)并保存(后面JointCbCr模式会用到)
- 遍历Cb Cr分量,分别对Cb Cr模式进行变换模式的遍历,计算RD Cost选出最佳的变换模式
- 测试JointCbCr模式,遍历需要测试的JointCbCr模式,并对残差进行变换量化,计算RD Cost并和Cb Cr单独编码的最佳RD Cost进行比较,选出最佳模式并存储最佳模式下的预测、残差、重建信号。
这里,在两种情况下会对当前CU进一步划分:
- 若当前CU的尺寸大于最大的变换尺寸,则需要强制对CU划分
- 若当前划分树类型是单树(即亮度色度的划分方式相同)且亮度是ISP模式,则需要将当前CU划分为子TU
注意:这里色度编码模式分为Cb Cr单独编码和JointCbCr(即CbCr联合编码)两种,并且都对这两种编码模式进行了变换模式的遍历。
代码和注释如下(基于VTM10.0):
ChromaCbfs IntraSearch::xRecurIntraChromaCodingQT( CodingStructure &cs, Partitioner& partitioner, const double bestCostSoFar, const PartSplit ispType )
{
UnitArea currArea = partitioner.currArea(); //当前分区
const bool keepResi = cs.sps->getUseLMChroma() || KEEP_PRED_AND_RESI_SIGNALS;
if( !currArea.Cb().valid() ) return ChromaCbfs( false );
const Slice &slice = *cs.slice;
TransformUnit &currTU = *cs.getTU( currArea.chromaPos(), CHANNEL_TYPE_CHROMA );//当前TU
const PredictionUnit &pu = *cs.getPU( currArea.chromaPos(), CHANNEL_TYPE_CHROMA );//当前PU
bool lumaUsesISP = false;
uint32_t currDepth = partitioner.currTrDepth;
ChromaCbfs cbfs ( false );
if (currDepth == currTU.depth) //当前划分深度等于当前深度
{
if (!currArea.Cb().valid() || !currArea.Cr().valid())
{
return cbfs;
}
CodingStructure &saveCS = *m_pSaveCS[1];
saveCS.pcv = cs.pcv;
saveCS.picture = cs.picture;
saveCS.area.repositionTo( cs.area );//设置临时存储CS的area
saveCS.initStructData( MAX_INT, true );
if( !currTU.cu->isSepTree() && currTU.cu->ispMode )
{
saveCS.clearCUs();
CodingUnit& auxCU = saveCS.addCU( *currTU.cu, partitioner.chType );
auxCU.ispMode = currTU.cu->ispMode;
saveCS.sps = currTU.cs->sps;
saveCS.clearPUs();
saveCS.addPU( *currTU.cu->firstPU, partitioner.chType );
}
TransformUnit &tmpTU = saveCS.addTU(currArea, partitioner.chType);
cs.setDecomp(currArea.Cb(), true); // set in advance (required for Cb2/Cr2 in 4:2:2 video)
const unsigned numTBlocks = ::getNumberValidTBlocks( *cs.pcv );
CompArea& cbArea = currTU.blocks[COMPONENT_Cb];
CompArea& crArea = currTU.blocks[COMPONENT_Cr];
double bestCostCb = MAX_DOUBLE;//最佳Cb的Cost
double bestCostCr = MAX_DOUBLE;//最佳Cr的Cost
Distortion bestDistCb = 0;
Distortion bestDistCr = 0;
int maxModesTested = 0;
bool earlyExitISP = false;
TempCtx ctxStartTU( m_CtxCache );
TempCtx ctxStart ( m_CtxCache );
TempCtx ctxBest ( m_CtxCache );
ctxStartTU = m_CABACEstimator->getCtx();
currTU.jointCbCr = 0;
// Do predictions here to avoid repeating the "default0Save1Load2" stuff
// 在这进行预测,之后就不需要传输并修改default0Save1Load2值,不需要在xIntraCodingTUBlock函数中计算并保存预测值
int predMode = pu.cu->bdpcmModeChroma ? BDPCM_IDX : PU::getFinalIntraMode(pu, CHANNEL_TYPE_CHROMA);
// Cb和Cr的预测值
PelBuf piPredCb = cs.getPredBuf(cbArea);
PelBuf piPredCr = cs.getPredBuf(crArea);
initIntraPatternChType( *currTU.cu, cbArea);
initIntraPatternChType( *currTU.cu, crArea);
// 计算预测值
if( PU::isLMCMode( predMode ) ) //CCLM预测
{
xGetLumaRecPixels( pu, cbArea );
predIntraChromaLM( COMPONENT_Cb, piPredCb, pu, cbArea, predMode );
predIntraChromaLM( COMPONENT_Cr, piPredCr, pu, crArea, predMode );
}
else if (PU::isMIP(pu, CHANNEL_TYPE_CHROMA)) //YUV444格式色度采用MIP
{
initIntraMip(pu, cbArea);
predIntraMip(COMPONENT_Cb, piPredCb, pu);
initIntraMip(pu, crArea);
predIntraMip(COMPONENT_Cr, piPredCr, pu);
}
else
{
predIntraAng( COMPONENT_Cb, piPredCb, pu);
predIntraAng( COMPONENT_Cr, piPredCr, pu);
}
// determination of chroma residuals including reshaping and cross-component prediction
//----- get chroma residuals -----
PelBuf resiCb = cs.getResiBuf(cbArea);
PelBuf resiCr = cs.getResiBuf(crArea);
resiCb.copyFrom( cs.getOrgBuf (cbArea) );
resiCr.copyFrom( cs.getOrgBuf (crArea) );
// 计算原始残差(原始值-预测值)
resiCb.subtract( piPredCb );
resiCr.subtract( piPredCr );
//----- get reshape parameter ----
bool doReshaping = ( cs.slice->getLmcsEnabledFlag() && cs.picHeader->getLmcsChromaResidualScaleFlag()
&& (cs.slice->isIntra() || m_pcReshape->getCTUFlag()) && (cbArea.width * cbArea.height > 4) );
if( doReshaping )
{
const Area area = currTU.Y().valid() ? currTU.Y() : Area(recalcPosition(currTU.chromaFormat, currTU.chType, CHANNEL_TYPE_LUMA, currTU.blocks[currTU.chType].pos()), recalcSize(currTU.chromaFormat, currTU.chType, CHANNEL_TYPE_LUMA, currTU.blocks[currTU.chType].size()));
const CompArea &areaY = CompArea(COMPONENT_Y, currTU.chromaFormat, area);
int adj = m_pcReshape->calculateChromaAdjVpduNei(currTU, areaY);
currTU.setChromaAdj(adj);
}
//----- get cross component prediction parameters -----
//===== store original residual signals 保存原始残差信号=====
CompStorage orgResiCb[4], orgResiCr[4]; // 0:std, 1-3:jointCbCr (placeholder at this stage)
orgResiCb[0].create( cbArea );
orgResiCr[0].create( crArea );
orgResiCb[0].copyFrom( resiCb );
orgResiCr[0].copyFrom( resiCr );
if( doReshaping )
{
int cResScaleInv = currTU.getChromaAdj();
orgResiCb[0].scaleSignal( cResScaleInv, 1, currTU.cu->cs->slice->clpRng(COMPONENT_Cb) );
orgResiCr[0].scaleSignal( cResScaleInv, 1, currTU.cu->cs->slice->clpRng(COMPONENT_Cr) );
}
// 遍历Cb Cr,分别对Cb Cr单独进行编码
for( uint32_t c = COMPONENT_Cb; c < numTBlocks; c++)
{
const ComponentID compID = ComponentID(c);
const CompArea& area = currTU.blocks[compID];
double dSingleCost = MAX_DOUBLE;
int bestModeId = 0;
Distortion singleDistCTmp = 0;
double singleCostTmp = 0;
// 是否允许色度进行变换跳过
const bool tsAllowed = TU::isTSAllowed(currTU, compID) && m_pcEncCfg->getUseChromaTS() && !currTU.cu->lfnstIdx;
uint8_t nNumTransformCands = 1 + (tsAllowed ? 1 : 0); // DCT + TS = 2 tests
std::vector<TrMode> trModes;
if (m_pcEncCfg->getCostMode() == COST_LOSSLESS_CODING && slice.isLossless())
{
// 无损编码
nNumTransformCands = 1;
CHECK(!tsAllowed && !currTU.cu->bdpcmModeChroma, "transform skip should be enabled for LS");
if (currTU.cu->bdpcmModeChroma)
{
trModes.push_back(TrMode(0, true));
}
else
{
trModes.push_back(TrMode(1, true));
}
}
else
{
// 将DCT-2加入模式测试列表
trModes.push_back(TrMode(0, true)); // DCT2
if (tsAllowed)
{
// 将TS加入模式测试列表
trModes.push_back(TrMode(1, true)); // TS
}
}
CHECK(!currTU.Cb().valid(), "Invalid TU");
const int totalModesToTest = nNumTransformCands;
bool cbfDCT2 = true;
const bool isOneMode = false;
maxModesTested = totalModesToTest > maxModesTested ? totalModesToTest : maxModesTested;
int currModeId = 0;
int default0Save1Load2 = 0;
if (!isOneMode)
{
ctxStart = m_CABACEstimator->getCtx();
}
// 遍历变换类型
for (int modeId = 0; modeId < nNumTransformCands; modeId++)
{
// 复制原始残差信号
resiCb.copyFrom(orgResiCb[0]);
resiCr.copyFrom(orgResiCr[0]);
currTU.mtsIdx[compID] = currTU.cu->bdpcmModeChroma ? MTS_SKIP : trModes[modeId].first;//设置变换类型
currModeId++;
const bool isFirstMode = (currModeId == 1); //是否是第一个测试的模式
const bool isLastMode = false; // Always store output to saveCS and tmpTU 始终将输出存储到saveCS和tmpTU
if (!(m_pcEncCfg->getCostMode() == COST_LOSSLESS_CODING && slice.isLossless()))
{
// if DCT2's cbf==0, skip ts search 如果DCT-2变换的cbf为0,则跳过变换跳过模式
if (!cbfDCT2 && trModes[modeId].first == MTS_SKIP)
{
break;
}
if (!trModes[modeId].second)
{
continue;
}
}
if (!isFirstMode) // if not first mode to be tested
{
m_CABACEstimator->getCtx() = ctxStart;
}
singleDistCTmp = 0;
// 进行变换量化,并计算原始值和重建值的失真D,存在singleDistCTmp中
if (nNumTransformCands > 1)
{
xIntraCodingTUBlock(currTU, compID, singleDistCTmp, default0Save1Load2, nullptr,
modeId == 0 ? &trModes : nullptr, true);
}
else
{
xIntraCodingTUBlock(currTU, compID, singleDistCTmp, default0Save1Load2);
}
// 为了在cbf为零时不编码TS标志,禁止cbf为零的TS的情况。
if (((currTU.mtsIdx[compID] == MTS_SKIP && !currTU.cu->bdpcmModeChroma)
&& !TU::getCbf(currTU, compID))) // In order not to code TS flag when cbf is zero, the case for TS with
// cbf being zero is forbidden.
{
if (m_pcEncCfg->getCostMode() != COST_LOSSLESS_CODING || !slice.isLossless())
{
singleCostTmp = MAX_DOUBLE;
}
else
{
uint64_t fracBitsTmp = xGetIntraFracBitsQTChroma(currTU, compID);
singleCostTmp = m_pcRdCost->calcRdCost(fracBitsTmp, singleDistCTmp);
}
}
else if (lumaUsesISP && bestCostSoFar != MAX_DOUBLE && c == COMPONENT_Cb)
{
uint64_t fracBitsTmp = xGetIntraFracBitsQTSingleChromaComponent(cs, partitioner, ComponentID(c));
singleCostTmp = m_pcRdCost->calcRdCost(fracBitsTmp, singleDistCTmp);
if (isOneMode || (!isOneMode && !isLastMode))
{
m_CABACEstimator->getCtx() = ctxStart;
}
}
else if (!isOneMode)
{
uint64_t fracBitsTmp = xGetIntraFracBitsQTChroma(currTU, compID); //获得残差系数编码比特数
singleCostTmp = m_pcRdCost->calcRdCost(fracBitsTmp, singleDistCTmp);//计算RD Cost
}
if (singleCostTmp < dSingleCost)
{
dSingleCost = singleCostTmp; //单个分量的最佳Cost
bestModeId = currModeId;
if (c == COMPONENT_Cb)
{
bestCostCb = singleCostTmp;//最佳Cb变换模式对应的Cost
bestDistCb = singleDistCTmp;//原始值和重建值的失真D
}
else
{
bestCostCr = singleCostTmp;
bestDistCr = singleDistCTmp;
}
if (currTU.mtsIdx[compID] == MTS_DCT2_DCT2)
{
cbfDCT2 = TU::getCbfAtDepth(currTU, compID, currDepth);
}
if (!isLastMode)
{
#if KEEP_PRED_AND_RESI_SIGNALS
saveCS.getPredBuf(area).copyFrom(cs.getPredBuf(area));
saveCS.getOrgResiBuf(area).copyFrom(cs.getOrgResiBuf(area));
#endif
saveCS.getPredBuf(area).copyFrom(cs.getPredBuf(area));
if (keepResi)
{
saveCS.getResiBuf(area).copyFrom(cs.getResiBuf(area));
}
saveCS.getRecoBuf(area).copyFrom(cs.getRecoBuf(area));
tmpTU.copyComponentFrom(currTU, compID);
ctxBest = m_CABACEstimator->getCtx();
}
}
} // for ( modeId )
if( lumaUsesISP && dSingleCost > bestCostSoFar && c == COMPONENT_Cb )
{
//Luma + Cb cost is already larger than the best cost, so we don't need to test Cr
// Luma+Cb成本已经大于最佳成本,所以我们不需要测试Cr
cs.dist = MAX_UINT;
m_CABACEstimator->getCtx() = ctxStart;
earlyExitISP = true;
break;
//return cbfs;
}
// Done with one component of separate coding of Cr and Cb, just switch to the best Cb contexts if Cr coding is still to be done
// 使用Cr和Cb单独编码的一个分量完成,如果Cr编码仍然需要完成,只需切换到最佳Cb上下文即可
if ((c == COMPONENT_Cb && bestModeId < totalModesToTest) || (c == COMPONENT_Cb && m_pcEncCfg->getCostMode() == COST_LOSSLESS_CODING && slice.isLossless()))
{
m_CABACEstimator->getCtx() = ctxBest;
currTU.copyComponentFrom(tmpTU, COMPONENT_Cb); // Cbf of Cb is needed to estimate cost for Cr Cbf 需要Cb的Cbf来估算Cr-Cbf的成本
}
}
//测试JointCbCr模式
if ( !earlyExitISP )
{
// Test using joint chroma residual coding
// 联合色度残差编码测试
double bestCostCbCr = bestCostCb + bestCostCr;//设置最佳Cb Cr编码的cost
Distortion bestDistCbCr = bestDistCb + bestDistCr;// 设置最佳Cb Cr编码的失真
int bestJointCbCr = 0;
std::vector<int> jointCbfMasksToTest;
if ( cs.sps->getJointCbCrEnabledFlag() && (TU::getCbf(tmpTU, COMPONENT_Cb) || TU::getCbf(tmpTU, COMPONENT_Cr)))
{
jointCbfMasksToTest = m_pcTrQuant->selectICTCandidates(currTU, orgResiCb, orgResiCr);
}
// 仅测试DCT-2变换
bool checkDCTOnly = (TU::getCbf(tmpTU, COMPONENT_Cb) && tmpTU.mtsIdx[COMPONENT_Cb] == MTS_DCT2_DCT2 && !TU::getCbf(tmpTU, COMPONENT_Cr)) ||
(TU::getCbf(tmpTU, COMPONENT_Cr) && tmpTU.mtsIdx[COMPONENT_Cr] == MTS_DCT2_DCT2 && !TU::getCbf(tmpTU, COMPONENT_Cb)) ||
(TU::getCbf(tmpTU, COMPONENT_Cb) && tmpTU.mtsIdx[COMPONENT_Cb] == MTS_DCT2_DCT2 && TU::getCbf(tmpTU, COMPONENT_Cr) && tmpTU.mtsIdx[COMPONENT_Cr] == MTS_DCT2_DCT2);
// 仅测试TransformSkip变换
bool checkTSOnly = (TU::getCbf(tmpTU, COMPONENT_Cb) && tmpTU.mtsIdx[COMPONENT_Cb] == MTS_SKIP && !TU::getCbf(tmpTU, COMPONENT_Cr)) ||
(TU::getCbf(tmpTU, COMPONENT_Cr) && tmpTU.mtsIdx[COMPONENT_Cr] == MTS_SKIP && !TU::getCbf(tmpTU, COMPONENT_Cb)) ||
(TU::getCbf(tmpTU, COMPONENT_Cb) && tmpTU.mtsIdx[COMPONENT_Cb] == MTS_SKIP && TU::getCbf(tmpTU, COMPONENT_Cr) && tmpTU.mtsIdx[COMPONENT_Cr] == MTS_SKIP);
if (jointCbfMasksToTest.size() && currTU.cu->bdpcmModeChroma)
{
CHECK(!checkTSOnly || checkDCTOnly, "bdpcm only allows transform skip");
}
for( int cbfMask : jointCbfMasksToTest ) //遍历需要测试的JointCbCr模式
{
currTU.jointCbCr = (uint8_t)cbfMask;
ComponentID codeCompId = ((currTU.jointCbCr >> 1) ? COMPONENT_Cb : COMPONENT_Cr);
ComponentID otherCompId = ((codeCompId == COMPONENT_Cb) ? COMPONENT_Cr : COMPONENT_Cb);
bool tsAllowed = TU::isTSAllowed(currTU, codeCompId) && (m_pcEncCfg->getUseChromaTS()) && !currTU.cu->lfnstIdx;
uint8_t numTransformCands = 1 + (tsAllowed ? 1 : 0); // DCT + TS = 2 tests
bool cbfDCT2 = true;
std::vector<TrMode> trModes;
if (checkDCTOnly || checkTSOnly)
{
numTransformCands = 1;
}
if (!checkTSOnly || currTU.cu->bdpcmModeChroma)
{
trModes.push_back(TrMode(0, true)); // DCT2
}
if (tsAllowed && !checkDCTOnly)
{
trModes.push_back(TrMode(1, true));//TS
}
for (int modeId = 0; modeId < numTransformCands; modeId++)
{
if (modeId && !cbfDCT2)
{
continue;
}
if (!trModes[modeId].second)
{
continue;
}
Distortion distTmp = 0;
currTU.mtsIdx[codeCompId] = currTU.cu->bdpcmModeChroma ? MTS_SKIP : trModes[modeId].first;
currTU.mtsIdx[otherCompId] = MTS_DCT2_DCT2;
m_CABACEstimator->getCtx() = ctxStartTU;
resiCb.copyFrom(orgResiCb[cbfMask]);
resiCr.copyFrom(orgResiCr[cbfMask]);
if (numTransformCands > 1)
{
xIntraCodingTUBlock(currTU, COMPONENT_Cb, distTmp, 0, nullptr, modeId == 0 ? &trModes : nullptr, true);
}
else
{
xIntraCodingTUBlock(currTU, COMPONENT_Cb, distTmp, 0);
}
double costTmp = std::numeric_limits<double>::max();
if (distTmp < std::numeric_limits<Distortion>::max())
{
uint64_t bits = xGetIntraFracBitsQTChroma(currTU, COMPONENT_Cb);
costTmp = m_pcRdCost->calcRdCost(bits, distTmp);
if (!currTU.mtsIdx[codeCompId])
{
cbfDCT2 = true;
}
}
else if (!currTU.mtsIdx[codeCompId])
{
cbfDCT2 = false;
}
if (costTmp < bestCostCbCr)
{
bestCostCbCr = costTmp;
bestDistCbCr = distTmp;
bestJointCbCr = currTU.jointCbCr;
// store data
{
#if KEEP_PRED_AND_RESI_SIGNALS
saveCS.getOrgResiBuf(cbArea).copyFrom(cs.getOrgResiBuf(cbArea));
saveCS.getOrgResiBuf(crArea).copyFrom(cs.getOrgResiBuf(crArea));
#endif
saveCS.getPredBuf(cbArea).copyFrom(cs.getPredBuf(cbArea));
saveCS.getPredBuf(crArea).copyFrom(cs.getPredBuf(crArea));
if (keepResi)
{
saveCS.getResiBuf(cbArea).copyFrom(cs.getResiBuf(cbArea));
saveCS.getResiBuf(crArea).copyFrom(cs.getResiBuf(crArea));
}
saveCS.getRecoBuf(cbArea).copyFrom(cs.getRecoBuf(cbArea));
saveCS.getRecoBuf(crArea).copyFrom(cs.getRecoBuf(crArea));
tmpTU.copyComponentFrom(currTU, COMPONENT_Cb);
tmpTU.copyComponentFrom(currTU, COMPONENT_Cr);
ctxBest = m_CABACEstimator->getCtx();
}
}
}
}
// Retrieve the best CU data (unless it was the very last one tested) 检索最佳CU数据(除非它是最后一个测试的数据)
{
#if KEEP_PRED_AND_RESI_SIGNALS
cs.getPredBuf (cbArea).copyFrom(saveCS.getPredBuf (cbArea));
cs.getOrgResiBuf(cbArea).copyFrom(saveCS.getOrgResiBuf(cbArea));
cs.getPredBuf (crArea).copyFrom(saveCS.getPredBuf (crArea));
cs.getOrgResiBuf(crArea).copyFrom(saveCS.getOrgResiBuf(crArea));
#endif
cs.getPredBuf (cbArea).copyFrom(saveCS.getPredBuf (cbArea));
cs.getPredBuf (crArea).copyFrom(saveCS.getPredBuf (crArea));
if( keepResi )
{
cs.getResiBuf (cbArea).copyFrom(saveCS.getResiBuf (cbArea));
cs.getResiBuf (crArea).copyFrom(saveCS.getResiBuf (crArea));
}
cs.getRecoBuf (cbArea).copyFrom(saveCS.getRecoBuf (cbArea));
cs.getRecoBuf (crArea).copyFrom(saveCS.getRecoBuf (crArea));
currTU.copyComponentFrom(tmpTU, COMPONENT_Cb);
currTU.copyComponentFrom(tmpTU, COMPONENT_Cr);
m_CABACEstimator->getCtx() = ctxBest;
}
// Copy results to the picture structures
cs.picture->getRecoBuf(cbArea).copyFrom(cs.getRecoBuf(cbArea));
cs.picture->getRecoBuf(crArea).copyFrom(cs.getRecoBuf(crArea));
cs.picture->getPredBuf(cbArea).copyFrom(cs.getPredBuf(cbArea));
cs.picture->getPredBuf(crArea).copyFrom(cs.getPredBuf(crArea));
cbfs.cbf(COMPONENT_Cb) = TU::getCbf(currTU, COMPONENT_Cb);
cbfs.cbf(COMPONENT_Cr) = TU::getCbf(currTU, COMPONENT_Cr);
currTU.jointCbCr = ( (cbfs.cbf(COMPONENT_Cb) + cbfs.cbf(COMPONENT_Cr)) ? bestJointCbCr : 0 );
cs.dist += bestDistCbCr;
}
}
else
{
// 仅ISP模式(仅单树下才会进入,双树亮度色度单独划分)或者CU尺寸大于最大变换尺寸时,会进入该部分,对TU进行进一步划分
unsigned numValidTBlocks = ::getNumberValidTBlocks( *cs.pcv );
ChromaCbfs SplitCbfs ( false );
if( partitioner.canSplit( TU_MAX_TR_SPLIT, cs ) ) //若超过最大TU尺寸,需要进一步划分
{
partitioner.splitCurrArea( TU_MAX_TR_SPLIT, cs );
}
else if( currTU.cu->ispMode ) //ISP模式(仅亮度色度共同划分时才会进入)
{
partitioner.splitCurrArea( ispType, cs );
}
else
{
THROW( "Implicit TU split not available" );
}
do
{
ChromaCbfs subCbfs = xRecurIntraChromaCodingQT( cs, partitioner, bestCostSoFar, ispType );
for( uint32_t ch = COMPONENT_Cb; ch < numValidTBlocks; ch++ )
{
const ComponentID compID = ComponentID( ch );
SplitCbfs.cbf( compID ) |= subCbfs.cbf( compID );
}
} while( partitioner.nextPart( cs ) );
partitioner.exitCurrSplit();
if( lumaUsesISP && cs.dist == MAX_UINT )
{
return cbfs;
}
cbfs.Cb |= SplitCbfs.Cb;
cbfs.Cr |= SplitCbfs.Cr;
if (!lumaUsesISP)
{
for (auto &ptu: cs.tus)
{
if (currArea.Cb().contains(ptu->Cb()) || (!ptu->Cb().valid() && currArea.Y().contains(ptu->Y())))
{
TU::setCbfAtDepth(*ptu, COMPONENT_Cb, currDepth, SplitCbfs.Cb);
TU::setCbfAtDepth(*ptu, COMPONENT_Cr, currDepth, SplitCbfs.Cr);
}
}
}
}
return cbfs;
}