Coverage Report

Created: 2026-10-17 06:56

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/vvenc/source/Lib/EncoderLib/EncSampleAdaptiveOffset.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
/**
45
 \file     EncSampleAdaptiveOffset.cpp
46
 \brief       estimation part of sample adaptive offset class
47
 */
48
49
#include "EncSampleAdaptiveOffset.h"
50
#include "CommonLib/UnitTools.h"
51
#include "CommonLib/dtrace_codingstruct.h"
52
#include "CommonLib/dtrace_buffer.h"
53
#include "CommonLib/CodingStructure.h"
54
#include <string.h>
55
#include <stdlib.h>
56
#include <math.h>
57
#include "vvenc/vvencCfg.h"
58
59
//! \ingroup EncoderLib
60
//! \{
61
62
namespace vvenc {
63
64
65
80.1k
#define SAOCtx(c) SubCtx( Ctx::Sao, c )
66
67
68
//! rounding with IBDI
69
inline double xRoundIbdi2(int bitDepth, double x)
70
0
{
71
0
  return ((x) >= 0 ? ((int)((x) + 0.5)) : ((int)((x) -0.5)));
72
0
}
73
74
inline double xRoundIbdi(int bitDepth, double x)
75
12.8k
{
76
12.8k
  return (bitDepth > 8 ? xRoundIbdi2(bitDepth, (x)) : ((x)>=0 ? ((int)((x)+0.5)) : ((int)((x)-0.5)))) ;
77
12.8k
}
78
79
80
void SaoPreFilterEdges::create( const ChromaFormat chFmt, const PreCalcValues& pcv )
81
4.62k
{
82
18.4k
  for( int compIdx = 0; compIdx < getNumberValidComponents( chFmt ); compIdx++ )
83
13.8k
  {
84
13.8k
    const ComponentID compID = ComponentID( compIdx );
85
13.8k
    width    [compIdx] = pcv.lumaWidth >> getComponentScaleX( compID, chFmt );
86
13.8k
    ctuHeight[compIdx] = pcv.maxCUSize >> getComponentScaleY( compID, chFmt );
87
13.8k
    bottomRows[compIdx].resize( pcv.heightInCtus * width[compIdx] );
88
13.8k
    rightCols [compIdx].resize( pcv.heightInCtus * ctuHeight[compIdx] );
89
13.8k
  }
90
4.62k
}
91
92
EncSampleAdaptiveOffset::EncSampleAdaptiveOffset()
93
8.15k
  : m_CABACEstimator( nullptr )
94
8.15k
  , m_CtxCache      ( nullptr )
95
8.15k
  , m_ctuPreSao     ( nullptr )
96
8.15k
{
97
8.15k
}
98
99
EncSampleAdaptiveOffset::~EncSampleAdaptiveOffset()
100
8.15k
{
101
8.15k
}
102
103
void EncSampleAdaptiveOffset::init( const VVEncCfg& encCfg )
104
8.15k
{
105
8.15k
  m_EncCfg = &encCfg;
106
107
8.15k
  if ( encCfg.m_bUseSAO )
108
8.15k
  {
109
8.15k
    SampleAdaptiveOffset::init( encCfg.m_internChromaFormat, encCfg.m_CTUSize, encCfg.m_CTUSize, encCfg.m_log2SaoOffsetScale[CH_L], encCfg.m_log2SaoOffsetScale[CH_C] );
110
8.15k
  }
111
8.15k
}
112
113
void EncSampleAdaptiveOffset::initSlice( const Slice* slice )
114
2.03k
{
115
2.03k
  memcpy( m_lambda, slice->getLambdas(), sizeof( m_lambda ) );
116
2.03k
}
117
118
void EncSampleAdaptiveOffset::setCtuEncRsrc( CABACWriter* cabacEstimator, CtxCache* ctxCache, PelStorage* ctuPreSaoBuf )
119
3.49k
{
120
3.49k
  m_CABACEstimator = cabacEstimator;
121
3.49k
  m_CtxCache       = ctxCache;
122
3.49k
  m_ctuPreSao      = ctuPreSaoBuf;
123
3.49k
}
124
125
void EncSampleAdaptiveOffset::disabledRate( CodingStructure& cs, double saoDisabledRate[ MAX_NUM_COMP ][ VVENC_MAX_TLAYER ], SAOBlkParam* reconParams, const double saoEncodingRate, const double saoEncodingRateChroma, const ChromaFormat& chromaFormat )
126
0
{
127
0
  if ( saoEncodingRate > 0.0 )
128
0
  {
129
0
    const PreCalcValues& pcv     = *cs.pcv;
130
0
    const int numberOfComponents = getNumberValidComponents( chromaFormat );
131
0
    const int picTempLayer       = cs.slice->TLayer;
132
0
    int numCtusForSAOOff[MAX_NUM_COMP];
133
134
0
    for (int compIdx = 0; compIdx < numberOfComponents; compIdx++)
135
0
    {
136
0
      numCtusForSAOOff[compIdx] = 0;
137
0
      for( int ctuRsAddr=0; ctuRsAddr< pcv.sizeInCtus; ctuRsAddr++)
138
0
      {
139
0
        if( reconParams[ctuRsAddr][compIdx].modeIdc == SAO_MODE_OFF)
140
0
        {
141
0
          numCtusForSAOOff[compIdx]++;
142
0
        }
143
0
      }
144
0
    }
145
0
    if (saoEncodingRateChroma > 0.0)
146
0
    {
147
0
      for (int compIdx = 0; compIdx < numberOfComponents; compIdx++)
148
0
      {
149
0
        saoDisabledRate[compIdx][picTempLayer] = (double)numCtusForSAOOff[compIdx]/(double)pcv.sizeInCtus;
150
0
      }
151
0
    }
152
0
    else if (picTempLayer == 0)
153
0
    {
154
0
      saoDisabledRate[COMP_Y][0] = (double)(numCtusForSAOOff[COMP_Y]+numCtusForSAOOff[COMP_Cb]+numCtusForSAOOff[COMP_Cr])/(double)(pcv.sizeInCtus *3);
155
0
    }
156
0
  }
157
0
}
158
159
void EncSampleAdaptiveOffset::decidePicParams( const CodingStructure& cs, double saoDisabledRate[ MAX_NUM_COMP ][ VVENC_MAX_TLAYER ], bool saoEnabled[ MAX_NUM_COMP ], const double saoEncodingRate, const double saoEncodingRateChroma, const ChromaFormat& chromaFormat )
160
1.15k
{
161
1.15k
  const Slice& slice           = *cs.slice;
162
1.15k
  const int numberOfComponents = getNumberValidComponents( chromaFormat );
163
164
  // reset
165
1.15k
  if( slice.pendingRasInit )
166
0
  {
167
0
    for( int compIdx = 0; compIdx < MAX_NUM_COMP; compIdx++ )
168
0
    {
169
0
      for( int tempLayer = 1; tempLayer < VVENC_MAX_TLAYER; tempLayer++ )
170
0
      {
171
0
        saoDisabledRate[ compIdx ][ tempLayer ] = 0.0;
172
0
      }
173
0
    }
174
0
  }
175
176
4.62k
  for( int compIdx = 0; compIdx < MAX_NUM_COMP; compIdx++ )
177
3.46k
  {
178
3.46k
    saoEnabled[ compIdx ] = false;
179
3.46k
  }
180
181
1.15k
  const int picTempLayer = slice.TLayer;
182
4.62k
  for( int compIdx = 0; compIdx < numberOfComponents; compIdx++ )
183
3.46k
  {
184
    // enable per default
185
3.46k
    saoEnabled[ compIdx ] = true;
186
187
3.46k
    if( saoEncodingRate > 0.0 )
188
0
    {
189
0
      if( saoEncodingRateChroma > 0.0 )
190
0
      {
191
        // decide slice-level on/off based on previous results
192
0
        if( ( picTempLayer > 0 )
193
0
          && ( saoDisabledRate[ compIdx ][ picTempLayer - 1 ] > ( ( compIdx == COMP_Y ) ? saoEncodingRate : saoEncodingRateChroma ) ) )
194
0
        {
195
0
          saoEnabled[ compIdx ] = false;
196
0
        }
197
0
      }
198
0
      else
199
0
      {
200
        // decide slice-level on/off based on previous results
201
0
        if( ( picTempLayer > 0 )
202
0
          && ( saoDisabledRate[ COMP_Y ][ 0 ] > saoEncodingRate ) )
203
0
        {
204
0
          saoEnabled[ compIdx ] = false;
205
0
        }
206
0
      }
207
0
    }
208
3.46k
  }
209
1.15k
}
210
211
void EncSampleAdaptiveOffset::loadCtuPreSao( CodingStructure& cs, const UnitArea& ctuArea, const int ctuY, SaoPreFilterEdges& edges )
212
3.49k
{
213
  // SAO of a CTU reads the CTU and one sample around it, all as they were before SAO. CTUs are
214
  // filtered row by row from the left, so the reconstruction still holds these samples for the CTU
215
  // itself and for the neighbours right of and below it. The CTUs above and to the left are
216
  // filtered already, and their edges come from the copies they saved here.
217
3.49k
  const PelUnitBuf reco = cs.picture->getRecoBuf();
218
219
13.9k
  for( int compIdx = 0; compIdx < getNumberValidComponents( ctuArea.chromaFormat ); compIdx++ )
220
10.5k
  {
221
10.5k
    const ComponentID compID = ComponentID( compIdx );
222
10.5k
    const CompArea&   area   = ctuArea.block( compID );
223
10.5k
    const CPelBuf     src    = reco.get( compID );
224
10.5k
    PelBuf            ctu    = m_ctuPreSao->get( compID );
225
226
10.5k
    const int x        = area.x;
227
10.5k
    const int y        = area.y;
228
10.5k
    const int w        = area.width;
229
10.5k
    const int h        = area.height;
230
10.5k
    const int colBegin = x > 0 ? -1 : 0;
231
10.5k
    const int colEnd   = x + w < ( int ) src.width ? w + 1 : w;
232
10.5k
    Pel* const savedRow = &edges.bottomRows[compIdx][ctuY * edges.width[compIdx]];
233
10.5k
    Pel* const savedCol = &edges.rightCols [compIdx][ctuY * edges.ctuHeight[compIdx]];
234
235
    // the row above, saved by the CTU row above
236
10.5k
    if( y > 0 )
237
4.75k
    {
238
4.75k
      const Pel* rowAbove = &edges.bottomRows[compIdx][( ctuY - 1 ) * edges.width[compIdx]];
239
4.75k
      std::copy( rowAbove + x + colBegin, rowAbove + x + colEnd, ctu.bufAt( colBegin, -1 ) );
240
4.75k
    }
241
    // the CTU and the column right of it
242
620k
    for( int r = 0; r < h; r++ )
243
610k
    {
244
610k
      const Pel* line = src.bufAt( x, y + r );
245
610k
      std::copy( line, line + colEnd, ctu.bufAt( 0, r ) );
246
610k
    }
247
    // the column left of it, saved by the CTU to the left
248
10.5k
    if( x > 0 )
249
4.39k
    {
250
264k
      for( int r = 0; r < h; r++ )
251
260k
      {
252
260k
        ctu.at( -1, r ) = savedCol[r];
253
260k
      }
254
4.39k
    }
255
    // the row below
256
10.5k
    if( y + h < ( int ) src.height )
257
4.75k
    {
258
4.75k
      const Pel* line = src.bufAt( x, y + h );
259
4.75k
      std::copy( line + colBegin, line + colEnd, ctu.bufAt( colBegin, h ) );
260
4.75k
    }
261
262
    // save this CTU's bottom row and right column for the CTUs filtered after it
263
10.5k
    std::copy( ctu.bufAt( 0, h - 1 ), ctu.bufAt( 0, h - 1 ) + w, savedRow + x );
264
621k
    for( int r = 0; r < h; r++ )
265
610k
    {
266
610k
      savedCol[r] = ctu.at( w - 1, r );
267
610k
    }
268
10.5k
  }
269
3.49k
}
270
271
void EncSampleAdaptiveOffset::getCtuStatistics( CodingStructure& cs, std::vector<SAOStatData**>& saoStatistics, const UnitArea& ctuArea, const int ctuRsAddr )
272
3.50k
{
273
3.50k
  const PreCalcValues& pcv     = *cs.pcv;
274
3.50k
  const int numberOfComponents = getNumberValidComponents( pcv.chrFormat );
275
3.50k
  bool isLeftAvail             = false;
276
3.50k
  bool isRightAvail            = false;
277
3.50k
  bool isAboveAvail            = false;
278
3.50k
  bool isBelowAvail            = false;
279
3.50k
  bool isAboveLeftAvail        = false;
280
3.50k
  bool isAboveRightAvail       = false;
281
282
3.50k
  deriveLoopFilterBoundaryAvailibility( cs, ctuArea.Y(), isLeftAvail, isAboveAvail, isAboveLeftAvail );
283
284
  // NOTE: The number of skipped lines during gathering CTU statistics depends on the slice boundary availabilities.
285
  // For simplicity, here only picture boundaries are considered.
286
287
3.50k
  isRightAvail      = ( ctuArea.Y().x + pcv.maxCUSize < pcv.lumaWidth  );
288
3.50k
  isBelowAvail      = ( ctuArea.Y().y + pcv.maxCUSize < pcv.lumaHeight );
289
3.50k
  isAboveRightAvail = ( ( ctuArea.Y().y > 0 ) && ( isRightAvail ) );
290
291
3.50k
  CHECK( !cs.pps->loopFilterAcrossSlicesEnabled, "Not implemented" );
292
3.50k
  if( cs.pps->getNumTiles() > 1 && !cs.pps->loopFilterAcrossTilesEnabled )
293
0
  {
294
0
    const int ctuX    = ctuArea.lx() >> cs.pcv->maxCUSizeLog2;
295
0
    const int ctuY    = ctuArea.ly() >> cs.pcv->maxCUSizeLog2;
296
0
    isRightAvail      = isRightAvail      && cs.pps->canFilterCtuBdry( ctuX, ctuY,  1, 0 );
297
0
    isBelowAvail      = isBelowAvail      && cs.pps->canFilterCtuBdry( ctuX, ctuY,  0, 1 );
298
0
    isAboveRightAvail = isAboveRightAvail && cs.pps->canFilterCtuBdry( ctuX, ctuY,  1,-1 );
299
0
  }
300
301
  //VirtualBoundaries vb;
302
  //bool isCtuCrossedByVirtualBoundaries = vb.isCrossedByVirtualBoundaries(xPos, yPos, width, height, cs.slice->pps);
303
304
14.0k
  for( int compIdx = 0; compIdx < numberOfComponents; compIdx++ )
305
10.5k
  {
306
10.5k
    const ComponentID compID = ComponentID( compIdx );
307
10.5k
    const CompArea& compArea = ctuArea.block( compID );
308
309
10.5k
    PelBuf srcBuf = m_ctuPreSao->get( compID );
310
10.5k
    PelBuf orgBuf = cs.picture->getOrigBuf().get( compID );
311
312
10.5k
    getBlkStats( compID,
313
10.5k
                 cs.sps->bitDepths[ toChannelType( compID ) ],
314
10.5k
                 saoStatistics[ ctuRsAddr ][ compID ],
315
10.5k
                 srcBuf.buf,
316
10.5k
                 orgBuf.bufAt( compArea ),
317
10.5k
                 srcBuf.stride,
318
10.5k
                 orgBuf.stride,
319
10.5k
                 compArea.width,
320
10.5k
                 compArea.height,
321
10.5k
                 isLeftAvail, isRightAvail, isAboveAvail, isBelowAvail, isAboveLeftAvail, isAboveRightAvail
322
10.5k
               );
323
10.5k
  }
324
3.50k
}
325
326
void EncSampleAdaptiveOffset::getStatistics(std::vector<SAOStatData**>& blkStats, PelUnitBuf& orgYuv, PelUnitBuf& srcYuv, CodingStructure& cs )
327
0
{
328
0
  bool isLeftAvail, isRightAvail, isAboveAvail, isBelowAvail, isAboveLeftAvail, isAboveRightAvail;
329
330
0
  const PreCalcValues& pcv = *cs.pcv;
331
0
  const int numberOfComponents = getNumberValidComponents(pcv.chrFormat);
332
333
0
  size_t lineBufferSize = pcv.maxCUSize + 1;
334
0
  if (m_signLineBuf1.size() != lineBufferSize)
335
0
  {
336
0
    m_signLineBuf1.resize(lineBufferSize);
337
0
    m_signLineBuf2.resize(lineBufferSize);
338
0
  }
339
340
0
  int ctuRsAddr = 0;
341
0
  for( uint32_t yPos = 0; yPos < pcv.lumaHeight; yPos += pcv.maxCUSize )
342
0
  {
343
0
    for( uint32_t xPos = 0; xPos < pcv.lumaWidth; xPos += pcv.maxCUSize )
344
0
    {
345
0
      const uint32_t width  = (xPos + pcv.maxCUSize  > pcv.lumaWidth)  ? (pcv.lumaWidth - xPos)  : pcv.maxCUSize;
346
0
      const uint32_t height = (yPos + pcv.maxCUSize > pcv.lumaHeight) ? (pcv.lumaHeight - yPos) : pcv.maxCUSize;
347
0
      const UnitArea area( cs.area.chromaFormat, Area(xPos , yPos, width, height) );
348
349
0
      deriveLoopFilterBoundaryAvailibility(cs, area.Y(), isLeftAvail, isAboveAvail, isAboveLeftAvail );
350
351
      //NOTE: The number of skipped lines during gathering CTU statistics depends on the slice boundary availabilities.
352
      //For simplicity, here only picture boundaries are considered.
353
354
0
      isRightAvail      = (xPos + pcv.maxCUSize  < pcv.lumaWidth );
355
0
      isBelowAvail      = (yPos + pcv.maxCUSize < pcv.lumaHeight);
356
0
      isAboveRightAvail = ((yPos > 0) && (isRightAvail));
357
358
0
      for(int compIdx = 0; compIdx < numberOfComponents; compIdx++)
359
0
      {
360
0
        const ComponentID compID = ComponentID(compIdx);
361
0
        const CompArea& compArea = area.block( compID );
362
363
0
        int  srcStride  = srcYuv.get(compID).stride;
364
0
        Pel* srcBlk     = srcYuv.get(compID).bufAt( compArea );
365
366
0
        int  orgStride  = orgYuv.get(compID).stride;
367
0
        Pel* orgBlk     = orgYuv.get(compID).bufAt( compArea );
368
369
0
        getBlkStats(compID, cs.sps->bitDepths[toChannelType(compID)], blkStats[ctuRsAddr][compID]
370
0
                  , srcBlk, orgBlk, srcStride, orgStride, compArea.width, compArea.height
371
0
                  , isLeftAvail,  isRightAvail, isAboveAvail, isBelowAvail, isAboveLeftAvail, isAboveRightAvail );
372
0
      }
373
0
      ctuRsAddr++;
374
0
    }
375
0
  }
376
0
}
377
378
void EncSampleAdaptiveOffset::decideCtuParams( CodingStructure& cs, const std::vector<SAOStatData**>& saoStatistics, const bool saoEnabled[ MAX_NUM_COMP ], const bool allBlksDisabled, const UnitArea& ctuArea, const int ctuRsAddr, SAOBlkParam* reconParams, SAOBlkParam* codedParams )
379
3.50k
{
380
3.50k
  const PreCalcValues& pcv = *cs.pcv;
381
3.50k
  const Slice& slice       = *cs.slice;
382
3.50k
  const int  ctuPosX       = ctuRsAddr % pcv.widthInCtus;
383
3.50k
  const int  ctuPosY       = ctuRsAddr / pcv.widthInCtus;
384
385
  // reset CABAC estimator
386
3.50k
  if( m_EncCfg->m_ensureWppBitEqual
387
3.50k
      && m_EncCfg->m_numThreads < 1
388
0
      && ctuPosX == 0
389
0
      && ctuPosY > 0 )
390
0
  {
391
0
    m_CABACEstimator->initCtxModels( slice );
392
0
  }
393
394
  // check disabled
395
3.50k
  if( allBlksDisabled )
396
0
  {
397
0
    codedParams[ ctuRsAddr ].reset();
398
0
    return;
399
0
  }
400
401
  // get merge list
402
3.50k
  SAOBlkParam* mergeList[ NUM_SAO_MERGE_TYPES ] = { NULL };
403
3.50k
  getMergeList( cs, ctuRsAddr, reconParams, mergeList );
404
405
3.50k
  const TempCtx ctxStart( m_CtxCache, SAOCtx( m_CABACEstimator->getCtx() ) );
406
3.50k
  TempCtx       ctxBest ( m_CtxCache );
407
408
3.50k
  SAOBlkParam modeParam;
409
3.50k
  double minCost  = MAX_DOUBLE;
410
3.50k
  double modeCost = MAX_DOUBLE;
411
10.5k
  for( int mode = 1; mode < NUM_SAO_MODES; mode++ )
412
7.00k
  {
413
7.00k
    if( mode > 1 )
414
3.50k
    {
415
3.50k
      m_CABACEstimator->getCtx() = SAOCtx( ctxStart );
416
3.50k
    }
417
7.00k
    switch( mode )
418
7.00k
    {
419
3.50k
    case SAO_MODE_NEW:
420
3.50k
      {
421
3.50k
        deriveModeNewRDO( cs.sps->bitDepths, ctuRsAddr, mergeList, saoEnabled, saoStatistics, modeParam, modeCost );
422
3.50k
      }
423
3.50k
      break;
424
3.50k
    case SAO_MODE_MERGE:
425
3.50k
      {
426
3.50k
        deriveModeMergeRDO( cs.sps->bitDepths, ctuRsAddr, mergeList, saoEnabled, saoStatistics, modeParam, modeCost );
427
3.50k
      }
428
3.50k
      break;
429
0
    default:
430
0
      {
431
0
        THROW( "Not a supported SAO mode." );
432
0
      }
433
7.00k
    }
434
435
7.00k
    if( modeCost < minCost )
436
5.84k
    {
437
5.84k
      minCost                  = modeCost;
438
5.84k
      codedParams[ ctuRsAddr ] = modeParam;
439
5.84k
      ctxBest                  = SAOCtx( m_CABACEstimator->getCtx() );
440
5.84k
    }
441
7.00k
  }
442
443
  // apply reconstructed offsets
444
3.50k
  m_CABACEstimator->getCtx() = SAOCtx( ctxBest );
445
3.50k
  reconParams[ ctuRsAddr ] = codedParams[ ctuRsAddr ];
446
447
3.50k
  reconstructBlkSAOParam( reconParams[ ctuRsAddr ], mergeList );
448
449
3.50k
  offsetCTU( ctuArea, *m_ctuPreSao, cs.getRecoBuf(), reconParams[ ctuRsAddr ], cs );
450
3.50k
}
451
452
int64_t EncSampleAdaptiveOffset::getDistortion(const int channelBitDepth, int typeIdc, int typeAuxInfo, int* invQuantOffset, SAOStatData& statData)
453
52.5k
{
454
52.5k
  int64_t dist        = 0;
455
52.5k
  int shift = 2 * DISTORTION_PRECISION_ADJUSTMENT(channelBitDepth);
456
457
52.5k
  switch(typeIdc)
458
52.5k
  {
459
10.5k
  case SAO_TYPE_EO_0:
460
21.0k
  case SAO_TYPE_EO_90:
461
31.5k
  case SAO_TYPE_EO_135:
462
42.0k
  case SAO_TYPE_EO_45:
463
42.0k
    {
464
252k
      for (int offsetIdx=0; offsetIdx<NUM_SAO_EO_CLASSES; offsetIdx++)
465
210k
      {
466
210k
        dist += estSaoDist( statData.count[offsetIdx], invQuantOffset[offsetIdx], statData.diff[offsetIdx], shift);
467
210k
      }
468
42.0k
    }
469
42.0k
    break;
470
10.5k
  case SAO_TYPE_BO:
471
10.5k
    {
472
52.5k
      for (int offsetIdx=typeAuxInfo; offsetIdx<typeAuxInfo+4; offsetIdx++)
473
42.0k
      {
474
42.0k
        int bandIdx = offsetIdx % NUM_SAO_BO_CLASSES ;
475
42.0k
        dist += estSaoDist( statData.count[bandIdx], invQuantOffset[bandIdx], statData.diff[bandIdx], shift);
476
42.0k
      }
477
10.5k
    }
478
10.5k
    break;
479
0
  default:
480
0
    {
481
0
      THROW("Not a supported type");
482
31.5k
    }
483
52.5k
  }
484
485
52.5k
  return dist;
486
52.5k
}
487
488
inline int64_t EncSampleAdaptiveOffset::estSaoDist(int64_t count, int64_t offset, int64_t diffSum, int shift)
489
253k
{
490
253k
  return (( count*offset*offset-diffSum*offset*2 ) >> shift);
491
253k
}
492
493
494
inline int EncSampleAdaptiveOffset::estIterOffset(int typeIdx, double lambda, int offsetInput, int64_t count, int64_t diffSum, int shift, int bitIncrease, int64_t& bestDist, double& bestCost, int offsetTh )
495
1.38k
{
496
1.38k
  int iterOffset, tempOffset;
497
1.38k
  int64_t tempDist, tempRate;
498
1.38k
  double tempCost, tempMinCost;
499
1.38k
  int offsetOutput = 0;
500
1.38k
  iterOffset = offsetInput;
501
  // Assuming sending quantized value 0 results in zero offset and sending the value zero needs 1 bit. entropy coder can be used to measure the exact rate here.
502
1.38k
  tempMinCost = lambda;
503
2.78k
  while (iterOffset != 0)
504
1.39k
  {
505
    // Calculate the bits required for signaling the offset
506
1.39k
    tempRate = (typeIdx == SAO_TYPE_BO) ? (abs((int)iterOffset)+2) : (abs((int)iterOffset)+1);
507
1.39k
    if (abs((int)iterOffset)==offsetTh) //inclusive
508
0
    {
509
0
      tempRate --;
510
0
    }
511
    // Do the dequantization before distortion calculation
512
1.39k
    tempOffset  = iterOffset * (1<< bitIncrease);
513
1.39k
    tempDist    = estSaoDist( count, tempOffset, diffSum, shift);
514
1.39k
    tempCost    = ((double)tempDist + lambda * (double) tempRate);
515
1.39k
    if(tempCost < tempMinCost)
516
284
    {
517
284
      tempMinCost = tempCost;
518
284
      offsetOutput = iterOffset;
519
284
      bestDist = tempDist;
520
284
      bestCost = tempCost;
521
284
    }
522
1.39k
    iterOffset = (iterOffset > 0) ? (iterOffset-1):(iterOffset+1);
523
1.39k
  }
524
1.38k
  return offsetOutput;
525
1.38k
}
526
527
void EncSampleAdaptiveOffset::deriveOffsets(ComponentID compIdx, const int channelBitDepth, int typeIdc, SAOStatData& statData, int* quantOffsets, int& typeAuxInfo)
528
52.5k
{
529
52.5k
  int bitDepth = channelBitDepth;
530
52.5k
  int shift = 2 * DISTORTION_PRECISION_ADJUSTMENT(bitDepth);
531
52.5k
  int offsetTh = SampleAdaptiveOffset::getMaxOffsetQVal(channelBitDepth);  //inclusive
532
533
52.5k
  ::memset(quantOffsets, 0, sizeof(int)*MAX_NUM_SAO_CLASSES);
534
535
  //derive initial offsets
536
52.5k
  int numClasses = (typeIdc == SAO_TYPE_BO)?((int)NUM_SAO_BO_CLASSES):((int)NUM_SAO_EO_CLASSES);
537
598k
  for(int classIdx=0; classIdx< numClasses; classIdx++)
538
546k
  {
539
546k
    if( (typeIdc != SAO_TYPE_BO) && (classIdx==SAO_CLASS_EO_PLAIN)  )
540
42.0k
    {
541
42.0k
      continue; //offset will be zero
542
42.0k
    }
543
544
504k
    if(statData.count[classIdx] == 0)
545
491k
    {
546
491k
      continue; //offset will be zero
547
491k
    }
548
12.8k
#if (  DISTORTION_PRECISION_ADJUSTMENT(x)  == 0 )
549
12.8k
    quantOffsets[classIdx] =
550
12.8k
       (int) xRoundIbdi(bitDepth, (double)(statData.diff[classIdx] ) / (double)(statData.count[classIdx] << m_offsetStepLog2[compIdx]));
551
12.8k
     quantOffsets[classIdx] = Clip3(-offsetTh, offsetTh, quantOffsets[classIdx]);
552
#else
553
      quantOffsets[classIdx] =
554
        (int) xRoundIbdi(bitDepth, (double)(statData.diff[classIdx] << DISTORTION_PRECISION_ADJUSTMENT(bitDepth))
555
                                     / (double)(statData.count[classIdx] << m_offsetStepLog2[compIdx]));
556
      quantOffsets[classIdx] = Clip3(-offsetTh, offsetTh, quantOffsets[classIdx]);
557
#endif
558
12.8k
  }
559
560
  // adjust offsets
561
52.5k
  switch(typeIdc)
562
52.5k
  {
563
10.5k
  case SAO_TYPE_EO_0:
564
21.0k
  case SAO_TYPE_EO_90:
565
31.5k
  case SAO_TYPE_EO_135:
566
42.0k
  case SAO_TYPE_EO_45:
567
42.0k
    {
568
42.0k
      int64_t classDist;
569
42.0k
      double classCost;
570
252k
      for(int classIdx=0; classIdx<NUM_SAO_EO_CLASSES; classIdx++)
571
210k
      {
572
210k
        if(classIdx==SAO_CLASS_EO_FULL_VALLEY && quantOffsets[classIdx] < 0)
573
0
        {
574
0
          quantOffsets[classIdx] =0;
575
0
        }
576
210k
        if(classIdx==SAO_CLASS_EO_HALF_VALLEY && quantOffsets[classIdx] < 0)
577
0
        {
578
0
          quantOffsets[classIdx] =0;
579
0
        }
580
210k
        if(classIdx==SAO_CLASS_EO_HALF_PEAK   && quantOffsets[classIdx] > 0)
581
0
        {
582
0
          quantOffsets[classIdx] =0;
583
0
        }
584
210k
        if(classIdx==SAO_CLASS_EO_FULL_PEAK   && quantOffsets[classIdx] > 0)
585
0
        {
586
0
          quantOffsets[classIdx] =0;
587
0
        }
588
589
210k
        if( quantOffsets[classIdx] != 0 ) //iterative adjustment only when derived offset is not zero
590
1.07k
        {
591
1.07k
          quantOffsets[classIdx] = estIterOffset( typeIdc, m_lambda[compIdx], quantOffsets[classIdx], statData.count[classIdx], statData.diff[classIdx], shift, m_offsetStepLog2[compIdx], classDist , classCost , offsetTh );
592
1.07k
        }
593
210k
      }
594
595
42.0k
      typeAuxInfo =0;
596
42.0k
    }
597
42.0k
    break;
598
10.5k
  case SAO_TYPE_BO:
599
10.5k
    {
600
10.5k
      int64_t  distBOClasses[NUM_SAO_BO_CLASSES];
601
10.5k
      double costBOClasses[NUM_SAO_BO_CLASSES];
602
10.5k
      ::memset(distBOClasses, 0, sizeof(int64_t)*NUM_SAO_BO_CLASSES);
603
346k
      for(int classIdx=0; classIdx< NUM_SAO_BO_CLASSES; classIdx++)
604
336k
      {
605
336k
        costBOClasses[classIdx]= m_lambda[compIdx];
606
336k
        if( quantOffsets[classIdx] != 0 ) //iterative adjustment only when derived offset is not zero
607
311
        {
608
311
          quantOffsets[classIdx] = estIterOffset( typeIdc, m_lambda[compIdx], quantOffsets[classIdx], statData.count[classIdx], statData.diff[classIdx], shift, m_offsetStepLog2[compIdx], distBOClasses[classIdx], costBOClasses[classIdx], offsetTh );
609
311
        }
610
336k
      }
611
612
      //decide the starting band index
613
10.5k
      double minCost = MAX_DOUBLE, cost;
614
346k
      for(int band=0; band< NUM_SAO_BO_CLASSES; band++)
615
336k
      {
616
336k
        cost  = costBOClasses[(band  )%NUM_SAO_BO_CLASSES];
617
336k
        cost += costBOClasses[(band+1)%NUM_SAO_BO_CLASSES];
618
336k
        cost += costBOClasses[(band+2)%NUM_SAO_BO_CLASSES];
619
336k
        cost += costBOClasses[(band+3)%NUM_SAO_BO_CLASSES];
620
621
336k
        if(cost < minCost)
622
10.5k
        {
623
10.5k
          minCost = cost;
624
10.5k
          typeAuxInfo = band;
625
10.5k
        }
626
336k
      }
627
      //clear those unused classes
628
10.5k
      int clearQuantOffset[NUM_SAO_BO_CLASSES];
629
10.5k
      ::memset(clearQuantOffset, 0, sizeof(int)*NUM_SAO_BO_CLASSES);
630
52.5k
      for(int i=0; i< 4; i++)
631
42.0k
      {
632
42.0k
        int band = (typeAuxInfo+i)%NUM_SAO_BO_CLASSES;
633
42.0k
        clearQuantOffset[band] = quantOffsets[band];
634
42.0k
      }
635
10.5k
      ::memcpy(quantOffsets, clearQuantOffset, sizeof(int)*NUM_SAO_BO_CLASSES);
636
10.5k
    }
637
10.5k
    break;
638
0
  default:
639
0
    {
640
0
      THROW("Not a supported type");
641
31.5k
    }
642
52.5k
  }
643
52.5k
}
644
645
void EncSampleAdaptiveOffset::deriveModeNewRDO(const BitDepths &bitDepths, int ctuRsAddr, SAOBlkParam* mergeList[NUM_SAO_MERGE_TYPES], const bool* sliceEnabled, const std::vector<SAOStatData**>& blkStats, SAOBlkParam& modeParam, double& modeNormCost )
646
3.50k
{
647
3.50k
  double minCost, cost;
648
3.50k
  uint64_t previousFracBits;
649
3.50k
  const int numberOfComponents = m_numberOfComponents;
650
651
3.50k
  int64_t dist[MAX_NUM_COMP], modeDist[MAX_NUM_COMP];
652
3.50k
  SAOOffset testOffset[MAX_NUM_COMP];
653
3.50k
  int invQuantOffset[MAX_NUM_SAO_CLASSES];
654
14.0k
  for(int comp=0; comp < MAX_NUM_COMP; comp++)
655
10.5k
  {
656
10.5k
    modeDist[comp] = 0;
657
10.5k
  }
658
659
  //pre-encode merge flags
660
3.50k
  modeParam[COMP_Y].modeIdc = SAO_MODE_OFF;
661
3.50k
  const TempCtx ctxStartBlk   ( m_CtxCache, SAOCtx( m_CABACEstimator->getCtx() ) );
662
3.50k
  m_CABACEstimator->sao_block_pars( modeParam, bitDepths, sliceEnabled, (mergeList[SAO_MERGE_LEFT]!= NULL), (mergeList[SAO_MERGE_ABOVE]!= NULL), true );
663
3.50k
  const TempCtx ctxStartLuma  ( m_CtxCache, SAOCtx( m_CABACEstimator->getCtx() ) );
664
3.50k
  TempCtx       ctxBestLuma   ( m_CtxCache );
665
666
    //------ luma --------//
667
3.50k
  {
668
3.50k
    const ComponentID compIdx = COMP_Y;
669
    //"off" case as initial cost
670
3.50k
    modeParam[compIdx].modeIdc = SAO_MODE_OFF;
671
3.50k
    m_CABACEstimator->resetBits();
672
3.50k
    m_CABACEstimator->sao_offset_pars( modeParam[compIdx], compIdx, sliceEnabled[compIdx], bitDepths[CH_L] );
673
3.50k
    modeDist[compIdx] = 0;
674
3.50k
    minCost           = m_lambda[compIdx] * (FRAC_BITS_SCALE * m_CABACEstimator->getEstFracBits());
675
3.50k
    ctxBestLuma = SAOCtx( m_CABACEstimator->getCtx() );
676
3.50k
    if(sliceEnabled[compIdx])
677
3.50k
    {
678
21.0k
      for(int typeIdc=0; typeIdc< NUM_SAO_NEW_TYPES; typeIdc++)
679
17.5k
      {
680
17.5k
        testOffset[compIdx].modeIdc = SAO_MODE_NEW;
681
17.5k
        testOffset[compIdx].typeIdc = typeIdc;
682
683
        //derive coded offset
684
17.5k
        deriveOffsets(compIdx, bitDepths[CH_L], typeIdc, blkStats[ctuRsAddr][compIdx][typeIdc], testOffset[compIdx].offset, testOffset[compIdx].typeAuxInfo);
685
686
        //inversed quantized offsets
687
17.5k
        invertQuantOffsets(compIdx, typeIdc, testOffset[compIdx].typeAuxInfo, invQuantOffset, testOffset[compIdx].offset);
688
689
        //get distortion
690
17.5k
        dist[compIdx] = getDistortion(bitDepths[CH_L], testOffset[compIdx].typeIdc, testOffset[compIdx].typeAuxInfo, invQuantOffset, blkStats[ctuRsAddr][compIdx][typeIdc]);
691
692
        //get rate
693
17.5k
        m_CABACEstimator->getCtx() = SAOCtx( ctxStartLuma );
694
17.5k
        m_CABACEstimator->resetBits();
695
17.5k
        m_CABACEstimator->sao_offset_pars( testOffset[compIdx], compIdx, sliceEnabled[compIdx], bitDepths[CH_L] );
696
17.5k
        double rate = FRAC_BITS_SCALE * m_CABACEstimator->getEstFracBits();
697
17.5k
        cost = (double)dist[compIdx] + m_lambda[compIdx]*rate;
698
17.5k
        if(cost < minCost)
699
28
        {
700
28
          minCost = cost;
701
28
          modeDist[compIdx] = dist[compIdx];
702
28
          modeParam[compIdx]= testOffset[compIdx];
703
28
          ctxBestLuma = SAOCtx( m_CABACEstimator->getCtx() );
704
28
        }
705
17.5k
      }
706
3.50k
    }
707
3.50k
    m_CABACEstimator->getCtx() = SAOCtx( ctxBestLuma );
708
3.50k
  }
709
710
  //------ chroma --------//
711
//"off" case as initial cost
712
3.50k
  cost = 0;
713
3.50k
  previousFracBits = 0;
714
3.50k
  m_CABACEstimator->resetBits();
715
10.5k
  for(uint32_t componentIndex = COMP_Cb; componentIndex < numberOfComponents; componentIndex++)
716
7.00k
  {
717
7.00k
    const ComponentID component = ComponentID(componentIndex);
718
719
7.00k
    modeParam[component].modeIdc = SAO_MODE_OFF;
720
7.00k
    modeDist [component]         = 0;
721
7.00k
    m_CABACEstimator->sao_offset_pars( modeParam[component], component, sliceEnabled[component], bitDepths[CH_C] );
722
7.00k
    const uint64_t currentFracBits = m_CABACEstimator->getEstFracBits();
723
7.00k
    cost += m_lambda[component] * FRAC_BITS_SCALE * (currentFracBits - previousFracBits);
724
7.00k
    previousFracBits = currentFracBits;
725
7.00k
  }
726
727
3.50k
  minCost = cost;
728
729
  //doesn't need to store cabac status here since the whole CTU parameters will be re-encoded at the end of this function
730
731
21.0k
  for(int typeIdc=0; typeIdc< NUM_SAO_NEW_TYPES; typeIdc++)
732
17.5k
  {
733
17.5k
    m_CABACEstimator->getCtx() = SAOCtx( ctxBestLuma );
734
17.5k
    m_CABACEstimator->resetBits();
735
17.5k
    previousFracBits = 0;
736
17.5k
    cost = 0;
737
738
52.5k
    for(uint32_t componentIndex = COMP_Cb; componentIndex < numberOfComponents; componentIndex++)
739
35.0k
    {
740
35.0k
      const ComponentID component = ComponentID(componentIndex);
741
35.0k
      if(!sliceEnabled[component])
742
0
      {
743
0
        testOffset[component].modeIdc = SAO_MODE_OFF;
744
0
        dist[component]= 0;
745
0
        continue;
746
0
      }
747
35.0k
      testOffset[component].modeIdc = SAO_MODE_NEW;
748
35.0k
      testOffset[component].typeIdc = typeIdc;
749
750
      //derive offset & get distortion
751
35.0k
      deriveOffsets(component, bitDepths[CH_C], typeIdc, blkStats[ctuRsAddr][component][typeIdc], testOffset[component].offset, testOffset[component].typeAuxInfo);
752
35.0k
      invertQuantOffsets(component, typeIdc, testOffset[component].typeAuxInfo, invQuantOffset, testOffset[component].offset);
753
35.0k
      dist[component] = getDistortion(bitDepths[CH_C], typeIdc, testOffset[component].typeAuxInfo, invQuantOffset, blkStats[ctuRsAddr][component][typeIdc]);
754
35.0k
      m_CABACEstimator->sao_offset_pars( testOffset[component], component, sliceEnabled[component], bitDepths[CH_C] );
755
35.0k
      const uint64_t currentFracBits = m_CABACEstimator->getEstFracBits();
756
35.0k
      cost += dist[component] + (m_lambda[component] * FRAC_BITS_SCALE * (currentFracBits - previousFracBits));
757
35.0k
      previousFracBits = currentFracBits;
758
35.0k
    }
759
760
17.5k
    if(cost < minCost)
761
1
    {
762
1
      minCost = cost;
763
3
      for(uint32_t componentIndex = COMP_Cb; componentIndex < numberOfComponents; componentIndex++)
764
2
      {
765
2
        modeDist[componentIndex]  = dist[componentIndex];
766
2
        modeParam[componentIndex] = testOffset[componentIndex];
767
2
      }
768
1
    }
769
770
17.5k
  } // SAO_TYPE loop
771
772
  //----- re-gen rate & normalized cost----//
773
3.50k
  modeNormCost = 0;
774
14.0k
  for(uint32_t componentIndex = COMP_Y; componentIndex < numberOfComponents; componentIndex++)
775
10.5k
  {
776
10.5k
    modeNormCost += (double)modeDist[componentIndex] / m_lambda[componentIndex];
777
10.5k
  }
778
779
3.50k
  m_CABACEstimator->getCtx() = SAOCtx( ctxStartBlk );
780
3.50k
  m_CABACEstimator->resetBits();
781
3.50k
  m_CABACEstimator->sao_block_pars( modeParam, bitDepths, sliceEnabled, (mergeList[SAO_MERGE_LEFT]!= NULL), (mergeList[SAO_MERGE_ABOVE]!= NULL), false );
782
3.50k
  modeNormCost += FRAC_BITS_SCALE * m_CABACEstimator->getEstFracBits();
783
3.50k
}
784
785
void EncSampleAdaptiveOffset::deriveModeMergeRDO(const BitDepths &bitDepths, int ctuRsAddr, SAOBlkParam* mergeList[NUM_SAO_MERGE_TYPES], const bool* sliceEnabled, const std::vector<SAOStatData**>& blkStats, SAOBlkParam& modeParam, double& modeNormCost )
786
3.50k
{
787
3.50k
  modeNormCost = MAX_DOUBLE;
788
789
3.50k
  double cost;
790
3.50k
  SAOBlkParam testBlkParam;
791
3.50k
  const int numberOfComponents = m_numberOfComponents;
792
793
3.50k
  const TempCtx ctxStart  ( m_CtxCache, SAOCtx( m_CABACEstimator->getCtx() ) );
794
3.50k
  TempCtx       ctxBest   ( m_CtxCache );
795
796
10.5k
  for(int mergeType=0; mergeType< NUM_SAO_MERGE_TYPES; mergeType++)
797
7.00k
  {
798
7.00k
    if(mergeList[mergeType] == NULL)
799
3.95k
    {
800
3.95k
      continue;
801
3.95k
    }
802
803
3.04k
    testBlkParam = *(mergeList[mergeType]);
804
    //normalized distortion
805
3.04k
    double normDist=0;
806
12.1k
    for(int compIdx = 0; compIdx < numberOfComponents; compIdx++)
807
9.14k
    {
808
9.14k
      testBlkParam[compIdx].modeIdc = SAO_MODE_MERGE;
809
9.14k
      testBlkParam[compIdx].typeIdc = mergeType;
810
811
9.14k
      SAOOffset& mergedOffsetParam = (*(mergeList[mergeType]))[compIdx];
812
813
9.14k
      if( mergedOffsetParam.modeIdc != SAO_MODE_OFF)
814
11
      {
815
        //offsets have been reconstructed. Don't call inversed quantization function.
816
11
        normDist += (((double)getDistortion(bitDepths[toChannelType(ComponentID(compIdx))], mergedOffsetParam.typeIdc, mergedOffsetParam.typeAuxInfo, mergedOffsetParam.offset, blkStats[ctuRsAddr][compIdx][mergedOffsetParam.typeIdc]))
817
11
                       /m_lambda[compIdx] );
818
11
      }
819
9.14k
    }
820
821
    //rate
822
3.04k
    m_CABACEstimator->getCtx() = SAOCtx( ctxStart );
823
3.04k
    m_CABACEstimator->resetBits();
824
3.04k
    m_CABACEstimator->sao_block_pars( testBlkParam, bitDepths, sliceEnabled, (mergeList[SAO_MERGE_LEFT]!= NULL), (mergeList[SAO_MERGE_ABOVE]!= NULL), false );
825
3.04k
    double rate = FRAC_BITS_SCALE * m_CABACEstimator->getEstFracBits();
826
3.04k
    cost = normDist+rate;
827
828
3.04k
    if(cost < modeNormCost)
829
2.34k
    {
830
2.34k
      modeNormCost = cost;
831
2.34k
      modeParam    = testBlkParam;
832
2.34k
      ctxBest      = SAOCtx( m_CABACEstimator->getCtx() );
833
2.34k
    }
834
3.04k
  }
835
3.50k
  if( modeNormCost < MAX_DOUBLE )
836
2.34k
  {
837
2.34k
    m_CABACEstimator->getCtx() = SAOCtx( ctxBest );
838
2.34k
  }
839
3.50k
}
840
841
void EncSampleAdaptiveOffset::getBlkStats(const ComponentID compIdx, const int channelBitDepth, SAOStatData* statsDataTypes
842
                        , Pel* srcBlk, Pel* orgBlk, int srcStride, int orgStride, int width, int height
843
                        , bool isLeftAvail,  bool isRightAvail, bool isAboveAvail, bool isBelowAvail, bool isAboveLeftAvail, bool isAboveRightAvail )
844
10.5k
{
845
10.5k
  int x, startX, startY, endX, endY, edgeType, firstLineStartX, firstLineEndX;
846
10.5k
  int64_t *diff, *count;
847
10.5k
  Pel* srcLine, *orgLine;
848
10.5k
  const int skipLinesR = compIdx == COMP_Y ? 5 : 3;
849
10.5k
  const int skipLinesB = compIdx == COMP_Y ? 4 : 2;
850
851
63.0k
  for(int typeIdx=0; typeIdx< NUM_SAO_NEW_TYPES; typeIdx++)
852
52.5k
  {
853
52.5k
    SAOStatData& statsData= statsDataTypes[typeIdx];
854
52.5k
    statsData.reset();
855
52.5k
    srcLine = srcBlk;
856
52.5k
    orgLine = orgBlk;
857
52.5k
    diff    = statsData.diff;
858
52.5k
    count   = statsData.count;
859
52.5k
    switch(typeIdx)
860
52.5k
    {
861
10.5k
    case SAO_TYPE_EO_0:
862
10.5k
      {
863
10.5k
        endY   =  isBelowAvail ? (height - skipLinesB) : height;
864
10.5k
        startX = (isLeftAvail  ? 0 : 1);
865
10.5k
        endX   = (isRightAvail ? (width - skipLinesR) : (width - 1));
866
10.5k
        calcSaoStatisticsEo0(width,startX,endX,endY,srcLine,orgLine,srcStride,orgStride,count,diff);
867
10.5k
      }
868
10.5k
      break;
869
10.5k
    case SAO_TYPE_EO_90:
870
10.5k
      {
871
10.5k
        int8_t *signUpLine = &m_signLineBuf1[0];
872
10.5k
        startX = 0;
873
10.5k
        startY = isAboveAvail ? 0 : 1;
874
10.5k
        endX   = (isRightAvail ? (width - skipLinesR) : width);
875
10.5k
        endY   = isBelowAvail ? (height - skipLinesB) : (height - 1);
876
10.5k
        if (!isAboveAvail)
877
5.75k
        {
878
5.75k
          srcLine += srcStride;
879
5.75k
          orgLine += orgStride;
880
5.75k
        }
881
10.5k
        calcSaoStatisticsEo90(width,endX,startY,endY,srcLine,orgLine,srcStride,orgStride,count,diff,signUpLine);
882
10.5k
      }
883
10.5k
      break;
884
10.5k
    case SAO_TYPE_EO_135:
885
10.5k
      {
886
10.5k
        diff +=2;
887
10.5k
        count+=2;
888
10.5k
        int8_t *signUpLine, *signDownLine;
889
10.5k
        signUpLine  = &m_signLineBuf1[0];
890
10.5k
        signDownLine= &m_signLineBuf2[0];
891
10.5k
        startX = isLeftAvail  ? 0 : 1;
892
10.5k
        endX   = isRightAvail ? (width - skipLinesR): (width - 1);
893
10.5k
        endY   = isBelowAvail ? (height - skipLinesB) : (height - 1);
894
        //prepare 2nd line's upper sign
895
10.5k
        Pel* srcLineBelow = srcLine + srcStride;
896
592k
        for (x=startX; x<endX+1; x++)
897
581k
        {
898
581k
          signUpLine[x] = (int8_t)sgn(srcLineBelow[x] - srcLine[x-1]);
899
581k
        }
900
        //1st line
901
10.5k
        Pel* srcLineAbove = srcLine - srcStride;
902
10.5k
        firstLineStartX = isAboveLeftAvail ? 0    : 1;
903
10.5k
        firstLineEndX   = isAboveAvail     ? endX : 1;
904
273k
        for(x=firstLineStartX; x<firstLineEndX; x++)
905
262k
        {
906
262k
          edgeType = sgn(srcLine[x] - srcLineAbove[x-1]) - signUpLine[x+1];
907
262k
          diff [edgeType] += (orgLine[x] - srcLine[x]);
908
262k
          count[edgeType] ++;
909
262k
        }
910
10.5k
        srcLine  += srcStride;
911
10.5k
        orgLine  += orgStride;
912
10.5k
        calcSaoStatisticsEo135(width,startX,endX,endY,srcLine,orgLine,srcStride,orgStride,count,diff,signUpLine,signDownLine);
913
10.5k
      }
914
10.5k
      break;
915
10.5k
    case SAO_TYPE_EO_45:
916
10.5k
      {
917
10.5k
        diff +=2;
918
10.5k
        count+=2;
919
10.5k
        int8_t *signUpLine = &m_signLineBuf1[1];
920
921
10.5k
        startX = isLeftAvail  ? 0 : 1;
922
10.5k
        endX   = isRightAvail ? (width - skipLinesR) : (width - 1);
923
10.5k
        endY   = isBelowAvail ? (height - skipLinesB) : (height - 1);
924
925
        //prepare 2nd line upper sign
926
10.5k
        Pel* srcLineBelow = srcLine + srcStride;
927
592k
        for (x=startX-1; x<endX; x++)
928
581k
        {
929
581k
          signUpLine[x] = (int8_t)sgn(srcLineBelow[x] - srcLine[x+1]);
930
581k
        }
931
        //first line
932
10.5k
        Pel* srcLineAbove = srcLine - srcStride;
933
10.5k
        firstLineStartX = isAboveAvail ? startX : endX;
934
10.5k
        firstLineEndX   = (!isRightAvail && isAboveRightAvail) ? width : endX;
935
273k
        for(x=firstLineStartX; x<firstLineEndX; x++)
936
262k
        {
937
262k
          edgeType = sgn(srcLine[x] - srcLineAbove[x+1]) - signUpLine[x-1];
938
262k
          diff [edgeType] += (orgLine[x] - srcLine[x]);
939
262k
          count[edgeType] ++;
940
262k
        }
941
10.5k
        srcLine += srcStride;
942
10.5k
        orgLine += orgStride;
943
10.5k
        calcSaoStatisticsEo45(width,startX,endX,endY,srcLine,orgLine,srcStride,orgStride,count,diff,signUpLine);
944
10.5k
      }
945
10.5k
      break;
946
10.5k
    case SAO_TYPE_BO:
947
10.5k
      {
948
10.5k
        startX = 0;
949
10.5k
        endX   = isRightAvail ? (width - skipLinesR) : width;
950
10.5k
        endY   = isBelowAvail ? (height- skipLinesB) : height;
951
10.5k
        calcSaoStatisticsBo(width,endX,endY,srcLine,orgLine,srcStride,orgStride,channelBitDepth,count,diff);
952
10.5k
      }
953
10.5k
      break;
954
0
    default:
955
0
      {
956
0
        THROW("Not a supported SAO type");
957
0
      }
958
52.5k
    }
959
52.5k
  }
960
10.5k
}
961
962
void EncSampleAdaptiveOffset::deriveLoopFilterBoundaryAvailibility(CodingStructure& cs, const Position& pos, bool& isLeftAvail, bool& isAboveAvail, bool& isAboveLeftAvail) const
963
3.50k
{
964
3.50k
  const bool isLoopFiltAcrossSlicePPS = cs.pps->loopFilterAcrossSlicesEnabled;
965
3.50k
  const bool isLoopFiltAcrossTilePPS = cs.pps->loopFilterAcrossTilesEnabled;
966
967
3.50k
  const int width = cs.pcv->maxCUSize;
968
3.50k
  const int height = cs.pcv->maxCUSize;
969
3.50k
  const CodingUnit* cuCurr = cs.getCU(pos, CH_L, TREE_D);
970
3.50k
  const int ctuX = pos.x >> cs.pcv->maxCUSizeLog2;
971
3.50k
  const int ctuY = pos.y >> cs.pcv->maxCUSizeLog2;
972
3.50k
  const PPS* pps = cs.slice->pps;
973
3.50k
  const CodingUnit* cuLeft      = ctuX > 0 &&             pps->canFilterCtuBdry( ctuX, ctuY, -1, 0 ) ? cs.getCU(pos.offset(-width, 0), CH_L, TREE_D): nullptr;
974
3.50k
  const CodingUnit* cuAbove     = ctuY > 0 &&             pps->canFilterCtuBdry( ctuX, ctuY, 0, -1 ) ? cs.getCU(pos.offset(0, -height), CH_L, TREE_D): nullptr;
975
3.50k
  const CodingUnit* cuAboveLeft = ctuY > 0 && ctuX > 0 && pps->canFilterCtuBdry( ctuX, ctuY, -1,-1 ) ? cs.getCU(pos.offset(-width, -height), CH_L, TREE_D): nullptr;
976
977
3.50k
  if (!isLoopFiltAcrossSlicePPS)
978
0
  {
979
0
    isLeftAvail      = (cuLeft == NULL)      ? false : CU::isSameSlice(*cuCurr, *cuLeft);
980
0
    isAboveAvail     = (cuAbove == NULL)     ? false : CU::isSameSlice(*cuCurr, *cuAbove);
981
0
    isAboveLeftAvail = (cuAboveLeft == NULL) ? false : CU::isSameSlice(*cuCurr, *cuAboveLeft);
982
0
  }
983
3.50k
  else
984
3.50k
  {
985
3.50k
    isLeftAvail      = (cuLeft != NULL);
986
3.50k
    isAboveAvail     = (cuAbove != NULL);
987
3.50k
    isAboveLeftAvail = (cuAboveLeft != NULL);
988
3.50k
  }
989
990
3.50k
  if (!isLoopFiltAcrossTilePPS)
991
0
  {
992
0
    isLeftAvail      = (!isLeftAvail)      ? false : CU::isSameTile(*cuCurr, *cuLeft);
993
0
    isAboveAvail     = (!isAboveAvail)     ? false : CU::isSameTile(*cuCurr, *cuAbove);
994
0
    isAboveLeftAvail = (!isAboveLeftAvail) ? false : CU::isSameTile(*cuCurr, *cuAboveLeft);
995
0
  }
996
997
998
3.50k
  SubPic curSubPic = cs.pps->getSubPicFromCU(*cuCurr);
999
3.50k
  if (!curSubPic.loopFilterAcrossSubPicEnabled )
1000
0
  {
1001
0
    isLeftAvail      = (!isLeftAvail)      ? false : CU::isSameSubPic(*cuCurr, *cuLeft);
1002
0
    isAboveAvail     = (!isAboveAvail)     ? false : CU::isSameSubPic(*cuCurr, *cuAbove);
1003
0
    isAboveLeftAvail = (!isAboveLeftAvail) ? false : CU::isSameSubPic(*cuCurr, *cuAboveLeft);
1004
0
  }
1005
1006
3.50k
}
1007
1008
} // namespace vvenc
1009
1010
//! \}
1011