Coverage Report

Created: 2026-10-28 06:18

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
114k
#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
17.5k
{
76
17.5k
  return (bitDepth > 8 ? xRoundIbdi2(bitDepth, (x)) : ((x)>=0 ? ((int)((x)+0.5)) : ((int)((x)-0.5)))) ;
77
17.5k
}
78
79
80
void SaoPreFilterEdges::create( const ChromaFormat chFmt, const PreCalcValues& pcv )
81
5.97k
{
82
23.8k
  for( int compIdx = 0; compIdx < getNumberValidComponents( chFmt ); compIdx++ )
83
17.9k
  {
84
17.9k
    const ComponentID compID = ComponentID( compIdx );
85
17.9k
    width    [compIdx] = pcv.lumaWidth >> getComponentScaleX( compID, chFmt );
86
17.9k
    ctuHeight[compIdx] = pcv.maxCUSize >> getComponentScaleY( compID, chFmt );
87
17.9k
    bottomRows[compIdx].resize( pcv.heightInCtus * width[compIdx] );
88
17.9k
    rightCols [compIdx].resize( pcv.heightInCtus * ctuHeight[compIdx] );
89
17.9k
  }
90
5.97k
}
91
92
EncSampleAdaptiveOffset::EncSampleAdaptiveOffset()
93
10.7k
  : m_CABACEstimator( nullptr )
94
10.7k
  , m_CtxCache      ( nullptr )
95
10.7k
  , m_ctuPreSao     ( nullptr )
96
10.7k
{
97
10.7k
}
98
99
EncSampleAdaptiveOffset::~EncSampleAdaptiveOffset()
100
10.7k
{
101
10.7k
}
102
103
void EncSampleAdaptiveOffset::init( const VVEncCfg& encCfg )
104
10.7k
{
105
10.7k
  m_EncCfg = &encCfg;
106
107
10.7k
  if ( encCfg.m_bUseSAO )
108
10.7k
  {
109
10.7k
    SampleAdaptiveOffset::init( encCfg.m_internChromaFormat, encCfg.m_CTUSize, encCfg.m_CTUSize, encCfg.m_log2SaoOffsetScale[CH_L], encCfg.m_log2SaoOffsetScale[CH_C] );
110
10.7k
  }
111
10.7k
}
112
113
void EncSampleAdaptiveOffset::initSlice( const Slice* slice )
114
2.69k
{
115
2.69k
  memcpy( m_lambda, slice->getLambdas(), sizeof( m_lambda ) );
116
2.69k
}
117
118
void EncSampleAdaptiveOffset::setCtuEncRsrc( CABACWriter* cabacEstimator, CtxCache* ctxCache, PelStorage* ctuPreSaoBuf )
119
4.97k
{
120
4.97k
  m_CABACEstimator = cabacEstimator;
121
4.97k
  m_CtxCache       = ctxCache;
122
4.97k
  m_ctuPreSao      = ctuPreSaoBuf;
123
4.97k
}
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.49k
{
161
1.49k
  const Slice& slice           = *cs.slice;
162
1.49k
  const int numberOfComponents = getNumberValidComponents( chromaFormat );
163
164
  // reset
165
1.49k
  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
5.97k
  for( int compIdx = 0; compIdx < MAX_NUM_COMP; compIdx++ )
177
4.47k
  {
178
4.47k
    saoEnabled[ compIdx ] = false;
179
4.47k
  }
180
181
1.49k
  const int picTempLayer = slice.TLayer;
182
5.97k
  for( int compIdx = 0; compIdx < numberOfComponents; compIdx++ )
183
4.47k
  {
184
    // enable per default
185
4.47k
    saoEnabled[ compIdx ] = true;
186
187
4.47k
    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
4.47k
  }
209
1.49k
}
210
211
void EncSampleAdaptiveOffset::loadCtuPreSao( CodingStructure& cs, const UnitArea& ctuArea, const int ctuY, SaoPreFilterEdges& edges )
212
4.97k
{
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
4.97k
  const PelUnitBuf reco = cs.picture->getRecoBuf();
218
219
19.8k
  for( int compIdx = 0; compIdx < getNumberValidComponents( ctuArea.chromaFormat ); compIdx++ )
220
14.9k
  {
221
14.9k
    const ComponentID compID = ComponentID( compIdx );
222
14.9k
    const CompArea&   area   = ctuArea.block( compID );
223
14.9k
    const CPelBuf     src    = reco.get( compID );
224
14.9k
    PelBuf            ctu    = m_ctuPreSao->get( compID );
225
226
14.9k
    const int x        = area.x;
227
14.9k
    const int y        = area.y;
228
14.9k
    const int w        = area.width;
229
14.9k
    const int h        = area.height;
230
14.9k
    const int colBegin = x > 0 ? -1 : 0;
231
14.9k
    const int colEnd   = x + w < ( int ) src.width ? w + 1 : w;
232
14.9k
    Pel* const savedRow = &edges.bottomRows[compIdx][ctuY * edges.width[compIdx]];
233
14.9k
    Pel* const savedCol = &edges.rightCols [compIdx][ctuY * edges.ctuHeight[compIdx]];
234
235
    // the row above, saved by the CTU row above
236
14.9k
    if( y > 0 )
237
6.99k
    {
238
6.99k
      const Pel* rowAbove = &edges.bottomRows[compIdx][( ctuY - 1 ) * edges.width[compIdx]];
239
6.99k
      std::copy( rowAbove + x + colBegin, rowAbove + x + colEnd, ctu.bufAt( colBegin, -1 ) );
240
6.99k
    }
241
    // the CTU and the column right of it
242
903k
    for( int r = 0; r < h; r++ )
243
889k
    {
244
889k
      const Pel* line = src.bufAt( x, y + r );
245
889k
      std::copy( line, line + colEnd, ctu.bufAt( 0, r ) );
246
889k
    }
247
    // the column left of it, saved by the CTU to the left
248
14.9k
    if( x > 0 )
249
6.85k
    {
250
420k
      for( int r = 0; r < h; r++ )
251
413k
      {
252
413k
        ctu.at( -1, r ) = savedCol[r];
253
413k
      }
254
6.85k
    }
255
    // the row below
256
14.9k
    if( y + h < ( int ) src.height )
257
6.99k
    {
258
6.99k
      const Pel* line = src.bufAt( x, y + h );
259
6.99k
      std::copy( line + colBegin, line + colEnd, ctu.bufAt( colBegin, h ) );
260
6.99k
    }
261
262
    // save this CTU's bottom row and right column for the CTUs filtered after it
263
14.9k
    std::copy( ctu.bufAt( 0, h - 1 ), ctu.bufAt( 0, h - 1 ) + w, savedRow + x );
264
904k
    for( int r = 0; r < h; r++ )
265
889k
    {
266
889k
      savedCol[r] = ctu.at( w - 1, r );
267
889k
    }
268
14.9k
  }
269
4.97k
}
270
271
void EncSampleAdaptiveOffset::getCtuStatistics( CodingStructure& cs, std::vector<SAOStatData**>& saoStatistics, const UnitArea& ctuArea, const int ctuRsAddr )
272
4.97k
{
273
4.97k
  const PreCalcValues& pcv     = *cs.pcv;
274
4.97k
  const int numberOfComponents = getNumberValidComponents( pcv.chrFormat );
275
4.97k
  bool isLeftAvail             = false;
276
4.97k
  bool isRightAvail            = false;
277
4.97k
  bool isAboveAvail            = false;
278
4.97k
  bool isBelowAvail            = false;
279
4.97k
  bool isAboveLeftAvail        = false;
280
4.97k
  bool isAboveRightAvail       = false;
281
282
4.97k
  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
4.97k
  isRightAvail      = ( ctuArea.Y().x + pcv.maxCUSize < pcv.lumaWidth  );
288
4.97k
  isBelowAvail      = ( ctuArea.Y().y + pcv.maxCUSize < pcv.lumaHeight );
289
4.97k
  isAboveRightAvail = ( ( ctuArea.Y().y > 0 ) && ( isRightAvail ) );
290
291
4.97k
  CHECK( !cs.pps->loopFilterAcrossSlicesEnabled, "Not implemented" );
292
4.97k
  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
19.9k
  for( int compIdx = 0; compIdx < numberOfComponents; compIdx++ )
305
14.9k
  {
306
14.9k
    const ComponentID compID = ComponentID( compIdx );
307
14.9k
    const CompArea& compArea = ctuArea.block( compID );
308
309
14.9k
    PelBuf srcBuf = m_ctuPreSao->get( compID );
310
14.9k
    PelBuf orgBuf = cs.picture->getOrigBuf().get( compID );
311
312
14.9k
    getBlkStats( compID,
313
14.9k
                 cs.sps->bitDepths[ toChannelType( compID ) ],
314
14.9k
                 saoStatistics[ ctuRsAddr ][ compID ],
315
14.9k
                 srcBuf.buf,
316
14.9k
                 orgBuf.bufAt( compArea ),
317
14.9k
                 srcBuf.stride,
318
14.9k
                 orgBuf.stride,
319
14.9k
                 compArea.width,
320
14.9k
                 compArea.height,
321
14.9k
                 isLeftAvail, isRightAvail, isAboveAvail, isBelowAvail, isAboveLeftAvail, isAboveRightAvail
322
14.9k
               );
323
14.9k
  }
324
4.97k
}
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
4.97k
{
380
4.97k
  const PreCalcValues& pcv = *cs.pcv;
381
4.97k
  const Slice& slice       = *cs.slice;
382
4.97k
  const int  ctuPosX       = ctuRsAddr % pcv.widthInCtus;
383
4.97k
  const int  ctuPosY       = ctuRsAddr / pcv.widthInCtus;
384
385
  // reset CABAC estimator
386
4.97k
  if( m_EncCfg->m_ensureWppBitEqual
387
4.97k
      && 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
4.97k
  if( allBlksDisabled )
396
0
  {
397
0
    codedParams[ ctuRsAddr ].reset();
398
0
    return;
399
0
  }
400
401
  // get merge list
402
4.97k
  SAOBlkParam* mergeList[ NUM_SAO_MERGE_TYPES ] = { NULL };
403
4.97k
  getMergeList( cs, ctuRsAddr, reconParams, mergeList );
404
405
4.97k
  const TempCtx ctxStart( m_CtxCache, SAOCtx( m_CABACEstimator->getCtx() ) );
406
4.97k
  TempCtx       ctxBest ( m_CtxCache );
407
408
4.97k
  SAOBlkParam modeParam;
409
4.97k
  double minCost  = MAX_DOUBLE;
410
4.97k
  double modeCost = MAX_DOUBLE;
411
14.9k
  for( int mode = 1; mode < NUM_SAO_MODES; mode++ )
412
9.94k
  {
413
9.94k
    if( mode > 1 )
414
4.97k
    {
415
4.97k
      m_CABACEstimator->getCtx() = SAOCtx( ctxStart );
416
4.97k
    }
417
9.94k
    switch( mode )
418
9.94k
    {
419
4.97k
    case SAO_MODE_NEW:
420
4.97k
      {
421
4.97k
        deriveModeNewRDO( cs.sps->bitDepths, ctuRsAddr, mergeList, saoEnabled, saoStatistics, modeParam, modeCost );
422
4.97k
      }
423
4.97k
      break;
424
4.97k
    case SAO_MODE_MERGE:
425
4.97k
      {
426
4.97k
        deriveModeMergeRDO( cs.sps->bitDepths, ctuRsAddr, mergeList, saoEnabled, saoStatistics, modeParam, modeCost );
427
4.97k
      }
428
4.97k
      break;
429
0
    default:
430
0
      {
431
0
        THROW( "Not a supported SAO mode." );
432
0
      }
433
9.94k
    }
434
435
9.95k
    if( modeCost < minCost )
436
8.45k
    {
437
8.45k
      minCost                  = modeCost;
438
8.45k
      codedParams[ ctuRsAddr ] = modeParam;
439
8.45k
      ctxBest                  = SAOCtx( m_CABACEstimator->getCtx() );
440
8.45k
    }
441
9.95k
  }
442
443
  // apply reconstructed offsets
444
4.97k
  m_CABACEstimator->getCtx() = SAOCtx( ctxBest );
445
4.97k
  reconParams[ ctuRsAddr ] = codedParams[ ctuRsAddr ];
446
447
4.97k
  reconstructBlkSAOParam( reconParams[ ctuRsAddr ], mergeList );
448
449
4.97k
  offsetCTU( ctuArea, *m_ctuPreSao, cs.getRecoBuf(), reconParams[ ctuRsAddr ], cs );
450
4.97k
}
451
452
int64_t EncSampleAdaptiveOffset::getDistortion(const int channelBitDepth, int typeIdc, int typeAuxInfo, int* invQuantOffset, SAOStatData& statData)
453
74.6k
{
454
74.6k
  int64_t dist        = 0;
455
74.6k
  int shift = 2 * DISTORTION_PRECISION_ADJUSTMENT(channelBitDepth);
456
457
74.6k
  switch(typeIdc)
458
74.6k
  {
459
14.9k
  case SAO_TYPE_EO_0:
460
29.8k
  case SAO_TYPE_EO_90:
461
44.7k
  case SAO_TYPE_EO_135:
462
59.7k
  case SAO_TYPE_EO_45:
463
59.7k
    {
464
358k
      for (int offsetIdx=0; offsetIdx<NUM_SAO_EO_CLASSES; offsetIdx++)
465
298k
      {
466
298k
        dist += estSaoDist( statData.count[offsetIdx], invQuantOffset[offsetIdx], statData.diff[offsetIdx], shift);
467
298k
      }
468
59.7k
    }
469
59.7k
    break;
470
14.9k
  case SAO_TYPE_BO:
471
14.9k
    {
472
74.6k
      for (int offsetIdx=typeAuxInfo; offsetIdx<typeAuxInfo+4; offsetIdx++)
473
59.7k
      {
474
59.7k
        int bandIdx = offsetIdx % NUM_SAO_BO_CLASSES ;
475
59.7k
        dist += estSaoDist( statData.count[bandIdx], invQuantOffset[bandIdx], statData.diff[bandIdx], shift);
476
59.7k
      }
477
14.9k
    }
478
14.9k
    break;
479
0
  default:
480
0
    {
481
0
      THROW("Not a supported type");
482
44.7k
    }
483
74.6k
  }
484
485
74.6k
  return dist;
486
74.6k
}
487
488
inline int64_t EncSampleAdaptiveOffset::estSaoDist(int64_t count, int64_t offset, int64_t diffSum, int shift)
489
359k
{
490
359k
  return (( count*offset*offset-diffSum*offset*2 ) >> shift);
491
359k
}
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.59k
{
496
1.59k
  int iterOffset, tempOffset;
497
1.59k
  int64_t tempDist, tempRate;
498
1.59k
  double tempCost, tempMinCost;
499
1.59k
  int offsetOutput = 0;
500
1.59k
  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.59k
  tempMinCost = lambda;
503
3.19k
  while (iterOffset != 0)
504
1.60k
  {
505
    // Calculate the bits required for signaling the offset
506
1.60k
    tempRate = (typeIdx == SAO_TYPE_BO) ? (abs((int)iterOffset)+2) : (abs((int)iterOffset)+1);
507
1.60k
    if (abs((int)iterOffset)==offsetTh) //inclusive
508
0
    {
509
0
      tempRate --;
510
0
    }
511
    // Do the dequantization before distortion calculation
512
1.60k
    tempOffset  = iterOffset * (1<< bitIncrease);
513
1.60k
    tempDist    = estSaoDist( count, tempOffset, diffSum, shift);
514
1.60k
    tempCost    = ((double)tempDist + lambda * (double) tempRate);
515
1.60k
    if(tempCost < tempMinCost)
516
342
    {
517
342
      tempMinCost = tempCost;
518
342
      offsetOutput = iterOffset;
519
342
      bestDist = tempDist;
520
342
      bestCost = tempCost;
521
342
    }
522
1.60k
    iterOffset = (iterOffset > 0) ? (iterOffset-1):(iterOffset+1);
523
1.60k
  }
524
1.59k
  return offsetOutput;
525
1.59k
}
526
527
void EncSampleAdaptiveOffset::deriveOffsets(ComponentID compIdx, const int channelBitDepth, int typeIdc, SAOStatData& statData, int* quantOffsets, int& typeAuxInfo)
528
74.6k
{
529
74.6k
  int bitDepth = channelBitDepth;
530
74.6k
  int shift = 2 * DISTORTION_PRECISION_ADJUSTMENT(bitDepth);
531
74.6k
  int offsetTh = SampleAdaptiveOffset::getMaxOffsetQVal(channelBitDepth);  //inclusive
532
533
74.6k
  ::memset(quantOffsets, 0, sizeof(int)*MAX_NUM_SAO_CLASSES);
534
535
  //derive initial offsets
536
74.6k
  int numClasses = (typeIdc == SAO_TYPE_BO)?((int)NUM_SAO_BO_CLASSES):((int)NUM_SAO_EO_CLASSES);
537
850k
  for(int classIdx=0; classIdx< numClasses; classIdx++)
538
776k
  {
539
776k
    if( (typeIdc != SAO_TYPE_BO) && (classIdx==SAO_CLASS_EO_PLAIN)  )
540
59.6k
    {
541
59.6k
      continue; //offset will be zero
542
59.6k
    }
543
544
716k
    if(statData.count[classIdx] == 0)
545
698k
    {
546
698k
      continue; //offset will be zero
547
698k
    }
548
17.5k
#if (  DISTORTION_PRECISION_ADJUSTMENT(x)  == 0 )
549
17.5k
    quantOffsets[classIdx] =
550
17.5k
       (int) xRoundIbdi(bitDepth, (double)(statData.diff[classIdx] ) / (double)(statData.count[classIdx] << m_offsetStepLog2[compIdx]));
551
17.5k
     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
17.5k
  }
559
560
  // adjust offsets
561
74.6k
  switch(typeIdc)
562
74.6k
  {
563
14.9k
  case SAO_TYPE_EO_0:
564
29.8k
  case SAO_TYPE_EO_90:
565
44.7k
  case SAO_TYPE_EO_135:
566
59.7k
  case SAO_TYPE_EO_45:
567
59.7k
    {
568
59.7k
      int64_t classDist;
569
59.7k
      double classCost;
570
358k
      for(int classIdx=0; classIdx<NUM_SAO_EO_CLASSES; classIdx++)
571
298k
      {
572
298k
        if(classIdx==SAO_CLASS_EO_FULL_VALLEY && quantOffsets[classIdx] < 0)
573
0
        {
574
0
          quantOffsets[classIdx] =0;
575
0
        }
576
298k
        if(classIdx==SAO_CLASS_EO_HALF_VALLEY && quantOffsets[classIdx] < 0)
577
0
        {
578
0
          quantOffsets[classIdx] =0;
579
0
        }
580
298k
        if(classIdx==SAO_CLASS_EO_HALF_PEAK   && quantOffsets[classIdx] > 0)
581
0
        {
582
0
          quantOffsets[classIdx] =0;
583
0
        }
584
298k
        if(classIdx==SAO_CLASS_EO_FULL_PEAK   && quantOffsets[classIdx] > 0)
585
0
        {
586
0
          quantOffsets[classIdx] =0;
587
0
        }
588
589
298k
        if( quantOffsets[classIdx] != 0 ) //iterative adjustment only when derived offset is not zero
590
1.19k
        {
591
1.19k
          quantOffsets[classIdx] = estIterOffset( typeIdc, m_lambda[compIdx], quantOffsets[classIdx], statData.count[classIdx], statData.diff[classIdx], shift, m_offsetStepLog2[compIdx], classDist , classCost , offsetTh );
592
1.19k
        }
593
298k
      }
594
595
59.7k
      typeAuxInfo =0;
596
59.7k
    }
597
59.7k
    break;
598
14.9k
  case SAO_TYPE_BO:
599
14.9k
    {
600
14.9k
      int64_t  distBOClasses[NUM_SAO_BO_CLASSES];
601
14.9k
      double costBOClasses[NUM_SAO_BO_CLASSES];
602
14.9k
      ::memset(distBOClasses, 0, sizeof(int64_t)*NUM_SAO_BO_CLASSES);
603
492k
      for(int classIdx=0; classIdx< NUM_SAO_BO_CLASSES; classIdx++)
604
477k
      {
605
477k
        costBOClasses[classIdx]= m_lambda[compIdx];
606
477k
        if( quantOffsets[classIdx] != 0 ) //iterative adjustment only when derived offset is not zero
607
399
        {
608
399
          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
399
        }
610
477k
      }
611
612
      //decide the starting band index
613
14.9k
      double minCost = MAX_DOUBLE, cost;
614
492k
      for(int band=0; band< NUM_SAO_BO_CLASSES; band++)
615
477k
      {
616
477k
        cost  = costBOClasses[(band  )%NUM_SAO_BO_CLASSES];
617
477k
        cost += costBOClasses[(band+1)%NUM_SAO_BO_CLASSES];
618
477k
        cost += costBOClasses[(band+2)%NUM_SAO_BO_CLASSES];
619
477k
        cost += costBOClasses[(band+3)%NUM_SAO_BO_CLASSES];
620
621
477k
        if(cost < minCost)
622
15.0k
        {
623
15.0k
          minCost = cost;
624
15.0k
          typeAuxInfo = band;
625
15.0k
        }
626
477k
      }
627
      //clear those unused classes
628
14.9k
      int clearQuantOffset[NUM_SAO_BO_CLASSES];
629
14.9k
      ::memset(clearQuantOffset, 0, sizeof(int)*NUM_SAO_BO_CLASSES);
630
74.6k
      for(int i=0; i< 4; i++)
631
59.7k
      {
632
59.7k
        int band = (typeAuxInfo+i)%NUM_SAO_BO_CLASSES;
633
59.7k
        clearQuantOffset[band] = quantOffsets[band];
634
59.7k
      }
635
14.9k
      ::memcpy(quantOffsets, clearQuantOffset, sizeof(int)*NUM_SAO_BO_CLASSES);
636
14.9k
    }
637
14.9k
    break;
638
0
  default:
639
0
    {
640
0
      THROW("Not a supported type");
641
44.7k
    }
642
74.6k
  }
643
74.6k
}
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
4.97k
{
647
4.97k
  double minCost, cost;
648
4.97k
  uint64_t previousFracBits;
649
4.97k
  const int numberOfComponents = m_numberOfComponents;
650
651
4.97k
  int64_t dist[MAX_NUM_COMP], modeDist[MAX_NUM_COMP];
652
4.97k
  SAOOffset testOffset[MAX_NUM_COMP];
653
4.97k
  int invQuantOffset[MAX_NUM_SAO_CLASSES];
654
19.9k
  for(int comp=0; comp < MAX_NUM_COMP; comp++)
655
14.9k
  {
656
14.9k
    modeDist[comp] = 0;
657
14.9k
  }
658
659
  //pre-encode merge flags
660
4.97k
  modeParam[COMP_Y].modeIdc = SAO_MODE_OFF;
661
4.97k
  const TempCtx ctxStartBlk   ( m_CtxCache, SAOCtx( m_CABACEstimator->getCtx() ) );
662
4.97k
  m_CABACEstimator->sao_block_pars( modeParam, bitDepths, sliceEnabled, (mergeList[SAO_MERGE_LEFT]!= NULL), (mergeList[SAO_MERGE_ABOVE]!= NULL), true );
663
4.97k
  const TempCtx ctxStartLuma  ( m_CtxCache, SAOCtx( m_CABACEstimator->getCtx() ) );
664
4.97k
  TempCtx       ctxBestLuma   ( m_CtxCache );
665
666
    //------ luma --------//
667
4.97k
  {
668
4.97k
    const ComponentID compIdx = COMP_Y;
669
    //"off" case as initial cost
670
4.97k
    modeParam[compIdx].modeIdc = SAO_MODE_OFF;
671
4.97k
    m_CABACEstimator->resetBits();
672
4.97k
    m_CABACEstimator->sao_offset_pars( modeParam[compIdx], compIdx, sliceEnabled[compIdx], bitDepths[CH_L] );
673
4.97k
    modeDist[compIdx] = 0;
674
4.97k
    minCost           = m_lambda[compIdx] * (FRAC_BITS_SCALE * m_CABACEstimator->getEstFracBits());
675
4.97k
    ctxBestLuma = SAOCtx( m_CABACEstimator->getCtx() );
676
4.97k
    if(sliceEnabled[compIdx])
677
4.97k
    {
678
29.8k
      for(int typeIdc=0; typeIdc< NUM_SAO_NEW_TYPES; typeIdc++)
679
24.8k
      {
680
24.8k
        testOffset[compIdx].modeIdc = SAO_MODE_NEW;
681
24.8k
        testOffset[compIdx].typeIdc = typeIdc;
682
683
        //derive coded offset
684
24.8k
        deriveOffsets(compIdx, bitDepths[CH_L], typeIdc, blkStats[ctuRsAddr][compIdx][typeIdc], testOffset[compIdx].offset, testOffset[compIdx].typeAuxInfo);
685
686
        //inversed quantized offsets
687
24.8k
        invertQuantOffsets(compIdx, typeIdc, testOffset[compIdx].typeAuxInfo, invQuantOffset, testOffset[compIdx].offset);
688
689
        //get distortion
690
24.8k
        dist[compIdx] = getDistortion(bitDepths[CH_L], testOffset[compIdx].typeIdc, testOffset[compIdx].typeAuxInfo, invQuantOffset, blkStats[ctuRsAddr][compIdx][typeIdc]);
691
692
        //get rate
693
24.8k
        m_CABACEstimator->getCtx() = SAOCtx( ctxStartLuma );
694
24.8k
        m_CABACEstimator->resetBits();
695
24.8k
        m_CABACEstimator->sao_offset_pars( testOffset[compIdx], compIdx, sliceEnabled[compIdx], bitDepths[CH_L] );
696
24.8k
        double rate = FRAC_BITS_SCALE * m_CABACEstimator->getEstFracBits();
697
24.8k
        cost = (double)dist[compIdx] + m_lambda[compIdx]*rate;
698
24.8k
        if(cost < minCost)
699
27
        {
700
27
          minCost = cost;
701
27
          modeDist[compIdx] = dist[compIdx];
702
27
          modeParam[compIdx]= testOffset[compIdx];
703
27
          ctxBestLuma = SAOCtx( m_CABACEstimator->getCtx() );
704
27
        }
705
24.8k
      }
706
4.97k
    }
707
4.97k
    m_CABACEstimator->getCtx() = SAOCtx( ctxBestLuma );
708
4.97k
  }
709
710
  //------ chroma --------//
711
//"off" case as initial cost
712
4.97k
  cost = 0;
713
4.97k
  previousFracBits = 0;
714
4.97k
  m_CABACEstimator->resetBits();
715
14.9k
  for(uint32_t componentIndex = COMP_Cb; componentIndex < numberOfComponents; componentIndex++)
716
9.95k
  {
717
9.95k
    const ComponentID component = ComponentID(componentIndex);
718
719
9.95k
    modeParam[component].modeIdc = SAO_MODE_OFF;
720
9.95k
    modeDist [component]         = 0;
721
9.95k
    m_CABACEstimator->sao_offset_pars( modeParam[component], component, sliceEnabled[component], bitDepths[CH_C] );
722
9.95k
    const uint64_t currentFracBits = m_CABACEstimator->getEstFracBits();
723
9.95k
    cost += m_lambda[component] * FRAC_BITS_SCALE * (currentFracBits - previousFracBits);
724
9.95k
    previousFracBits = currentFracBits;
725
9.95k
  }
726
727
4.97k
  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
29.8k
  for(int typeIdc=0; typeIdc< NUM_SAO_NEW_TYPES; typeIdc++)
732
24.8k
  {
733
24.8k
    m_CABACEstimator->getCtx() = SAOCtx( ctxBestLuma );
734
24.8k
    m_CABACEstimator->resetBits();
735
24.8k
    previousFracBits = 0;
736
24.8k
    cost = 0;
737
738
74.6k
    for(uint32_t componentIndex = COMP_Cb; componentIndex < numberOfComponents; componentIndex++)
739
49.7k
    {
740
49.7k
      const ComponentID component = ComponentID(componentIndex);
741
49.7k
      if(!sliceEnabled[component])
742
0
      {
743
0
        testOffset[component].modeIdc = SAO_MODE_OFF;
744
0
        dist[component]= 0;
745
0
        continue;
746
0
      }
747
49.7k
      testOffset[component].modeIdc = SAO_MODE_NEW;
748
49.7k
      testOffset[component].typeIdc = typeIdc;
749
750
      //derive offset & get distortion
751
49.7k
      deriveOffsets(component, bitDepths[CH_C], typeIdc, blkStats[ctuRsAddr][component][typeIdc], testOffset[component].offset, testOffset[component].typeAuxInfo);
752
49.7k
      invertQuantOffsets(component, typeIdc, testOffset[component].typeAuxInfo, invQuantOffset, testOffset[component].offset);
753
49.7k
      dist[component] = getDistortion(bitDepths[CH_C], typeIdc, testOffset[component].typeAuxInfo, invQuantOffset, blkStats[ctuRsAddr][component][typeIdc]);
754
49.7k
      m_CABACEstimator->sao_offset_pars( testOffset[component], component, sliceEnabled[component], bitDepths[CH_C] );
755
49.7k
      const uint64_t currentFracBits = m_CABACEstimator->getEstFracBits();
756
49.7k
      cost += dist[component] + (m_lambda[component] * FRAC_BITS_SCALE * (currentFracBits - previousFracBits));
757
49.7k
      previousFracBits = currentFracBits;
758
49.7k
    }
759
760
24.8k
    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
24.8k
  } // SAO_TYPE loop
771
772
  //----- re-gen rate & normalized cost----//
773
4.97k
  modeNormCost = 0;
774
19.9k
  for(uint32_t componentIndex = COMP_Y; componentIndex < numberOfComponents; componentIndex++)
775
14.9k
  {
776
14.9k
    modeNormCost += (double)modeDist[componentIndex] / m_lambda[componentIndex];
777
14.9k
  }
778
779
4.97k
  m_CABACEstimator->getCtx() = SAOCtx( ctxStartBlk );
780
4.97k
  m_CABACEstimator->resetBits();
781
4.97k
  m_CABACEstimator->sao_block_pars( modeParam, bitDepths, sliceEnabled, (mergeList[SAO_MERGE_LEFT]!= NULL), (mergeList[SAO_MERGE_ABOVE]!= NULL), false );
782
4.97k
  modeNormCost += FRAC_BITS_SCALE * m_CABACEstimator->getEstFracBits();
783
4.97k
}
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
4.97k
{
787
4.97k
  modeNormCost = MAX_DOUBLE;
788
789
4.97k
  double cost;
790
4.97k
  SAOBlkParam testBlkParam;
791
4.97k
  const int numberOfComponents = m_numberOfComponents;
792
793
4.97k
  const TempCtx ctxStart  ( m_CtxCache, SAOCtx( m_CABACEstimator->getCtx() ) );
794
4.97k
  TempCtx       ctxBest   ( m_CtxCache );
795
796
14.9k
  for(int mergeType=0; mergeType< NUM_SAO_MERGE_TYPES; mergeType++)
797
9.95k
  {
798
9.95k
    if(mergeList[mergeType] == NULL)
799
5.33k
    {
800
5.33k
      continue;
801
5.33k
    }
802
803
4.61k
    testBlkParam = *(mergeList[mergeType]);
804
    //normalized distortion
805
4.61k
    double normDist=0;
806
18.4k
    for(int compIdx = 0; compIdx < numberOfComponents; compIdx++)
807
13.8k
    {
808
13.8k
      testBlkParam[compIdx].modeIdc = SAO_MODE_MERGE;
809
13.8k
      testBlkParam[compIdx].typeIdc = mergeType;
810
811
13.8k
      SAOOffset& mergedOffsetParam = (*(mergeList[mergeType]))[compIdx];
812
813
13.8k
      if( mergedOffsetParam.modeIdc != SAO_MODE_OFF)
814
13
      {
815
        //offsets have been reconstructed. Don't call inversed quantization function.
816
13
        normDist += (((double)getDistortion(bitDepths[toChannelType(ComponentID(compIdx))], mergedOffsetParam.typeIdc, mergedOffsetParam.typeAuxInfo, mergedOffsetParam.offset, blkStats[ctuRsAddr][compIdx][mergedOffsetParam.typeIdc]))
817
13
                       /m_lambda[compIdx] );
818
13
      }
819
13.8k
    }
820
821
    //rate
822
4.61k
    m_CABACEstimator->getCtx() = SAOCtx( ctxStart );
823
4.61k
    m_CABACEstimator->resetBits();
824
4.61k
    m_CABACEstimator->sao_block_pars( testBlkParam, bitDepths, sliceEnabled, (mergeList[SAO_MERGE_LEFT]!= NULL), (mergeList[SAO_MERGE_ABOVE]!= NULL), false );
825
4.61k
    double rate = FRAC_BITS_SCALE * m_CABACEstimator->getEstFracBits();
826
4.61k
    cost = normDist+rate;
827
828
4.61k
    if(cost < modeNormCost)
829
3.48k
    {
830
3.48k
      modeNormCost = cost;
831
3.48k
      modeParam    = testBlkParam;
832
3.48k
      ctxBest      = SAOCtx( m_CABACEstimator->getCtx() );
833
3.48k
    }
834
4.61k
  }
835
4.97k
  if( modeNormCost < MAX_DOUBLE )
836
3.48k
  {
837
3.48k
    m_CABACEstimator->getCtx() = SAOCtx( ctxBest );
838
3.48k
  }
839
4.97k
}
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
14.9k
{
845
14.9k
  int x, startX, startY, endX, endY, edgeType, firstLineStartX, firstLineEndX;
846
14.9k
  int64_t *diff, *count;
847
14.9k
  Pel* srcLine, *orgLine;
848
14.9k
  const int skipLinesR = compIdx == COMP_Y ? 5 : 3;
849
14.9k
  const int skipLinesB = compIdx == COMP_Y ? 4 : 2;
850
851
89.5k
  for(int typeIdx=0; typeIdx< NUM_SAO_NEW_TYPES; typeIdx++)
852
74.6k
  {
853
74.6k
    SAOStatData& statsData= statsDataTypes[typeIdx];
854
74.6k
    statsData.reset();
855
74.6k
    srcLine = srcBlk;
856
74.6k
    orgLine = orgBlk;
857
74.6k
    diff    = statsData.diff;
858
74.6k
    count   = statsData.count;
859
74.6k
    switch(typeIdx)
860
74.6k
    {
861
14.9k
    case SAO_TYPE_EO_0:
862
14.9k
      {
863
14.9k
        endY   =  isBelowAvail ? (height - skipLinesB) : height;
864
14.9k
        startX = (isLeftAvail  ? 0 : 1);
865
14.9k
        endX   = (isRightAvail ? (width - skipLinesR) : (width - 1));
866
14.9k
        calcSaoStatisticsEo0(width,startX,endX,endY,srcLine,orgLine,srcStride,orgStride,count,diff);
867
14.9k
      }
868
14.9k
      break;
869
14.9k
    case SAO_TYPE_EO_90:
870
14.9k
      {
871
14.9k
        int8_t *signUpLine = &m_signLineBuf1[0];
872
14.9k
        startX = 0;
873
14.9k
        startY = isAboveAvail ? 0 : 1;
874
14.9k
        endX   = (isRightAvail ? (width - skipLinesR) : width);
875
14.9k
        endY   = isBelowAvail ? (height - skipLinesB) : (height - 1);
876
14.9k
        if (!isAboveAvail)
877
7.92k
        {
878
7.92k
          srcLine += srcStride;
879
7.92k
          orgLine += orgStride;
880
7.92k
        }
881
14.9k
        calcSaoStatisticsEo90(width,endX,startY,endY,srcLine,orgLine,srcStride,orgStride,count,diff,signUpLine);
882
14.9k
      }
883
14.9k
      break;
884
14.9k
    case SAO_TYPE_EO_135:
885
14.9k
      {
886
14.9k
        diff +=2;
887
14.9k
        count+=2;
888
14.9k
        int8_t *signUpLine, *signDownLine;
889
14.9k
        signUpLine  = &m_signLineBuf1[0];
890
14.9k
        signDownLine= &m_signLineBuf2[0];
891
14.9k
        startX = isLeftAvail  ? 0 : 1;
892
14.9k
        endX   = isRightAvail ? (width - skipLinesR): (width - 1);
893
14.9k
        endY   = isBelowAvail ? (height - skipLinesB) : (height - 1);
894
        //prepare 2nd line's upper sign
895
14.9k
        Pel* srcLineBelow = srcLine + srcStride;
896
862k
        for (x=startX; x<endX+1; x++)
897
847k
        {
898
847k
          signUpLine[x] = (int8_t)sgn(srcLineBelow[x] - srcLine[x-1]);
899
847k
        }
900
        //1st line
901
14.9k
        Pel* srcLineAbove = srcLine - srcStride;
902
14.9k
        firstLineStartX = isAboveLeftAvail ? 0    : 1;
903
14.9k
        firstLineEndX   = isAboveAvail     ? endX : 1;
904
411k
        for(x=firstLineStartX; x<firstLineEndX; x++)
905
396k
        {
906
396k
          edgeType = sgn(srcLine[x] - srcLineAbove[x-1]) - signUpLine[x+1];
907
396k
          diff [edgeType] += (orgLine[x] - srcLine[x]);
908
396k
          count[edgeType] ++;
909
396k
        }
910
14.9k
        srcLine  += srcStride;
911
14.9k
        orgLine  += orgStride;
912
14.9k
        calcSaoStatisticsEo135(width,startX,endX,endY,srcLine,orgLine,srcStride,orgStride,count,diff,signUpLine,signDownLine);
913
14.9k
      }
914
14.9k
      break;
915
14.9k
    case SAO_TYPE_EO_45:
916
14.9k
      {
917
14.9k
        diff +=2;
918
14.9k
        count+=2;
919
14.9k
        int8_t *signUpLine = &m_signLineBuf1[1];
920
921
14.9k
        startX = isLeftAvail  ? 0 : 1;
922
14.9k
        endX   = isRightAvail ? (width - skipLinesR) : (width - 1);
923
14.9k
        endY   = isBelowAvail ? (height - skipLinesB) : (height - 1);
924
925
        //prepare 2nd line upper sign
926
14.9k
        Pel* srcLineBelow = srcLine + srcStride;
927
862k
        for (x=startX-1; x<endX; x++)
928
847k
        {
929
847k
          signUpLine[x] = (int8_t)sgn(srcLineBelow[x] - srcLine[x+1]);
930
847k
        }
931
        //first line
932
14.9k
        Pel* srcLineAbove = srcLine - srcStride;
933
14.9k
        firstLineStartX = isAboveAvail ? startX : endX;
934
14.9k
        firstLineEndX   = (!isRightAvail && isAboveRightAvail) ? width : endX;
935
410k
        for(x=firstLineStartX; x<firstLineEndX; x++)
936
396k
        {
937
396k
          edgeType = sgn(srcLine[x] - srcLineAbove[x+1]) - signUpLine[x-1];
938
396k
          diff [edgeType] += (orgLine[x] - srcLine[x]);
939
396k
          count[edgeType] ++;
940
396k
        }
941
14.9k
        srcLine += srcStride;
942
14.9k
        orgLine += orgStride;
943
14.9k
        calcSaoStatisticsEo45(width,startX,endX,endY,srcLine,orgLine,srcStride,orgStride,count,diff,signUpLine);
944
14.9k
      }
945
14.9k
      break;
946
14.9k
    case SAO_TYPE_BO:
947
14.9k
      {
948
14.9k
        startX = 0;
949
14.9k
        endX   = isRightAvail ? (width - skipLinesR) : width;
950
14.9k
        endY   = isBelowAvail ? (height- skipLinesB) : height;
951
14.9k
        calcSaoStatisticsBo(width,endX,endY,srcLine,orgLine,srcStride,orgStride,channelBitDepth,count,diff);
952
14.9k
      }
953
14.9k
      break;
954
0
    default:
955
0
      {
956
0
        THROW("Not a supported SAO type");
957
0
      }
958
74.6k
    }
959
74.6k
  }
960
14.9k
}
961
962
void EncSampleAdaptiveOffset::deriveLoopFilterBoundaryAvailibility(CodingStructure& cs, const Position& pos, bool& isLeftAvail, bool& isAboveAvail, bool& isAboveLeftAvail) const
963
4.97k
{
964
4.97k
  const bool isLoopFiltAcrossSlicePPS = cs.pps->loopFilterAcrossSlicesEnabled;
965
4.97k
  const bool isLoopFiltAcrossTilePPS = cs.pps->loopFilterAcrossTilesEnabled;
966
967
4.97k
  const int width = cs.pcv->maxCUSize;
968
4.97k
  const int height = cs.pcv->maxCUSize;
969
4.97k
  const CodingUnit* cuCurr = cs.getCU(pos, CH_L, TREE_D);
970
4.97k
  const int ctuX = pos.x >> cs.pcv->maxCUSizeLog2;
971
4.97k
  const int ctuY = pos.y >> cs.pcv->maxCUSizeLog2;
972
4.97k
  const PPS* pps = cs.slice->pps;
973
4.97k
  const CodingUnit* cuLeft      = ctuX > 0 &&             pps->canFilterCtuBdry( ctuX, ctuY, -1, 0 ) ? cs.getCU(pos.offset(-width, 0), CH_L, TREE_D): nullptr;
974
4.97k
  const CodingUnit* cuAbove     = ctuY > 0 &&             pps->canFilterCtuBdry( ctuX, ctuY, 0, -1 ) ? cs.getCU(pos.offset(0, -height), CH_L, TREE_D): nullptr;
975
4.97k
  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
4.97k
  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
4.97k
  else
984
4.97k
  {
985
4.97k
    isLeftAvail      = (cuLeft != NULL);
986
4.97k
    isAboveAvail     = (cuAbove != NULL);
987
4.97k
    isAboveLeftAvail = (cuAboveLeft != NULL);
988
4.97k
  }
989
990
4.97k
  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
4.97k
  SubPic curSubPic = cs.pps->getSubPicFromCU(*cuCurr);
999
4.97k
  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
4.97k
}
1007
1008
} // namespace vvenc
1009
1010
//! \}
1011