Coverage Report

Created: 2026-04-01 07:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vvenc/source/Lib/CommonLib/UnitPartitioner.cpp
Line
Count
Source
1
/* -----------------------------------------------------------------------------
2
The copyright in this software is being made available under the Clear BSD
3
License, included below. No patent rights, trademark rights and/or 
4
other Intellectual Property Rights other than the copyrights concerning 
5
the Software are granted under this license.
6
7
The Clear BSD License
8
9
Copyright (c) 2019-2026, Fraunhofer-Gesellschaft zur Förderung der angewandten Forschung e.V. & The VVenC Authors.
10
All rights reserved.
11
12
Redistribution and use in source and binary forms, with or without modification,
13
are permitted (subject to the limitations in the disclaimer below) provided that
14
the following conditions are met:
15
16
     * Redistributions of source code must retain the above copyright notice,
17
     this list of conditions and the following disclaimer.
18
19
     * Redistributions in binary form must reproduce the above copyright
20
     notice, this list of conditions and the following disclaimer in the
21
     documentation and/or other materials provided with the distribution.
22
23
     * Neither the name of the copyright holder nor the names of its
24
     contributors may be used to endorse or promote products derived from this
25
     software without specific prior written permission.
26
27
NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY
28
THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
29
CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
30
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
31
PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
32
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
33
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
34
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
35
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
36
IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
37
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
38
POSSIBILITY OF SUCH DAMAGE.
39
40
41
------------------------------------------------------------------------------------------- */
42
43
44
/** \file     UnitPartitioner.h
45
 *  \brief    Provides a class for partitioning management
46
 */
47
48
#include "UnitPartitioner.h"
49
#include "CodingStructure.h"
50
#include "Unit.h"
51
#include "Slice.h"
52
#include "UnitTools.h"
53
#include "Picture.h"
54
55
//! \ingroup CommonLib
56
//! \{
57
58
namespace vvenc {
59
60
PartLevel::PartLevel()
61
0
: split               ( CU_DONT_SPLIT )
62
, parts               (               )
63
0
, idx                 ( 0u            )
64
0
, checkdIfImplicit    ( false         )
65
0
, isImplicit          ( false         )
66
0
, implicitSplit       ( CU_DONT_SPLIT )
67
0
, firstSubPartSplit   ( CU_DONT_SPLIT )
68
0
, canQtSplit          ( true          )
69
0
, qgEnable            ( true          )
70
0
, qgChromaEnable      ( true          )
71
0
, modeType            ( MODE_TYPE_ALL )
72
0
{
73
0
}
74
75
void PartLevel::init()
76
0
{
77
0
  split               = CU_DONT_SPLIT;
78
0
  idx                 = 0u;
79
0
  checkdIfImplicit    = false;
80
0
  isImplicit          = false;
81
0
  implicitSplit       = CU_DONT_SPLIT;
82
0
  firstSubPartSplit   = CU_DONT_SPLIT;
83
0
  canQtSplit          = true;
84
0
  qgEnable            = true;
85
0
  qgChromaEnable      = true;
86
0
  modeType            = MODE_TYPE_ALL;
87
0
  numParts            = 0;
88
0
}
89
90
//////////////////////////////////////////////////////////////////////////
91
// Partitioner class
92
//////////////////////////////////////////////////////////////////////////
93
94
SplitSeries Partitioner::getSplitSeries() const
95
0
{
96
0
  SplitSeries splitSeries = 0;
97
0
  SplitSeries depth = 0;
98
99
0
  for( const auto &level : m_partStack )
100
0
  {
101
0
    if( level.split == CTU_LEVEL ) continue;
102
0
    else splitSeries += static_cast< SplitSeries >( level.split ) << ( depth * SPLIT_DMULT );
103
104
0
    depth++;
105
0
  }
106
107
0
  return splitSeries;
108
0
}
109
110
ModeTypeSeries Partitioner::getModeTypeSeries() const
111
0
{
112
0
  ModeTypeSeries modeTypeSeries = 0;
113
0
  int depth = 0;
114
115
0
  for( const auto &level : m_partStack )
116
0
  {
117
0
    if( level.split == CTU_LEVEL ) continue;
118
0
    else modeTypeSeries += static_cast<int>(level.modeType) << (depth * 3);
119
120
0
    depth++;
121
0
  }
122
123
0
  return modeTypeSeries;
124
0
}
125
126
bool Partitioner::isSepTree( const CodingStructure &cs )
127
0
{
128
0
  return treeType != TREE_D || CS::isDualITree( cs );
129
0
}
130
131
void Partitioner::setCUData( CodingUnit& cu )
132
0
{
133
0
  cu.depth       = currDepth;
134
0
  cu.btDepth     = currBtDepth;
135
0
  cu.mtDepth     = currMtDepth;
136
0
  cu.qtDepth     = currQtDepth;
137
0
  cu.splitSeries = getSplitSeries();
138
0
  cu.modeTypeSeries = getModeTypeSeries();
139
0
  cu.treeType    = treeType; 
140
0
  cu.modeType    = modeType; 
141
142
0
}
143
144
void Partitioner::copyState( const Partitioner& other )
145
0
{
146
0
  m_partStack = other.m_partStack;
147
0
  currBtDepth = other.currBtDepth;
148
0
  currQtDepth = other.currQtDepth;
149
0
  currDepth   = other.currDepth;
150
0
  currMtDepth = other.currMtDepth;
151
0
  currTrDepth = other.currTrDepth;
152
0
  currSubdiv  = other.currSubdiv;
153
0
  currQgPos   = other.currQgPos;
154
0
  currQgChromaPos = other.currQgChromaPos;
155
0
  currImplicitBtDepth
156
0
              = other.currImplicitBtDepth;
157
0
  chType      = other.chType;
158
0
#ifdef _DEBUG
159
0
  m_currArea  = other.m_currArea;
160
0
#endif
161
0
}
162
163
void Partitioner::setMaxMinDepth( unsigned& minDepth, unsigned& maxDepth, const CodingStructure& cs, int QtbttSpeedUp, bool MergeFlag) const
164
0
{
165
0
  unsigned          stdMinDepth = 0;
166
0
  unsigned          stdMaxDepth = cs.pcv->getMaxDepth( cs.slice->sliceType, chType );
167
0
  const Position    pos         = currArea().blocks[chType].pos();
168
0
  const unsigned    curSliceIdx = cs.slice->independentSliceIdx;
169
0
  const unsigned    curTileIdx  = cs.pps->getTileIdx( currArea().lumaPos() );
170
171
0
  const CodingUnit* cuLeft        = cs.getCURestricted( pos.offset( -1,                               0 ), pos, curSliceIdx, curTileIdx, chType, treeType );
172
0
  const CodingUnit* cuBelowLeft   = cs.getCURestricted( pos.offset( -1, currArea().blocks[chType].height), pos, curSliceIdx, curTileIdx, chType, treeType );
173
0
  const CodingUnit* cuAbove       = cs.getCURestricted( pos.offset(  0,                              -1 ), pos, curSliceIdx, curTileIdx, chType, treeType );
174
0
  const CodingUnit* cuAboveRight  = cs.getCURestricted( pos.offset( currArea().blocks[chType].width, -1 ), pos, curSliceIdx, curTileIdx, chType, treeType );
175
176
0
  minDepth = stdMaxDepth;
177
0
  maxDepth = stdMinDepth;
178
179
0
  if( cuLeft )
180
0
  {
181
0
    minDepth = std::min<unsigned>( minDepth, cuLeft->qtDepth );
182
0
    maxDepth = std::max<unsigned>( maxDepth, cuLeft->qtDepth );
183
0
  }
184
0
  else
185
0
  {
186
0
    minDepth = stdMinDepth;
187
0
    maxDepth = stdMaxDepth;
188
0
  }
189
190
0
  if( cuBelowLeft )
191
0
  {
192
0
    minDepth = std::min<unsigned>( minDepth, cuBelowLeft->qtDepth );
193
0
    maxDepth = std::max<unsigned>( maxDepth, cuBelowLeft->qtDepth );
194
0
  }
195
0
  else
196
0
  {
197
0
    minDepth = stdMinDepth;
198
0
    maxDepth = stdMaxDepth;
199
0
  }
200
201
0
  if( cuAbove )
202
0
  {
203
0
    minDepth = std::min<unsigned>( minDepth, cuAbove->qtDepth );
204
0
    maxDepth = std::max<unsigned>( maxDepth, cuAbove->qtDepth );
205
0
  }
206
0
  else
207
0
  {
208
0
    minDepth = stdMinDepth;
209
0
    maxDepth = stdMaxDepth;
210
0
  }
211
212
0
  if( cuAboveRight )
213
0
  {
214
0
    minDepth = std::min<unsigned>( minDepth, cuAboveRight->qtDepth );
215
0
    maxDepth = std::max<unsigned>( maxDepth, cuAboveRight->qtDepth );
216
0
  }
217
0
  else
218
0
  {
219
0
    minDepth = stdMinDepth;
220
0
    maxDepth = stdMaxDepth;
221
0
  }
222
223
0
  minDepth = ( minDepth >= 1 ? minDepth - 1 : 0 );
224
0
  maxDepth = std::min<unsigned>( stdMaxDepth, maxDepth + 1 );
225
0
  if((QtbttSpeedUp >> 2) && (cs.slice->TLayer > 0) && ((cs.area.Y().width >= 8) || (cs.area.Y().height >= 8)))
226
0
  {
227
0
    int minDepthCur = stdMaxDepth;
228
0
    int maxDepthCur = stdMinDepth;
229
0
    int amountN = 0;
230
0
    for (int n = 0; n < 3; n++)
231
0
    {
232
0
      const CodingUnit* cuNeigh = (n==0)?cs.getCURestricted(pos.offset(-1, -1), pos, curSliceIdx, curTileIdx, chType, treeType): (n==1)? cuAbove : cuLeft;
233
0
      if (cuNeigh)
234
0
      {
235
0
        amountN++;
236
0
        minDepthCur = std::min<unsigned>(minDepthCur, cuNeigh->qtDepth);
237
0
        maxDepthCur = std::max<unsigned>(maxDepthCur, cuNeigh->qtDepth);
238
0
      }
239
0
    }
240
0
    if (amountN)
241
0
    {
242
0
      minDepthCur = (minDepthCur >= 1 ? minDepthCur - 1 : 0);
243
0
      maxDepthCur = std::min<unsigned>(stdMaxDepth, maxDepthCur + 1);
244
0
      maxDepth = std::min<unsigned>(maxDepthCur, maxDepth);
245
0
      minDepth = std::max<unsigned>(minDepthCur, minDepth);
246
0
    }
247
0
  }
248
249
0
  if (!cs.slice->isIntra() && (QtbttSpeedUp & 3))
250
0
  {
251
0
    bool doMin_SCC = !(((QtbttSpeedUp & 3) == 2) && (cs.area.Y().width < cs.pcv->maxCUSize));
252
0
    bool LimitDepths = (QtbttSpeedUp & 2) ? (MergeFlag == 0) : (cs.area.Y().width >= cs.pcv->maxCUSize);
253
0
    if (LimitDepths && cuAbove && cuLeft && (cuLeft->qtDepth == cuAbove->qtDepth))
254
0
    {
255
0
      int minDepthCur = cuAbove->qtDepth;
256
0
      int maxDepthCur = cuAbove->qtDepth;
257
0
      minDepthCur = (minDepthCur > 0) ? (minDepthCur - 1) : 0;
258
0
      maxDepthCur = (maxDepthCur < stdMaxDepth) ? (maxDepthCur + 1) : maxDepthCur;
259
0
      maxDepth = std::min<unsigned>(maxDepthCur, maxDepth);
260
0
      minDepth = std::max<unsigned>(minDepthCur, minDepth);
261
0
    }
262
0
    else if (doMin_SCC && LimitDepths && cuAbove && cuLeft)
263
0
    {
264
0
      int minDepthCur = cuAbove->qtDepth;
265
0
      int maxDepthCur = cuAbove->qtDepth;
266
0
      minDepthCur = (minDepthCur > 0) ? (minDepthCur - 1) : 0;
267
0
      maxDepthCur = (maxDepthCur < stdMaxDepth) ? (maxDepthCur + 1) : maxDepthCur;
268
0
      if ((cuLeft->qtDepth > maxDepthCur) && ((cuLeft->qtDepth - 1) >= maxDepthCur))
269
0
      {
270
0
        minDepthCur += 1;
271
0
        maxDepthCur += 1;
272
0
      }
273
0
      else if ((cuLeft->qtDepth < minDepthCur) && ((cuLeft->qtDepth + 1) <= minDepthCur))
274
0
      {
275
0
        maxDepthCur -= 1;
276
0
        minDepthCur -= 1;
277
0
      }
278
0
      maxDepth = std::min<unsigned>(maxDepthCur, maxDepth);
279
0
      minDepth = std::max<unsigned>(minDepthCur, minDepth);
280
0
    }
281
0
    if ((QtbttSpeedUp & 2) && MergeFlag && (maxDepth == 4) && (cs.area.Y().width <= 16))
282
0
    {
283
0
      maxDepth = 3;
284
0
      minDepth = (minDepth == 3)? 2: minDepth;
285
0
    }
286
0
  }
287
0
}
288
289
void Partitioner::initCtu( const UnitArea& ctuArea, const ChannelType _chType, const Slice& slice )
290
0
{
291
0
#if _DEBUG
292
0
  m_currArea = ctuArea;
293
0
#endif
294
0
  currDepth   = 0;
295
0
  currTrDepth = 0;
296
0
  currBtDepth = 0;
297
0
  currMtDepth = 0;
298
0
  currQtDepth = 0;
299
0
  currSubdiv  = 0;
300
0
  currQgPos   = ctuArea.lumaPos();
301
0
  currQgChromaPos = ctuArea.chromaFormat != CHROMA_400 ? ctuArea.chromaPos() : Position();
302
0
  currImplicitBtDepth = 0;
303
0
  chType      = _chType;
304
305
0
  const PreCalcValues& pcv = *slice.pps->pcv;
306
  
307
0
  maxBTD      = pcv.getMaxMTTDepth( slice, chType );
308
0
  maxBtSize   = pcv.getMaxBtSize  ( slice, chType );
309
0
  minTSize    = pcv.getMinTSize   ( slice, chType );
310
0
  maxTtSize   = pcv.getMaxTtSize  ( slice, chType );
311
0
  minQtSize   = pcv.getMinQtSize  ( slice, chType );
312
  
313
0
  m_partBufIdx = 1;
314
0
  m_partStack.resize_noinit( 1 );
315
0
  m_partStack.back().init();
316
0
  m_partStack.back().split = CTU_LEVEL;
317
0
  m_partStack.back().parts = m_partBuf;
318
0
  m_partStack.back().parts[0] = ctuArea;
319
0
  m_partStack.back().numParts = 1;
320
321
0
  treeType = TREE_D;
322
0
  modeType = MODE_TYPE_ALL;
323
0
}
324
325
void Partitioner::splitCurrArea( const PartSplit split, const CodingStructure& cs )
326
0
{
327
0
  if ((split != TU_1D_HORZ_SPLIT) && (split != TU_1D_VERT_SPLIT))
328
0
  {
329
0
    CHECKD(!canSplit(split, cs), "Trying to apply a prohibited split!");
330
0
  }
331
332
0
  bool isImplicit = isSplitImplicit( split, cs );
333
0
  bool canQtSplit = canSplit( CU_QUAD_SPLIT, cs );
334
0
  bool qgEnable = currQgEnable();
335
0
  bool qgChromaEnable = currQgChromaEnable();
336
337
0
  const UnitArea& area = currArea();
338
0
  m_partStack.resize_noinit( m_partStack.size() + 1 );
339
0
  PartLevel& back = m_partStack.back();
340
0
  back.init();
341
0
  back.split = split;
342
0
  back.parts = &m_partBuf[m_partBufIdx];
343
0
  int numParts;
344
345
0
  CHECK( m_partBufIdx > partBufSize, "Partition buffer overflow" );
346
347
0
  switch( split )
348
0
  {
349
0
  case CU_QUAD_SPLIT:
350
0
    numParts = PartitionerImpl::getCUSubPartitions( back.parts, area, cs, split );
351
0
    back.modeType = modeType;
352
0
    break;
353
0
  case CU_HORZ_SPLIT:
354
0
  case CU_VERT_SPLIT:
355
0
    numParts = PartitionerImpl::getCUSubPartitions( back.parts, area, cs, split );
356
0
    back.modeType = modeType;
357
0
    break;
358
0
  case CU_TRIH_SPLIT:
359
0
  case CU_TRIV_SPLIT:
360
0
    numParts = PartitionerImpl::getCUSubPartitions( back.parts, area, cs, split );
361
0
    back.modeType = modeType;
362
0
    break;
363
0
  case TU_MAX_TR_SPLIT:
364
0
    numParts = PartitionerImpl::getMaxTuTiling( back.parts, area, cs );
365
0
    break;
366
0
  case SBT_VER_HALF_POS0_SPLIT:
367
0
  case SBT_VER_HALF_POS1_SPLIT:
368
0
  case SBT_HOR_HALF_POS0_SPLIT:
369
0
  case SBT_HOR_HALF_POS1_SPLIT:
370
0
  case SBT_VER_QUAD_POS0_SPLIT:
371
0
  case SBT_VER_QUAD_POS1_SPLIT:
372
0
  case SBT_HOR_QUAD_POS0_SPLIT:
373
0
  case SBT_HOR_QUAD_POS1_SPLIT:
374
0
    numParts = PartitionerImpl::getSbtTuTiling( back.parts, area, cs, split );
375
0
    break;
376
0
  case TU_1D_HORZ_SPLIT:
377
0
  case TU_1D_VERT_SPLIT:
378
0
  {
379
0
    numParts = PartitionerImpl::getTUIntraSubPartitions(back.parts, area, cs, split, TREE_D);
380
0
    break;
381
0
  }
382
0
  default:
383
0
    THROW( "Unknown split mode" );
384
0
    break;
385
0
  }
386
387
0
  back.numParts = numParts;
388
0
  m_partBufIdx += numParts;
389
390
0
  CHECK( m_partBufIdx > partBufSize, "Partition buffer overflow" );
391
392
0
  currDepth++;
393
0
  currSubdiv++;
394
0
#if _DEBUG
395
0
  m_currArea = m_partStack.back().parts[0];
396
0
#endif
397
398
0
  if ((split == TU_MAX_TR_SPLIT) || (split == TU_1D_HORZ_SPLIT) || (split == TU_1D_VERT_SPLIT))
399
0
  {
400
0
    currTrDepth++;
401
0
  }
402
0
  else if( split >= SBT_VER_HALF_POS0_SPLIT && split <= SBT_HOR_QUAD_POS1_SPLIT )
403
0
  {
404
0
    currTrDepth++;
405
0
  }
406
0
  else
407
0
  {
408
0
    currTrDepth = 0;
409
0
  }
410
411
0
  if( split == CU_HORZ_SPLIT || split == CU_VERT_SPLIT || split == CU_TRIH_SPLIT || split == CU_TRIV_SPLIT )
412
0
  {
413
0
    currBtDepth++;
414
0
    if( isImplicit ) currImplicitBtDepth++;
415
0
    currMtDepth++;
416
417
0
    if( split == CU_TRIH_SPLIT || split == CU_TRIV_SPLIT )
418
0
    {
419
      // first and last part of triple split are equivalent to double bt split
420
0
      currBtDepth++;
421
0
      currSubdiv++;
422
0
    }
423
0
    m_partStack.back().canQtSplit = canQtSplit;
424
0
  }
425
0
  else if( split == CU_QUAD_SPLIT )
426
0
  {
427
0
    CHECK( currBtDepth > 0, "Cannot split a non-square area other than with a binary split" );
428
0
    CHECK( currMtDepth > 0, "Cannot split a non-square area other than with a binary split" );
429
0
    currMtDepth = 0;
430
0
    currBtDepth = 0;
431
0
    currQtDepth++;
432
0
    currSubdiv++;
433
0
  }
434
435
0
  qgEnable       &= (currSubdiv <= (cs.slice->isIntra() ? cs.slice->picHeader->cuQpDeltaSubdivIntra : cs.slice->picHeader->cuQpDeltaSubdivInter ));
436
0
  qgChromaEnable &= (currSubdiv <= (cs.slice->isIntra() ? cs.slice->picHeader->cuChromaQpOffsetSubdivIntra : cs.slice->picHeader->cuChromaQpOffsetSubdivInter ));
437
0
  m_partStack.back().qgEnable       = qgEnable;
438
0
  m_partStack.back().qgChromaEnable = qgChromaEnable;
439
0
  if (qgEnable)
440
0
    currQgPos = currArea().lumaPos();
441
0
  if (qgChromaEnable)
442
0
    currQgChromaPos = currArea().chromaPos();
443
0
}
444
445
void Partitioner::canSplit( const CodingStructure &cs, bool& canNo, bool& canQt, bool& canBh, bool& canBv, bool& canTh, bool& canTv )
446
0
{
447
0
  const PartSplit implicitSplit = m_partStack.back().checkdIfImplicit ? m_partStack.back().implicitSplit : getImplicitSplit( cs );
448
449
0
  canNo = canQt = canBh = canTh = canBv = canTv = true;
450
0
  bool canBtt = currMtDepth < (maxBTD + currImplicitBtDepth);
451
452
  // the minimal and maximal sizes are given in luma samples
453
0
  const CompArea&  area  = currArea().Y();
454
0
  const CompArea  *areaC = (chType == CH_C) ? &(currArea().Cb()) : nullptr;
455
0
        PartLevel& level = m_partStack.back();
456
457
0
  const PartSplit lastSplit = level.split;
458
0
  const PartSplit parlSplit = lastSplit == CU_TRIH_SPLIT ? CU_HORZ_SPLIT : CU_VERT_SPLIT;
459
460
  // don't allow QT-splitting below a BT split
461
0
  if( lastSplit != CTU_LEVEL && lastSplit != CU_QUAD_SPLIT ) canQt = false;
462
  // minQtSize is in luma samples unit
463
0
  const unsigned minQTThreshold = minQtSize >> ((area.chromaFormat == CHROMA_400) ? 0 : ((int) getChannelTypeScaleX(CH_C, area.chromaFormat) - (int) getChannelTypeScaleY(CH_C, area.chromaFormat)));
464
0
  if( area.width <= minQTThreshold )                         canQt = false;
465
0
  if( areaC && areaC->width <= MIN_DUALTREE_CHROMA_WIDTH ) canQt = false;
466
0
  if( treeType == TREE_C )
467
0
  {
468
0
    canQt = canBh = canTh = canBv = canTv = false;
469
0
    return;
470
0
  }
471
0
  if( implicitSplit != CU_DONT_SPLIT )
472
0
  {
473
0
    canNo = canTh = canTv = false;
474
475
0
    canBh = implicitSplit == CU_HORZ_SPLIT;
476
0
    canBv = implicitSplit == CU_VERT_SPLIT;
477
0
    if (areaC && areaC->width == 4) canBv = false;
478
0
    if( !canBh && !canBv && !canQt ) canQt = true;
479
0
    return;
480
0
  }
481
482
0
  if( ( lastSplit == CU_TRIH_SPLIT || lastSplit == CU_TRIV_SPLIT ) && currPartIdx() == 1 )
483
0
  {
484
0
    canBh = parlSplit != CU_HORZ_SPLIT;
485
0
    canBv = parlSplit != CU_VERT_SPLIT;
486
0
  }
487
488
0
  if( canBtt && ( area.width <= minTSize && area.height <= minTSize ) )
489
0
  {
490
0
    canBtt = false;
491
0
  }
492
0
  if( canBtt && ( area.width > maxBtSize || area.height > maxBtSize )
493
0
      && ( ( area.width > maxTtSize || area.height > maxTtSize ) ) )
494
0
  {
495
0
    canBtt = false;
496
0
  }
497
498
0
  if( !canBtt )
499
0
  {
500
0
    canBh = canTh = canBv = canTv = false;
501
502
0
    return;
503
0
  }
504
505
0
  if( area.width > maxBtSize || area.height > maxBtSize )
506
0
  {
507
0
    canBh = canBv = false;
508
0
  }
509
510
  // specific check for BT splits
511
0
  if( area.height <= minTSize )                            canBh = false;
512
0
  if( area.width > MAX_TB_SIZEY && area.height <= MAX_TB_SIZEY ) canBh = false;
513
0
  if( areaC && areaC->width * areaC->height <= MIN_DUALTREE_CHROMA_SIZE )     canBh = false;
514
0
  if( area.width <= minTSize )                              canBv = false;
515
0
  if( area.width <= MAX_TB_SIZEY && area.height > MAX_TB_SIZEY ) canBv = false;
516
0
  if (areaC && (areaC->width * areaC->height <= MIN_DUALTREE_CHROMA_SIZE || areaC->width == 4))     canBv = false;
517
0
  if( modeType == MODE_TYPE_INTER && area.width * area.height == 32 )  canBv = canBh = false;
518
0
  if( area.height <= 2 * minTSize || area.height > maxTtSize || area.width > maxTtSize )
519
0
                                                                                       canTh = false;
520
0
  if( area.width > MAX_TB_SIZEY || area.height > MAX_TB_SIZEY )  canTh = false;
521
0
  if( areaC && areaC->width * areaC->height <= MIN_DUALTREE_CHROMA_SIZE*2 )     canTh = false;
522
0
  if( area.width <= 2 * minTSize || area.width > maxTtSize || area.height > maxTtSize )
523
0
                                                                                       canTv = false;
524
0
  if( area.width > MAX_TB_SIZEY || area.height > MAX_TB_SIZEY )  canTv = false;
525
0
  if (areaC && (areaC->width * areaC->height <= MIN_DUALTREE_CHROMA_SIZE * 2 || areaC->width == 8))     canTv = false;
526
0
  if( modeType == MODE_TYPE_INTER && area.width * area.height == 64 )  canTv = canTh = false;
527
0
}
528
529
bool Partitioner::canSplit( const PartSplit split, const CodingStructure &cs )
530
0
{
531
0
  const CompArea area       = currArea().Y();
532
0
  const unsigned maxTrSize  = cs.sps->getMaxTbSize();
533
534
0
  bool canNo, canQt, canBh, canTh, canBv, canTv;
535
536
0
  canSplit( cs, canNo, canQt, canBh, canBv, canTh, canTv );
537
538
0
  switch( split )
539
0
  {
540
0
  case CTU_LEVEL:
541
0
    THROW( "Checking if top level split is possible" );
542
0
    return true;
543
0
    break;
544
0
  case TU_MAX_TR_SPLIT:
545
0
    return area.width > maxTrSize || area.height > maxTrSize;
546
0
    break;
547
0
  case SBT_VER_HALF_POS0_SPLIT:
548
0
  case SBT_VER_HALF_POS1_SPLIT:
549
0
  case SBT_HOR_HALF_POS0_SPLIT:
550
0
  case SBT_HOR_HALF_POS1_SPLIT:
551
0
  case SBT_VER_QUAD_POS0_SPLIT:
552
0
  case SBT_VER_QUAD_POS1_SPLIT:
553
0
  case SBT_HOR_QUAD_POS0_SPLIT:
554
0
  case SBT_HOR_QUAD_POS1_SPLIT:
555
0
    return currTrDepth == 0;
556
0
    break;
557
0
  case CU_QUAD_SPLIT:
558
0
    return canQt;
559
0
  case CU_DONT_SPLIT:
560
0
    return canNo;
561
0
  case CU_HORZ_SPLIT:
562
0
    return canBh;
563
0
  case CU_VERT_SPLIT:
564
0
    return canBv;
565
0
  case CU_TRIH_SPLIT:
566
0
    return canTh;
567
0
  case CU_TRIV_SPLIT:
568
0
    return canTv;
569
0
  case CU_MT_SPLIT:
570
0
    return ( canBh || canTh || canBv || canTv );
571
0
  case CU_BT_SPLIT:
572
0
    return ( canBh || canBv );
573
0
  break;
574
0
  default:
575
0
    THROW( "Unknown split mode" );
576
0
    return false;
577
0
    break;
578
0
  }
579
580
0
  return true;
581
0
}
582
583
bool Partitioner::canSplitISP(const PartSplit split, const CodingStructure& cs, CodingUnit& cu)
584
0
{
585
  // const PartSplit implicitSplit = getImplicitSplit(cs);
586
0
  const UnitArea& area = currArea();
587
588
0
  switch (split)
589
0
  {
590
0
  case TU_1D_HORZ_SPLIT:
591
0
  {
592
0
    return area.lheight() == cu.lheight();
593
0
  }
594
0
  case TU_1D_VERT_SPLIT:
595
0
  {
596
0
    return area.lwidth() == cu.lwidth();
597
0
  }
598
0
  case TU_MAX_TR_SPLIT:
599
0
  {
600
    // this split is performed implicitly with the other splits
601
0
    return false;
602
0
  }
603
0
  default: THROW("Unknown 1-D split mode"); break;
604
0
  }
605
0
}
606
607
bool Partitioner::isSplitImplicit( const PartSplit split, const CodingStructure &cs )
608
0
{
609
0
  return split == getImplicitSplit( cs );
610
0
}
611
612
PartSplit Partitioner::getImplicitSplit( const CodingStructure &cs )
613
0
{
614
0
  if( m_partStack.back().checkdIfImplicit )
615
0
  {
616
0
    return m_partStack.back().implicitSplit;
617
0
  }
618
619
0
  PartSplit split = CU_DONT_SPLIT;
620
621
0
  if( split == CU_DONT_SPLIT )
622
0
  {
623
0
    const bool isBlInPic = cs.picture->Y().contains( currArea().Y().bottomLeft() );
624
0
    const bool isTrInPic = cs.picture->Y().contains( currArea().Y().topRight() );
625
626
0
    const CompArea& area      = currArea().Y();
627
0
    const bool isBtAllowed    = area.width <= maxBtSize && area.height <= maxBtSize && currMtDepth < (maxBTD + currImplicitBtDepth);
628
    // minQtSize is in luma samples unit
629
0
    const unsigned minQTThreshold = minQtSize >> ((area.chromaFormat == CHROMA_400) ? 0 : ((int) getChannelTypeScaleX(CH_C, area.chromaFormat) - (int) getChannelTypeScaleY(CH_C, area.chromaFormat)));
630
0
    const bool isQtAllowed    = area.width > minQTThreshold && currBtDepth == 0;
631
632
0
    if( !isBlInPic && !isTrInPic && isQtAllowed )
633
0
    {
634
0
      split = CU_QUAD_SPLIT;
635
0
    }
636
0
    else if( !isBlInPic && isBtAllowed && area.width <= MAX_TB_SIZEY )
637
0
    {
638
0
      split = CU_HORZ_SPLIT;
639
0
    }
640
0
    else if( !isTrInPic && isBtAllowed && area.height <= MAX_TB_SIZEY )
641
0
    {
642
0
      split = CU_VERT_SPLIT;
643
0
    }
644
0
    else if( !isBlInPic || !isTrInPic )
645
0
    {
646
0
      split = CU_QUAD_SPLIT;
647
0
    }
648
0
    if (CS::isDualITree(cs) && (currArea().Y().width > 64 || currArea().Y().height > 64))
649
0
    {
650
0
      split = CU_QUAD_SPLIT;
651
0
    }
652
0
    if( (!isBlInPic || !isTrInPic) && split == CU_DONT_SPLIT )
653
0
    {
654
0
      split = CU_QUAD_SPLIT;
655
0
    }
656
0
  }
657
658
0
  m_partStack.back().checkdIfImplicit = true;
659
0
  m_partStack.back().isImplicit = split != CU_DONT_SPLIT;
660
0
  m_partStack.back().implicitSplit = split;
661
662
0
  return split;
663
0
}
664
665
void Partitioner::exitCurrSplit()
666
0
{
667
0
  const PartSplit currSplit = m_partStack.back().split;
668
0
  const int       currIndex = m_partStack.back().idx;
669
0
  const int       numParts  = m_partStack.back().numParts;
670
671
0
  m_partStack.pop_back();
672
0
  m_partBufIdx -= numParts;
673
674
0
  CHECK( currDepth == 0, "depth is '0', although a split was performed" );
675
0
  currDepth--;
676
0
  currSubdiv--;
677
0
  if( currQgEnable() )
678
0
    currQgPos = currArea().lumaPos();
679
0
  if( currArea().chromaFormat != CHROMA_400 && currQgChromaEnable() )
680
0
    currQgChromaPos = currArea().chromaPos();
681
0
#if _DEBUG
682
0
  m_currArea = m_partStack.back().parts[m_partStack.back().idx];
683
0
#endif
684
685
0
  if( currSplit == CU_HORZ_SPLIT || currSplit == CU_VERT_SPLIT || currSplit == CU_TRIH_SPLIT || currSplit == CU_TRIV_SPLIT )
686
0
  {
687
0
    CHECK( !m_partStack.back().checkdIfImplicit, "Didn't check if the current split is implicit" );
688
0
    CHECK( currBtDepth == 0, "BT depth is '0', athough a BT split was performed" );
689
0
    CHECK( currMtDepth == 0, "MT depth is '0', athough a BT split was performed" );
690
0
    currMtDepth--;
691
0
    if( m_partStack.back().isImplicit ) currImplicitBtDepth--;
692
0
    currBtDepth--;
693
0
    if( ( currSplit == CU_TRIH_SPLIT || currSplit == CU_TRIV_SPLIT ) && currIndex != 1 )
694
0
    {
695
0
      CHECK( currBtDepth == 0, "BT depth is '0', athough a TT split was performed" );
696
0
      currBtDepth--;
697
0
      currSubdiv--;
698
0
    }
699
0
  }
700
0
  else if( currSplit == TU_MAX_TR_SPLIT )
701
0
  {
702
0
    CHECK( currTrDepth == 0, "TR depth is '0', although a TU split was performed" );
703
0
    currTrDepth--;
704
0
  }
705
0
  else if( currSplit >= SBT_VER_HALF_POS0_SPLIT && currSplit <= SBT_HOR_QUAD_POS1_SPLIT )
706
0
  {
707
0
    CHECK( currTrDepth == 0, "TR depth is '0', although a TU split was performed" );
708
0
    currTrDepth--;
709
0
  }
710
0
  else if ((currSplit == TU_1D_HORZ_SPLIT) || (currSplit == TU_1D_VERT_SPLIT))
711
0
  {
712
0
    CHECK(currTrDepth == 0, "TR depth is '0', although a TU split was performed");
713
0
    currTrDepth--;
714
0
  }
715
0
  else
716
0
  {
717
0
    CHECK( currTrDepth > 0, "RQT found with QTBT partitioner" );
718
719
0
    CHECK( currQtDepth == 0, "QT depth is '0', although a QT split was performed" );
720
0
    currQtDepth--;
721
0
    currSubdiv--;
722
0
  }
723
0
}
724
725
bool Partitioner::nextPart( const CodingStructure &cs, bool autoPop /*= false*/ )
726
0
{
727
0
  const Position& prevPos = currArea().blocks[chType].pos();
728
729
0
  unsigned currIdx = ++m_partStack.back().idx;
730
731
0
  m_partStack.back().checkdIfImplicit = false;
732
0
  m_partStack.back().isImplicit = false;
733
734
0
  if( currIdx == 1 )
735
0
  {
736
0
    const CodingUnit* prevCU = cs.getCU( prevPos, chType, treeType );
737
0
    m_partStack.back().firstSubPartSplit = prevCU ? CU::getSplitAtDepth( *prevCU, currDepth ) : CU_DONT_SPLIT;
738
0
  }
739
740
0
  if( currIdx < m_partStack.back().numParts )
741
0
  {
742
0
    if( m_partStack.back().split == CU_TRIH_SPLIT || m_partStack.back().split == CU_TRIV_SPLIT )
743
0
    {
744
      // adapt the current bt depth
745
0
      if( currIdx == 1 ) currBtDepth--;
746
0
      else               currBtDepth++;
747
0
      if( currIdx == 1 ) currSubdiv--;
748
0
      else               currSubdiv++;
749
0
    }
750
0
  if( currQgEnable() )
751
0
    currQgPos = currArea().lumaPos();
752
0
  if( currQgChromaEnable() )
753
0
    currQgChromaPos = currArea().chromaPos();
754
0
#if _DEBUG
755
0
    m_currArea = m_partStack.back().parts[currIdx];
756
0
#endif
757
0
    return true;
758
0
  }
759
0
  else
760
0
  {
761
0
    if( autoPop ) exitCurrSplit();
762
0
    return false;
763
0
  }
764
0
}
765
766
bool Partitioner::hasNextPart()
767
0
{
768
0
  return ( ( m_partStack.back().idx + 1 ) < m_partStack.back().numParts );
769
0
}
770
771
//////////////////////////////////////////////////////////////////////////
772
// Partitioner methods describing the actual partitioning logic
773
//////////////////////////////////////////////////////////////////////////
774
775
int PartitionerImpl::getCUSubPartitions( Partitioning& dst, const UnitArea &cuArea, const CodingStructure &cs, const PartSplit _splitType )
776
0
{
777
0
  const PartSplit splitType = _splitType;
778
779
0
  if( splitType == CU_QUAD_SPLIT )
780
0
  {
781
0
    Partitioning& sub = dst;
782
783
0
    for( uint32_t i = 0; i < 4; i++ )
784
0
    {
785
0
      sub[i] = cuArea;
786
787
0
      for( auto &blk : sub[i].blocks )
788
0
      {
789
0
        blk.height >>= 1;
790
0
        blk.width  >>= 1;
791
0
        if( i >= 2 ) blk.y += blk.height;
792
0
        if( i &  1 ) blk.x += blk.width;
793
0
      }
794
0
    }
795
796
0
    return 4;
797
0
  }
798
0
  else if( splitType == CU_HORZ_SPLIT )
799
0
  {
800
0
    Partitioning& sub = dst;
801
802
0
    for (uint32_t i = 0; i < 2; i++)
803
0
    {
804
0
      sub[i] = cuArea;
805
806
0
      for (auto &blk : sub[i].blocks)
807
0
      {
808
0
        blk.height >>= 1;
809
0
        if (i == 1) blk.y += blk.height;
810
0
      }
811
0
    }
812
813
0
    return 2;
814
0
  }
815
0
  else if( splitType == CU_VERT_SPLIT )
816
0
  {
817
0
    Partitioning& sub = dst;
818
819
0
    for( uint32_t i = 0; i < 2; i++ )
820
0
    {
821
0
      sub[i] = cuArea;
822
823
0
      for( auto &blk : sub[i].blocks )
824
0
      {
825
0
        blk.width >>= 1;
826
0
        if( i == 1 ) blk.x += blk.width;
827
0
      }
828
0
    }
829
830
0
    return 2;
831
0
  }
832
0
  else if( splitType == CU_TRIH_SPLIT )
833
0
  {
834
0
    Partitioning& sub = dst;
835
836
0
    for( int i = 0; i < 3; i++ )
837
0
    {
838
0
      sub[i] = cuArea;
839
840
0
      for( auto &blk : sub[i].blocks )
841
0
      {
842
0
        blk.height >>= 1;
843
0
        if( ( i + 1 ) & 1 ) blk.height >>= 1;
844
0
        if( i == 1 )        blk.y       +=     blk.height / 2;
845
0
        if( i == 2 )        blk.y       += 3 * blk.height;
846
0
      }
847
0
    }
848
849
0
    return 3;
850
0
  }
851
0
  else if( splitType == CU_TRIV_SPLIT )
852
0
  {
853
0
    Partitioning& sub = dst;
854
855
0
    for( int i = 0; i < 3; i++ )
856
0
    {
857
0
      sub[i] = cuArea;
858
859
0
      for( auto &blk : sub[i].blocks )
860
0
      {
861
0
        blk.width >>= 1;
862
863
0
        if( ( i + 1 ) & 1 ) blk.width >>= 1;
864
0
        if( i == 1 )        blk.x      +=     blk.width / 2;
865
0
        if( i == 2 )        blk.x      += 3 * blk.width;
866
0
      }
867
0
    }
868
869
0
    return 3;
870
0
  }
871
0
  else
872
0
  {
873
0
    THROW( "Unknown CU sub-partitioning" );
874
0
  }
875
0
}
876
877
int PartitionerImpl::getTUIntraSubPartitions( Partitioning& sub, const UnitArea &tuArea, const CodingStructure &cs, const PartSplit splitType, const TreeType treeType )
878
0
{
879
0
  uint32_t nPartitions;
880
0
  uint32_t splitDimensionSize = CU::getISPSplitDim( tuArea.lumaSize().width, tuArea.lumaSize().height, splitType );
881
882
0
  bool isDualTree = CS::isDualITree( cs ) || treeType != TREE_D;
883
884
0
  if( splitType == TU_1D_HORZ_SPLIT )
885
0
  {
886
0
    nPartitions = tuArea.lumaSize().height >> Log2(splitDimensionSize);
887
888
0
    for( uint32_t i = 0; i < nPartitions; i++ )
889
0
    {
890
0
      sub[i] = tuArea;
891
0
      CompArea& blkY = sub[i].blocks[COMP_Y];
892
893
0
      blkY.height = splitDimensionSize;
894
0
      blkY.y = i > 0 ? sub[i - 1].blocks[COMP_Y].y + splitDimensionSize : blkY.y;
895
896
0
      CHECK( sub[i].lumaSize().height < 1, "the cs split causes the block to be smaller than the minimal TU size" );
897
0
    }
898
0
  }
899
0
  else if( splitType == TU_1D_VERT_SPLIT )
900
0
  {
901
0
    nPartitions = tuArea.lumaSize().width >> Log2(splitDimensionSize);
902
903
0
    for( uint32_t i = 0; i < nPartitions; i++ )
904
0
    {
905
0
      sub[i] = tuArea;
906
0
      CompArea& blkY = sub[i].blocks[COMP_Y];
907
908
0
      blkY.width = splitDimensionSize;
909
0
      blkY.x = i > 0 ? sub[i - 1].blocks[COMP_Y].x + splitDimensionSize : blkY.x;
910
0
      CHECK( sub[i].lumaSize().width < 1, "the split causes the block to be smaller than the minimal TU size" );
911
0
    }
912
0
  }
913
0
  else
914
0
  {
915
0
    THROW( "Unknown TU sub-partitioning" );
916
0
  }
917
  //we only partition luma, so there is going to be only one chroma tu at the end (unless it is dual tree, in which case there won't be any chroma components)
918
0
  uint32_t partitionsWithoutChroma = (cs.area.chromaFormat == CHROMA_400) ? 0 : (isDualTree ? nPartitions : nPartitions - 1);
919
0
  for( uint32_t i = 0; i < partitionsWithoutChroma; i++ )
920
0
  {
921
0
    CompArea& blkCb = sub[i].blocks[COMP_Cb];
922
0
    CompArea& blkCr = sub[i].blocks[COMP_Cr];
923
0
    blkCb = CompArea();
924
0
    blkCr = CompArea();
925
0
  }
926
927
0
  return nPartitions;
928
0
}
929
930
931
static const int g_rsScanToZ_w4[16] =
932
{
933
   0,  1,  4,  5, // wouldn't work for 128x32 blocks, but those are forbidden bcs of VPDU constraints
934
   2,  3,  6,  7, // correct ordering for 128x64 (TU32)
935
   8,  9, 12, 13,
936
  10, 11, 14, 15, // correct ordering for 128x128 (TU32)
937
};
938
939
static const int g_rsScanToZ_w2[8] =
940
{
941
   0,  1, // correct ordering for 64x32 (TU32) and 128x64 (TU64)
942
   2,  3, // correct ordering for 64x64 (TU32) and 128x128 (TU64)
943
   4,  5,
944
   6,  7, // correct ordering for 32x64 (TU32) and 64x128 (TU64)
945
};
946
947
static const int g_rsScanToZ_w1[4] =
948
{
949
   0, // no tiling, never used
950
   1, // correct ordering for 64x32 (TU32) and 128x64 (TU64)
951
   2,
952
   3, // correct ordering for 128x32 (TU32)
953
};
954
955
static const int* g_rsScanToZ[3] = { g_rsScanToZ_w1, g_rsScanToZ_w2, g_rsScanToZ_w4 };
956
957
int PartitionerImpl::getMaxTuTiling( Partitioning& dst, const UnitArea& cuArea, const CodingStructure& cs )
958
0
{
959
0
  const Size area = cuArea.lumaSize();
960
0
  const int maxTrSize = cs.sps->getMaxTbSize();
961
0
  const int numTilesH = std::max<int>( 1, area.width / maxTrSize );
962
0
  const int numTilesV = std::max<int>( 1, area.height / maxTrSize );
963
0
  const int numTiles  = numTilesH * numTilesV;
964
0
  const int numLog2H  = Log2( numTilesH );
965
0
  const int* rsScanToZ = g_rsScanToZ[numLog2H];
966
967
0
  Partitioning& ret = dst;
968
969
0
  for( int i = 0; i < numTiles; i++ )
970
0
  {
971
0
    ret[i] = cuArea;
972
973
0
    const int zid = rsScanToZ[i];
974
975
0
    const int y = zid >> numLog2H;
976
0
    const int x = zid & ( ( 1 << numLog2H ) - 1 );
977
978
0
    UnitArea& tile = ret[i];
979
980
0
    for( CompArea& comp : tile.blocks )
981
0
    {
982
0
      if( !comp.valid() ) continue;
983
984
0
      comp.width  /= numTilesH;
985
0
      comp.height /= numTilesV;
986
987
0
      comp.x += comp.width  * x;
988
0
      comp.y += comp.height * y;
989
0
    }
990
0
  }
991
992
0
  return numTiles;
993
0
}
994
995
int PartitionerImpl::getSbtTuTiling( Partitioning& dst, const UnitArea& cuArea, const CodingStructure &cs, const PartSplit splitType )
996
0
{
997
0
  Partitioning& ret = dst;
998
0
  int numTiles      = 2;
999
0
  int widthFactor, heightFactor, xOffsetFactor, yOffsetFactor;
1000
1001
0
  CHECK( !(splitType >= SBT_VER_HALF_POS0_SPLIT && splitType <= SBT_HOR_QUAD_POS1_SPLIT), "wrong" );
1002
1003
0
  for( int i = 0; i < numTiles; i++ )
1004
0
  {
1005
0
    ret[i] = cuArea;
1006
1007
0
    if( splitType >= SBT_VER_QUAD_POS0_SPLIT )
1008
0
    {
1009
0
      if( splitType == SBT_HOR_QUAD_POS0_SPLIT || splitType == SBT_HOR_QUAD_POS1_SPLIT )
1010
0
      {
1011
0
        widthFactor   = 4;
1012
0
        xOffsetFactor = 0;
1013
0
        heightFactor  = ( ( i == 0 &&        splitType == SBT_HOR_QUAD_POS0_SPLIT ) || ( i == 1 && splitType == SBT_HOR_QUAD_POS1_SPLIT ) ) ? 1 : 3;
1014
0
        yOffsetFactor =   ( i == 0 ) ? 0 : ( splitType == SBT_HOR_QUAD_POS0_SPLIT ? 1 : 3 );
1015
0
      }
1016
0
      else
1017
0
      {
1018
0
        widthFactor   = ( ( i == 0 &&        splitType == SBT_VER_QUAD_POS0_SPLIT ) || ( i == 1 && splitType == SBT_VER_QUAD_POS1_SPLIT ) ) ? 1 : 3;
1019
0
        xOffsetFactor =   ( i == 0 ) ? 0 : ( splitType == SBT_VER_QUAD_POS0_SPLIT ? 1 : 3 );
1020
0
        heightFactor  = 4;
1021
0
        yOffsetFactor = 0;
1022
0
      }
1023
0
    }
1024
0
    else
1025
0
    {
1026
0
      if( splitType == SBT_HOR_HALF_POS0_SPLIT || splitType == SBT_HOR_HALF_POS1_SPLIT )
1027
0
      {
1028
0
        widthFactor   = 4;
1029
0
        xOffsetFactor = 0;
1030
0
        heightFactor  = 2;
1031
0
        yOffsetFactor = ( i == 0 ) ? 0 : 2;
1032
0
      }
1033
0
      else
1034
0
      {
1035
0
        widthFactor   = 2;
1036
0
        xOffsetFactor = ( i == 0 ) ? 0 : 2;
1037
0
        heightFactor  = 4;
1038
0
        yOffsetFactor = 0;
1039
0
      }
1040
0
    }
1041
1042
0
    UnitArea& tile = ret[i];
1043
1044
0
    for( CompArea &comp : tile.blocks )
1045
0
    {
1046
0
      if( !comp.valid() ) continue;
1047
1048
0
      comp.x     += ( comp.width  * xOffsetFactor ) >> 2;
1049
0
      comp.y     += ( comp.height * yOffsetFactor ) >> 2;
1050
0
      comp.width  = ( comp.width  * widthFactor   ) >> 2;
1051
0
      comp.height = ( comp.height * heightFactor  ) >> 2;
1052
0
    }
1053
0
  }
1054
1055
0
  return numTiles;
1056
0
}
1057
1058
1059
} // namespace vvenc
1060
1061
//! \}
1062