Coverage Report

Created: 2026-04-01 07:49

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