Coverage Report

Created: 2026-06-15 06:25

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/vvdec/source/Lib/CommonLib/SampleAdaptiveOffset.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) 2018-2026, Fraunhofer-Gesellschaft zur Förderung der angewandten Forschung e.V. & The VVdeC 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
/** \file     SampleAdaptiveOffset.cpp
44
    \brief    sample adaptive offset class
45
*/
46
47
#include "SampleAdaptiveOffset.h"
48
49
#include "UnitTools.h"
50
#include "UnitPartitioner.h"
51
#include "CodingStructure.h"
52
#include "CommonLib/dtrace_codingstruct.h"
53
#include "CommonLib/dtrace_buffer.h"
54
#include "CommonLib/TimeProfiler.h"
55
56
#include <string.h>
57
#include <stdlib.h>
58
#include <stdio.h>
59
#include <math.h>
60
61
namespace vvdec
62
{
63
64
void SampleAdaptiveOffset::offsetBlock_core( const int            channelBitDepth,
65
                                             const ClpRng&        clpRng,
66
                                             int                  typeIdx,
67
                                             int*                 offset,
68
                                             int                  startIdx,
69
                                             const Pel*           srcBlk,
70
                                             Pel*                 resBlk,
71
                                             ptrdiff_t            srcStride,
72
                                             ptrdiff_t            resStride,
73
                                             int                  width,
74
                                             int                  height,
75
                                             bool                 isLeftAvail,
76
                                             bool                 isRightAvail,
77
                                             bool                 isAboveAvail,
78
                                             bool                 isBelowAvail,
79
                                             bool                 isAboveLeftAvail,
80
                                             bool                 isAboveRightAvail,
81
                                             bool                 isBelowLeftAvail,
82
                                             bool                 isBelowRightAvail,
83
                                             std::vector<int8_t>* m_signLineBuf1,
84
                                             std::vector<int8_t>* m_signLineBuf2,
85
                                             bool                 isCtuCrossedByVirtualBoundaries,
86
                                             int                  horVirBndryPos[],
87
                                             int                  verVirBndryPos[],
88
                                             int                  numHorVirBndry,
89
                                             int                  numVerVirBndry )
90
0
{
91
0
  int x,y, startX, startY, endX, endY, edgeType;
92
0
  int firstLineStartX, firstLineEndX, lastLineStartX, lastLineEndX;
93
0
  int8_t signLeft, signRight, signDown;
94
95
96
0
  const Pel* srcLine = srcBlk;
97
0
  Pel* resLine = resBlk;
98
99
0
  switch(typeIdx)
100
0
  {
101
0
  case SAO_TYPE_EO_0:
102
0
  {
103
0
    offset += 2;
104
0
    startX = isLeftAvail ? 0 : 1;
105
0
    endX   = isRightAvail ? width : (width -1);
106
0
    for (y=0; y< height; y++)
107
0
    {
108
0
      signLeft = (int8_t)sgn(srcLine[startX] - srcLine[startX-1]);
109
0
      for (x=startX; x< endX; x++)
110
0
      {
111
0
        signRight = (int8_t)sgn(srcLine[x] - srcLine[x+1]);
112
0
        if( isCtuCrossedByVirtualBoundaries && isProcessDisabled( x, y, numVerVirBndry, 0, verVirBndryPos, horVirBndryPos ) )
113
0
        {
114
0
          signLeft = -signRight;
115
0
          continue;
116
0
        }
117
0
        edgeType =  signRight + signLeft;
118
0
        signLeft  = -signRight;
119
120
0
        resLine[x] = ClipPel<int>( srcLine[x] + offset[edgeType], clpRng);
121
0
      }
122
123
0
      srcLine  += srcStride;
124
0
      resLine += resStride;
125
0
    }
126
0
  }
127
0
  break;
128
129
0
  case SAO_TYPE_EO_90:
130
0
  {
131
0
    offset += 2;
132
0
    int8_t *signUpLine = &m_signLineBuf1->front();
133
134
0
    startY = isAboveAvail ? 0 : 1;
135
0
    endY   = isBelowAvail ? height : height-1;
136
0
    if (!isAboveAvail)
137
0
    {
138
0
      srcLine += srcStride;
139
0
      resLine += resStride;
140
0
    }
141
142
0
    const Pel* srcLineAbove= srcLine- srcStride;
143
0
    for (x=0; x< width; x++)
144
0
    {
145
0
      signUpLine[x] = (int8_t)sgn(srcLine[x] - srcLineAbove[x]);
146
0
    }
147
148
0
    const Pel* srcLineBelow;
149
0
    for (y=startY; y<endY; y++)
150
0
    {
151
0
      srcLineBelow= srcLine+ srcStride;
152
153
0
      for (x=0; x< width; x++)
154
0
      {
155
0
        signDown  = (int8_t)sgn(srcLine[x] - srcLineBelow[x]);
156
0
        if( isCtuCrossedByVirtualBoundaries && isProcessDisabled( x, y, 0, numHorVirBndry, verVirBndryPos, horVirBndryPos ) )
157
0
        {
158
0
          signUpLine[x] = -signDown;
159
0
          continue;
160
0
        }
161
0
        edgeType = signDown + signUpLine[x];
162
0
        signUpLine[x]= -signDown;
163
164
0
        resLine[x] = ClipPel<int>(srcLine[x] + offset[edgeType], clpRng);
165
0
      }
166
0
      srcLine += srcStride;
167
0
      resLine += resStride;
168
0
    }
169
0
  }
170
0
  break;
171
0
  case SAO_TYPE_EO_135:
172
0
  {
173
0
    offset += 2;
174
0
    int8_t *signUpLine, *signDownLine, *signTmpLine;
175
176
0
    signUpLine  = &m_signLineBuf1->front();
177
0
    signDownLine= &m_signLineBuf2->front();
178
179
0
    startX = isLeftAvail ? 0 : 1 ;
180
0
    endX   = isRightAvail ? width : (width-1);
181
182
    //prepare 2nd line's upper sign
183
0
    const Pel* srcLineBelow= srcLine+ srcStride;
184
0
    for (x=startX; x< endX+1; x++)
185
0
    {
186
0
      signUpLine[x] = (int8_t)sgn(srcLineBelow[x] - srcLine[x- 1]);
187
0
    }
188
189
    //1st line
190
0
    const Pel* srcLineAbove= srcLine- srcStride;
191
0
    firstLineStartX = isAboveLeftAvail ? 0 : 1;
192
0
    firstLineEndX   = isAboveAvail? endX: 1;
193
0
    for(x= firstLineStartX; x< firstLineEndX; x++)
194
0
    {
195
0
      if( isCtuCrossedByVirtualBoundaries && isProcessDisabled( x, 0, numVerVirBndry, numHorVirBndry, verVirBndryPos, horVirBndryPos ) )
196
0
      {
197
0
        continue;
198
0
      }
199
0
      edgeType  =  sgn(srcLine[x] - srcLineAbove[x- 1]) - signUpLine[x+1];
200
201
0
      resLine[x] = ClipPel<int>( srcLine[x] + offset[edgeType], clpRng);
202
0
    }
203
0
    srcLine  += srcStride;
204
0
    resLine  += resStride;
205
206
207
    //middle lines
208
0
    for (y= 1; y< height-1; y++)
209
0
    {
210
0
      srcLineBelow= srcLine+ srcStride;
211
212
0
      for (x=startX; x<endX; x++)
213
0
      {
214
0
        signDown =  (int8_t)sgn(srcLine[x] - srcLineBelow[x+ 1]);
215
0
        if (isCtuCrossedByVirtualBoundaries && isProcessDisabled(x, y, numVerVirBndry, numHorVirBndry, verVirBndryPos, horVirBndryPos))
216
0
        {
217
0
          signDownLine[x + 1] = -signDown;
218
0
          continue;
219
0
        }
220
0
        edgeType =  signDown + signUpLine[x];
221
0
        resLine[x] = ClipPel<int>( srcLine[x] + offset[edgeType], clpRng);
222
223
0
        signDownLine[x+1] = -signDown;
224
0
      }
225
0
      signDownLine[startX] = (int8_t)sgn(srcLineBelow[startX] - srcLine[startX-1]);
226
227
0
      signTmpLine  = signUpLine;
228
0
      signUpLine   = signDownLine;
229
0
      signDownLine = signTmpLine;
230
231
0
      srcLine += srcStride;
232
0
      resLine += resStride;
233
0
    }
234
235
    //last line
236
0
    srcLineBelow= srcLine+ srcStride;
237
0
    lastLineStartX = isBelowAvail ? startX : (width -1);
238
0
    lastLineEndX   = isBelowRightAvail ? width : (width -1);
239
0
    for(x= lastLineStartX; x< lastLineEndX; x++)
240
0
    {
241
0
      if (isCtuCrossedByVirtualBoundaries && isProcessDisabled(x, height - 1, numVerVirBndry, numHorVirBndry, verVirBndryPos, horVirBndryPos))
242
0
      {
243
0
        continue;
244
0
      }
245
0
      edgeType =  sgn(srcLine[x] - srcLineBelow[x+ 1]) + signUpLine[x];
246
0
      resLine[x] = ClipPel<int>( srcLine[x] + offset[edgeType], clpRng);
247
0
    }
248
0
  }
249
0
  break;
250
0
  case SAO_TYPE_EO_45:
251
0
  {
252
0
    offset += 2;
253
0
    int8_t *signUpLine = &m_signLineBuf1->at(1);
254
255
0
    startX = isLeftAvail ? 0 : 1;
256
0
    endX   = isRightAvail ? width : (width -1);
257
258
    //prepare 2nd line upper sign
259
0
    const Pel* srcLineBelow= srcLine+ srcStride;
260
0
    for (x=startX-1; x< endX; x++)
261
0
    {
262
0
      signUpLine[x] = (int8_t)sgn(srcLineBelow[x] - srcLine[x+1]);
263
0
    }
264
265
266
    //first line
267
0
    const Pel* srcLineAbove= srcLine- srcStride;
268
0
    firstLineStartX = isAboveAvail ? startX : (width -1 );
269
0
    firstLineEndX   = isAboveRightAvail ? width : (width-1);
270
0
    for(x= firstLineStartX; x< firstLineEndX; x++)
271
0
    {
272
0
      if (isCtuCrossedByVirtualBoundaries && isProcessDisabled(x, 0, numVerVirBndry, numHorVirBndry, verVirBndryPos, horVirBndryPos))
273
0
      {
274
0
        continue;
275
0
      }
276
0
      edgeType = sgn(srcLine[x] - srcLineAbove[x+1]) -signUpLine[x-1];
277
0
      resLine[x] = ClipPel<int>(srcLine[x] + offset[edgeType], clpRng);
278
0
    }
279
0
    srcLine += srcStride;
280
0
    resLine += resStride;
281
282
    //middle lines
283
0
    for (y= 1; y< height-1; y++)
284
0
    {
285
0
      srcLineBelow= srcLine+ srcStride;
286
287
0
      for(x= startX; x< endX; x++)
288
0
      {
289
0
        signDown =  (int8_t)sgn(srcLine[x] - srcLineBelow[x-1]);
290
0
        if( isCtuCrossedByVirtualBoundaries && isProcessDisabled( x, y, numVerVirBndry, numHorVirBndry, verVirBndryPos, horVirBndryPos ) )
291
0
        {
292
0
          signUpLine[x - 1] = -signDown;
293
0
          continue;
294
0
        }
295
0
        edgeType =  signDown + signUpLine[x];
296
0
        resLine[x] = ClipPel<int>(srcLine[x] + offset[edgeType], clpRng);
297
0
        signUpLine[x-1] = -signDown;
298
0
      }
299
0
      signUpLine[endX-1] = (int8_t)sgn(srcLineBelow[endX-1] - srcLine[endX]);
300
0
      srcLine  += srcStride;
301
0
      resLine += resStride;
302
0
    }
303
304
    //last line
305
0
    srcLineBelow= srcLine+ srcStride;
306
0
    lastLineStartX = isBelowLeftAvail ? 0 : 1;
307
0
    lastLineEndX   = isBelowAvail ? endX : 1;
308
0
    for(x= lastLineStartX; x< lastLineEndX; x++)
309
0
    {
310
0
      if( isCtuCrossedByVirtualBoundaries && isProcessDisabled( x, height - 1, numVerVirBndry, numHorVirBndry, verVirBndryPos, horVirBndryPos ) )
311
0
      {
312
0
        continue;
313
0
      }
314
0
      edgeType = sgn(srcLine[x] - srcLineBelow[x-1]) + signUpLine[x];
315
0
      resLine[x] = ClipPel<int>(srcLine[x] + offset[edgeType], clpRng);
316
317
0
    }
318
0
  }
319
0
  break;
320
0
  case SAO_TYPE_BO:
321
0
  {
322
0
    const int shiftBits = channelBitDepth - NUM_SAO_BO_CLASSES_LOG2;
323
324
0
    for (y=0; y< height; y++)
325
0
    {
326
0
      for (x=0; x< width; x++)
327
0
      {
328
0
        resLine[x] = ClipPel<int>(srcLine[x] + offset[srcLine[x] >> shiftBits], clpRng );
329
0
      }
330
0
      srcLine += srcStride;
331
0
      resLine += resStride;
332
0
    }
333
0
  }
334
0
  break;
335
0
  default:
336
0
  {
337
0
    THROW_FATAL( "Not a supported SAO types\n" );
338
0
  }
339
0
  }
340
0
}
341
342
SampleAdaptiveOffset::SampleAdaptiveOffset( bool enableOpt )
343
2.13k
{
344
2.13k
  offsetBlock = offsetBlock_core;
345
346
2.13k
  if( enableOpt )
347
2.13k
  {
348
#if ENABLE_SIMD_OPT_SAO && defined( TARGET_SIMD_X86 )
349
    initSampleAdaptiveOffsetX86();
350
#endif
351
#if ENABLE_SIMD_OPT_SAO && defined( TARGET_SIMD_ARM )
352
    initSampleAdaptiveOffsetARM();
353
#endif
354
2.13k
  }
355
2.13k
}
356
357
SampleAdaptiveOffset::~SampleAdaptiveOffset()
358
2.13k
{
359
2.13k
  destroy();
360
2.13k
}
361
362
void SampleAdaptiveOffset::create( int picWidth, int picHeight, ChromaFormat format, uint32_t maxCUWidth, uint32_t maxCUHeight, uint32_t maxCUDepth, uint32_t bitShift, PelUnitBuf& unitBuf )
363
0
{
364
0
  m_tempBuf = unitBuf;
365
366
  //bit-depth related
367
0
  m_offsetStepLog2 = bitShift;
368
0
  m_numberOfComponents = getNumberValidComponents(format);
369
0
}
370
371
void SampleAdaptiveOffset::destroy()
372
2.13k
{
373
2.13k
}
374
375
#if 0
376
void SampleAdaptiveOffset::SAOProcess( CodingStructure& cs )
377
{
378
  const PreCalcValues& pcv = *cs.pcv;
379
  for( uint32_t yPos = 0; yPos < pcv.lumaHeight; yPos += pcv.maxCUHeight )
380
  {
381
    for( uint32_t xPos = 0; xPos < pcv.lumaWidth; xPos += pcv.maxCUWidth )
382
    {
383
      const uint32_t width  = (xPos + pcv.maxCUWidth  > pcv.lumaWidth)  ? (pcv.lumaWidth - xPos)  : pcv.maxCUWidth;
384
      const uint32_t height = (yPos + pcv.maxCUHeight > pcv.lumaHeight) ? (pcv.lumaHeight - yPos) : pcv.maxCUHeight;
385
386
      SAOProcessCTU( cs, UnitArea(cs.area.chromaFormat, Area(xPos , yPos, width, height)) );
387
    }
388
  }
389
390
  DTRACE_UPDATE(g_trace_ctx, (std::make_pair("poc", cs.slice->getPOC())));
391
  DTRACE_PIC_COMP(D_REC_CB_LUMA_SAO, cs, cs.getRecoBuf(), COMPONENT_Y);
392
  DTRACE_PIC_COMP(D_REC_CB_CHROMA_SAO, cs, cs.getRecoBuf(), COMPONENT_Cb);
393
  DTRACE_PIC_COMP(D_REC_CB_CHROMA_SAO, cs, cs.getRecoBuf(), COMPONENT_Cr);
394
395
  DTRACE    ( g_trace_ctx, D_CRC, "SAO" );
396
  DTRACE_CRC( g_trace_ctx, D_CRC, cs, cs.getRecoBuf() );
397
}
398
#endif
399
400
void SampleAdaptiveOffset::SAOPrepareCTULine( CodingStructure &cs, const UnitArea &lineArea )
401
0
{
402
0
  PROFILER_SCOPE_AND_STAGE( 1, g_timeProfiler, P_SAO );
403
404
0
  const PreCalcValues& pcv = *cs.pcv;
405
0
  PelUnitBuf           rec = cs.getRecoBuf();
406
407
0
  const int height = lineArea.lumaSize().height;
408
0
  const int y      = lineArea.lumaPos().y;
409
410
0
  const int cpyY       = y == 0 ? 0 : y + 2;
411
0
        int cpyHeight  = height;
412
0
            cpyHeight -= y == 0 ? 0 : 2;
413
0
            cpyHeight -= cpyY + cpyHeight >= pcv.lumaHeight ? 0 : 2;
414
0
            cpyHeight  = std::min<int>( cpyHeight, pcv.lumaHeight - cpyY );
415
416
0
  for( int x = 0; x < pcv.lumaWidth; x += pcv.maxCUWidth )
417
0
  {
418
0
    const int ctuRsAddr = getCtuAddr( Position( x, y ), *cs.pcv );
419
420
0
    SAOBlkParam* mergeList[NUM_SAO_MERGE_TYPES] = { nullptr, nullptr };
421
0
    getMergeList( cs, ctuRsAddr, mergeList );
422
423
0
    reconstructBlkSAOParam( cs.m_ctuData[ctuRsAddr].saoParam, mergeList );
424
0
  }
425
426
0
  const int numComp = getNumberValidComponents( pcv.chrFormat );
427
428
0
  for( int i = 0; i < numComp; i++ )
429
0
  {
430
0
    const ComponentID compID = ComponentID( i );
431
432
0
    const int currCpyY =   cpyY            >> getComponentScaleY( compID, pcv.chrFormat );
433
0
    const int currCpyH =   cpyHeight       >> getComponentScaleY( compID, pcv.chrFormat );
434
0
    const int currCtuW =   pcv.maxCUWidth  >> getComponentScaleX( compID, pcv.chrFormat );
435
0
    const int currPicW =   pcv.lumaWidth   >> getComponentScaleX( compID, pcv.chrFormat );
436
    
437
0
    const ptrdiff_t src_stride =       rec.bufs[ compID ].stride;
438
0
    const ptrdiff_t dst_stride = m_tempBuf.bufs[ compID ].stride;
439
440
0
    const Pel* src =       rec.bufs[ compID ].bufAt( 0, currCpyY );
441
0
          Pel* dst = m_tempBuf.bufs[ compID ].bufAt( 0, currCpyY );
442
443
0
    for( int j = currCpyY; j < ( currCpyY + currCpyH ); j++, src += src_stride, dst += dst_stride )
444
0
    {
445
0
      int numCpy   = 0;
446
0
      int cpyStart = 0;
447
448
0
      int ctuRsAddr = getCtuAddr( Position( 0, y ), *cs.pcv );
449
450
0
      for( int x = 0, currCpyX = 0; x <= pcv.widthInCtus; x++, currCpyX += currCtuW, ctuRsAddr++ )
451
0
      {
452
0
        if( x < pcv.widthInCtus && cs.m_ctuData[ctuRsAddr].saoParam[i].modeIdc != SAO_MODE_OFF )
453
0
        {
454
0
          if( !numCpy ) cpyStart = currCpyX - !!x;
455
0
          numCpy++;
456
0
        }
457
0
        else if( numCpy )
458
0
        {
459
          // copy
460
0
          int cpyLen = numCpy * currCtuW + !!cpyStart + 1;
461
0
          if( cpyStart + cpyLen > currPicW ) cpyLen = currPicW - cpyStart;
462
0
          memcpy( dst + cpyStart, src + cpyStart, cpyLen * sizeof( Pel ) );
463
0
          numCpy = 0;
464
0
        }
465
0
      }
466
0
    }
467
0
  }
468
469
0
  const int cpyY2      = cpyY + cpyHeight;
470
0
  const int cpyHeight2 = 4;
471
  
472
0
  if( cpyY2 < pcv.lumaHeight )
473
0
  {
474
0
    const UnitArea cpyLine2( pcv.chrFormat, Area( 0, cpyY2, pcv.lumaWidth, cpyHeight2 ) );
475
  
476
0
    m_tempBuf.subBuf( cpyLine2 ).copyFrom( rec.subBuf( cpyLine2 ) );
477
0
  }
478
0
}
479
480
void SampleAdaptiveOffset::SAOProcessCTULine( CodingStructure &cs, const UnitArea &lineArea )
481
0
{
482
0
  PROFILER_SCOPE_AND_STAGE( 1, g_timeProfiler, P_SAO );
483
484
0
  const PreCalcValues& pcv = *cs.pcv;
485
0
  PelUnitBuf           rec = cs.getRecoBuf();
486
487
0
  const int height         = lineArea.lumaSize().height;
488
0
  const int y              = lineArea.lumaPos().y;
489
  
490
0
  bool anySaoBlk = false;
491
  
492
0
  for( int x = 0; x < pcv.lumaWidth; x += pcv.maxCUWidth )
493
0
  {
494
0
    const int ctuRsAddr = getCtuAddr( Position( x, y ), *cs.pcv );
495
496
0
    for( int i = 0; i < MAX_NUM_COMPONENT; i++ )
497
0
    {
498
0
      if( cs.m_ctuData[ctuRsAddr].saoParam[i].modeIdc != SAO_MODE_OFF )
499
0
      {
500
0
        anySaoBlk = true;
501
0
      }
502
0
    }
503
0
  }
504
505
0
  if( !anySaoBlk ) return;
506
507
0
  std::vector<int8_t> signLineBuf1;
508
0
  std::vector<int8_t> signLineBuf2;
509
510
0
  for( int x = 0; x < pcv.lumaWidth; x += pcv.maxCUWidth )
511
0
  {
512
0
    const int width = ( x + pcv.maxCUWidth > pcv.lumaWidth ) ? ( pcv.lumaWidth - x ) : pcv.maxCUWidth;
513
514
0
    const UnitArea ctuArea( pcv.chrFormat, Area( x, y, width, height ) );
515
516
0
    const int ctuRsAddr = getCtuAddr( ctuArea.lumaPos(), *cs.pcv );
517
518
0
    offsetCTU( ctuArea, m_tempBuf, rec, cs.m_ctuData[ctuRsAddr].saoParam, cs, signLineBuf1, signLineBuf2 );
519
0
  }
520
0
}
521
522
void SampleAdaptiveOffset::SAOProcessCTU( CodingStructure &cs, const UnitArea &ctuArea )
523
0
{
524
0
  PROFILER_SCOPE_AND_STAGE( 1, g_timeProfiler, P_SAO );
525
526
0
  PelUnitBuf           rec = cs.getRecoBuf();
527
528
0
  const int ctuRsAddr = getCtuAddr( ctuArea.lumaPos(), *cs.pcv );
529
530
0
  bool anySaoBlk = false;
531
532
0
  for( int i = 0; i < MAX_NUM_COMPONENT; i++ )
533
0
  {
534
0
    if( cs.m_ctuData[ctuRsAddr].saoParam[i].modeIdc != SAO_MODE_OFF )
535
0
    {
536
0
      anySaoBlk = true;
537
0
    }
538
0
  }
539
540
0
  if( !anySaoBlk ) return;
541
542
0
  std::vector<int8_t> signLineBuf1;
543
0
  std::vector<int8_t> signLineBuf2;
544
545
0
  offsetCTU( ctuArea, m_tempBuf, rec, cs.m_ctuData[ctuRsAddr].saoParam, cs, signLineBuf1, signLineBuf2 );
546
0
}
547
548
void SampleAdaptiveOffset::invertQuantOffsets(ComponentID compIdx, int typeIdc, int typeAuxInfo, int* dstOffsets, int* srcOffsets) const
549
0
{
550
0
  int codedOffset[MAX_NUM_SAO_CLASSES];
551
552
0
  ::memcpy(codedOffset, srcOffsets, sizeof(int)*MAX_NUM_SAO_CLASSES);
553
0
  ::memset(dstOffsets, 0, sizeof(int)*MAX_NUM_SAO_CLASSES);
554
555
0
  if(typeIdc == SAO_TYPE_START_BO)
556
0
  {
557
0
    for(int i=0; i< 4; i++)
558
0
    {
559
0
      dstOffsets[(typeAuxInfo+ i)%NUM_SAO_BO_CLASSES] = codedOffset[(typeAuxInfo+ i)%NUM_SAO_BO_CLASSES]*(1<<m_offsetStepLog2);
560
0
    }
561
0
  }
562
0
  else //EO
563
0
  {
564
0
    for(int i=0; i< NUM_SAO_EO_CLASSES; i++)
565
0
    {
566
0
      dstOffsets[i] = codedOffset[i] *(1<<m_offsetStepLog2);
567
0
    }
568
0
    CHECK(dstOffsets[SAO_CLASS_EO_PLAIN] != 0, "EO offset is not '0'"); //keep EO plain offset as zero
569
0
  }
570
571
0
}
572
573
int SampleAdaptiveOffset::getMergeList(CodingStructure& cs, int ctuRsAddr, SAOBlkParam* mergeList[NUM_SAO_MERGE_TYPES])
574
0
{
575
0
  const PreCalcValues& pcv = *cs.pcv;
576
577
0
  const int ctuX       = ctuRsAddr % pcv.widthInCtus;
578
0
  const int ctuY       = ctuRsAddr / pcv.widthInCtus;
579
0
  const CodingUnit& cu = *cs.getCtuData( ctuRsAddr ).cuPtr[CH_L][0];
580
0
  int mergedCTUPos;
581
0
  int numValidMergeCandidates = 0;
582
583
0
  for( auto mergeType: { SAO_MERGE_LEFT, SAO_MERGE_ABOVE } )
584
0
  {
585
0
    SAOBlkParam* mergeCandidate = NULL;
586
587
0
    switch(mergeType)
588
0
    {
589
0
    case SAO_MERGE_ABOVE:
590
0
      if( ctuY > 0 )
591
0
      {
592
0
        mergedCTUPos = ctuRsAddr - pcv.widthInCtus;
593
0
        if( cu.above )
594
0
        {
595
0
          mergeCandidate = &( cs.m_ctuData[mergedCTUPos].saoParam );
596
0
        }
597
0
      }
598
0
      break;
599
0
    case SAO_MERGE_LEFT:
600
0
      if( ctuX > 0 )
601
0
      {
602
0
        mergedCTUPos = ctuRsAddr - 1;
603
0
        if( cu.left )
604
0
        {
605
0
          mergeCandidate = &( cs.m_ctuData[mergedCTUPos].saoParam );
606
0
        }
607
0
      }
608
0
      break;
609
0
    default:
610
0
      THROW_FATAL( "not a supported merge type" );
611
0
    }
612
613
0
    mergeList[mergeType] = mergeCandidate;
614
0
    if (mergeCandidate != NULL)
615
0
    {
616
0
      numValidMergeCandidates++;
617
0
    }
618
0
  }
619
620
0
  return numValidMergeCandidates;
621
0
}
622
623
624
void SampleAdaptiveOffset::reconstructBlkSAOParam(SAOBlkParam& recParam, SAOBlkParam* mergeList[NUM_SAO_MERGE_TYPES]) const
625
0
{
626
0
  const int numberOfComponents = m_numberOfComponents;
627
0
  for(int compIdx = 0; compIdx < numberOfComponents; compIdx++)
628
0
  {
629
0
    const ComponentID component = ComponentID(compIdx);
630
0
    SAOOffset& offsetParam = recParam[component];
631
632
0
    if(offsetParam.modeIdc == SAO_MODE_OFF)
633
0
    {
634
0
      continue;
635
0
    }
636
637
0
    switch(offsetParam.modeIdc)
638
0
    {
639
0
    case SAO_MODE_NEW:
640
0
      {
641
0
        invertQuantOffsets(component, offsetParam.typeIdc, offsetParam.typeAuxInfo, offsetParam.offset, offsetParam.offset);
642
0
      }
643
0
      break;
644
0
    case SAO_MODE_MERGE:
645
0
      {
646
0
        const SAOBlkParam* mergeTarget = mergeList[offsetParam.typeIdc];
647
0
        CHECK(mergeTarget == NULL, "Merge target does not exist");
648
649
0
        offsetParam = (*mergeTarget)[component];
650
0
      }
651
0
      break;
652
0
    default:
653
0
      {
654
0
        THROW_RECOVERABLE( "Not a supported mode" );
655
0
      }
656
0
    }
657
0
  }
658
0
}
659
660
661
void SampleAdaptiveOffset::offsetCTU( const UnitArea& area, const CPelUnitBuf& src, PelUnitBuf& res, SAOBlkParam& saoblkParam, CodingStructure& cs, std::vector<int8_t> &signLineBuf1, std::vector<int8_t> &signLineBuf2 )
662
0
{
663
0
  const uint32_t numberOfComponents = getNumberValidComponents( area.chromaFormat );
664
0
  if( std::all_of( &saoblkParam[0],
665
0
                   &saoblkParam[numberOfComponents],
666
0
                   [](const SAOOffset & p){ return p.modeIdc == SAO_MODE_OFF; } ) )
667
0
  {
668
0
    return;
669
0
  }
670
671
0
  bool isLeftAvail, isRightAvail, isAboveAvail, isBelowAvail, isAboveLeftAvail, isAboveRightAvail, isBelowLeftAvail, isBelowRightAvail;
672
673
  // block boundary availability
674
0
  deriveLoopFilterBoundaryAvailibility( cs, area.Y(), isLeftAvail, isRightAvail, isAboveAvail, isBelowAvail, isAboveLeftAvail, isAboveRightAvail, isBelowLeftAvail, isBelowRightAvail );
675
676
0
  const size_t lineBufferSize = area.Y().width + 1;
677
0
  if (signLineBuf1.size() < lineBufferSize)
678
0
  {
679
0
    signLineBuf1.resize(lineBufferSize);
680
0
    signLineBuf2.resize(lineBufferSize);
681
0
  }
682
683
0
  int  numHorVirBndry       = 0;
684
0
  int  numVerVirBndry       = 0;
685
0
  int  horVirBndryPos[]     = { -1, -1, -1 };
686
0
  int  verVirBndryPos[]     = { -1, -1, -1 };
687
0
  int  horVirBndryPosComp[] = { -1, -1, -1 };
688
0
  int  verVirBndryPosComp[] = { -1, -1, -1 };
689
0
  bool isCtuCrossedByVirtualBoundaries = isCrossedByVirtualBoundaries( cs.picHeader.get(),
690
0
                                                                       area.Y(),
691
0
                                                                       numHorVirBndry, numVerVirBndry,
692
0
                                                                       horVirBndryPos, verVirBndryPos );
693
0
  if( isCtuCrossedByVirtualBoundaries )
694
0
  {
695
0
    CHECK( numHorVirBndry >= (int)( sizeof(horVirBndryPos) / sizeof(horVirBndryPos[0]) ), "Too many virtual boundaries" );
696
0
    CHECK( numVerVirBndry >= (int)( sizeof(verVirBndryPos) / sizeof(verVirBndryPos[0]) ), "Too many virtual boundaries" );
697
0
  }
698
699
0
  for(int compIdx = 0; compIdx < numberOfComponents; compIdx++)
700
0
  {
701
0
    const ComponentID compID = ComponentID(compIdx);
702
0
    const CompArea& compArea = area.block(compID);
703
0
    SAOOffset& ctbOffset     = saoblkParam[compIdx];
704
705
0
    if(ctbOffset.modeIdc != SAO_MODE_OFF)
706
0
    {
707
0
      ptrdiff_t  srcStride = src.get(compID).stride;
708
0
      const Pel* srcBlk    = src.get(compID).bufAt(compArea);
709
0
      ptrdiff_t  resStride = res.get(compID).stride;
710
0
      Pel* resBlk          = res.get(compID).bufAt(compArea);
711
712
0
      for (int i = 0; i < numHorVirBndry; i++)
713
0
      {
714
0
        horVirBndryPosComp[i] = (horVirBndryPos[i] >> getComponentScaleY(compID, area.chromaFormat)) - compArea.y;
715
0
      }
716
0
      for (int i = 0; i < numVerVirBndry; i++)
717
0
      {
718
0
        verVirBndryPosComp[i] = (verVirBndryPos[i] >> getComponentScaleX(compID, area.chromaFormat)) - compArea.x;
719
0
      }
720
721
0
      offsetBlock( cs.sps->getBitDepth(),
722
0
                   cs.getCtuData(cs.ctuRsAddr(area.Y().pos(), CH_L)).cuPtr[0][0]->slice->clpRng(compID),
723
0
                   ctbOffset.typeIdc, ctbOffset.offset,ctbOffset.typeAuxInfo
724
0
                  , srcBlk, resBlk, srcStride, resStride, compArea.width, compArea.height
725
0
                  , isLeftAvail, isRightAvail
726
0
                  , isAboveAvail, isBelowAvail
727
0
                  , isAboveLeftAvail, isAboveRightAvail
728
0
                  , isBelowLeftAvail, isBelowRightAvail
729
0
                  , &signLineBuf1
730
0
                  , &signLineBuf2
731
0
                  , isCtuCrossedByVirtualBoundaries
732
0
                  , horVirBndryPosComp
733
0
                  , verVirBndryPosComp
734
0
                  , numHorVirBndry
735
0
                  , numVerVirBndry
736
0
                  );
737
0
    }
738
0
  } //compIdx
739
0
}
740
741
void SampleAdaptiveOffset::deriveLoopFilterBoundaryAvailibility( CodingStructure& cs,
742
                                                                 const Position&  pos,
743
                                                                 bool&            isLeftAvail,
744
                                                                 bool&            isRightAvail,
745
                                                                 bool&            isAboveAvail,
746
                                                                 bool&            isBelowAvail,
747
                                                                 bool&            isAboveLeftAvail,
748
                                                                 bool&            isAboveRightAvail,
749
                                                                 bool&            isBelowLeftAvail,
750
                                                                 bool&            isBelowRightAvail ) const
751
0
{
752
0
  const int ctusz  = cs.pcv->maxCUWidth;
753
0
  const int ctuX   = pos.x / ctusz;
754
0
  const int ctuY   = pos.y / ctusz;
755
0
  const int width  = cs.pcv->widthInCtus;
756
0
  const int height = cs.pcv->heightInCtus;
757
758
0
  const CodingUnit* cuCurr        =                       cs.getCtuData( ctuX,     ctuY     ).cuPtr[0][0];
759
0
  const CodingUnit* cuLeft        = ctuX     > 0        ? cs.getCtuData( ctuX - 1, ctuY     ).cuPtr[0][0] : nullptr;
760
0
  const CodingUnit* cuRight       = ctuX + 1 < width    ? cs.getCtuData( ctuX + 1, ctuY     ).cuPtr[0][0] : nullptr;
761
0
  const CodingUnit* cuAbove       = ctuY     > 0        ? cs.getCtuData( ctuX,     ctuY - 1 ).cuPtr[0][0] : nullptr;
762
0
  const CodingUnit* cuBelow       = ctuY + 1 < height   ? cs.getCtuData( ctuX,     ctuY + 1 ).cuPtr[0][0] : nullptr;
763
0
  const CodingUnit* cuAboveLeft   = cuLeft  && cuAbove  ? cs.getCtuData( ctuX - 1, ctuY - 1 ).cuPtr[0][0] : nullptr;
764
0
  const CodingUnit* cuAboveRight  = cuRight && cuAbove  ? cs.getCtuData( ctuX + 1, ctuY - 1 ).cuPtr[0][0] : nullptr;
765
0
  const CodingUnit* cuBelowLeft   = cuLeft  && cuBelow  ? cs.getCtuData( ctuX - 1, ctuY + 1 ).cuPtr[0][0] : nullptr;
766
0
  const CodingUnit* cuBelowRight  = cuRight && cuBelow  ? cs.getCtuData( ctuX + 1, ctuY + 1 ).cuPtr[0][0] : nullptr;
767
768
0
  isLeftAvail       = (cuLeft       != NULL);
769
0
  isAboveAvail      = (cuAbove      != NULL);
770
0
  isRightAvail      = (cuRight      != NULL);
771
0
  isBelowAvail      = (cuBelow      != NULL);
772
0
  isAboveLeftAvail  = (cuAboveLeft  != NULL);
773
0
  isAboveRightAvail = (cuAboveRight != NULL);
774
0
  isBelowLeftAvail  = (cuBelowLeft  != NULL);
775
0
  isBelowRightAvail = (cuBelowRight != NULL);
776
777
  // check cross slice flags
778
0
  const bool isLoopFilterAcrossSlicePPS = cs.pps->getLoopFilterAcrossSlicesEnabledFlag();
779
0
  if (!isLoopFilterAcrossSlicePPS)
780
0
  {
781
0
    isLeftAvail       = isLeftAvail       && CU::isSameSlice(*cuCurr, *cuLeft);
782
0
    isAboveAvail      = isAboveAvail      && CU::isSameSlice(*cuCurr, *cuAbove);
783
0
    isRightAvail      = isRightAvail      && CU::isSameSlice(*cuCurr, *cuRight);
784
0
    isBelowAvail      = isBelowAvail      && CU::isSameSlice(*cuCurr, *cuBelow);
785
0
    isAboveLeftAvail  = isAboveLeftAvail  && CU::isSameSlice(*cuCurr, *cuAboveLeft);
786
0
    isAboveRightAvail = isAboveRightAvail && CU::isSameSlice(*cuCurr, *cuAboveRight);
787
0
    isBelowLeftAvail  = isBelowLeftAvail  && CU::isSameSlice(*cuCurr, *cuBelowLeft);
788
0
    isBelowRightAvail = isBelowRightAvail && CU::isSameSlice(*cuCurr, *cuBelowRight);
789
0
  }
790
791
  // check cross tile flags
792
0
  const bool isLoopFilterAcrossTilePPS = cs.pps->getLoopFilterAcrossTilesEnabledFlag();
793
0
  if (!isLoopFilterAcrossTilePPS)
794
0
  {
795
0
    isLeftAvail       = isLeftAvail       && CU::isSameTile(*cuCurr, *cuLeft);
796
0
    isAboveAvail      = isAboveAvail      && CU::isSameTile(*cuCurr, *cuAbove);
797
0
    isRightAvail      = isRightAvail      && CU::isSameTile(*cuCurr, *cuRight);
798
0
    isBelowAvail      = isBelowAvail      && CU::isSameTile(*cuCurr, *cuBelow);
799
0
    isAboveLeftAvail  = isAboveLeftAvail  && CU::isSameTile(*cuCurr, *cuAboveLeft);
800
0
    isAboveRightAvail = isAboveRightAvail && CU::isSameTile(*cuCurr, *cuAboveRight);
801
0
    isBelowLeftAvail  = isBelowLeftAvail  && CU::isSameTile(*cuCurr, *cuBelowLeft);
802
0
    isBelowRightAvail = isBelowRightAvail && CU::isSameTile(*cuCurr, *cuBelowRight);
803
0
  }
804
805
  // check cross subpic flags
806
0
  if( cs.sps->getSubPicInfoPresentFlag() )
807
0
  {
808
0
    const SubPic& curSubPic = cs.pps->getSubPicFromCU(*cuCurr);
809
0
    if( !curSubPic.getloopFilterAcrossSubPicEnabledFlag() )
810
0
    {
811
0
      isLeftAvail       = isLeftAvail       && CU::isSameSubPic(*cuCurr, *cuLeft);
812
0
      isAboveAvail      = isAboveAvail      && CU::isSameSubPic(*cuCurr, *cuAbove);
813
0
      isRightAvail      = isRightAvail      && CU::isSameSubPic(*cuCurr, *cuRight);
814
0
      isBelowAvail      = isBelowAvail      && CU::isSameSubPic(*cuCurr, *cuBelow);
815
0
      isAboveLeftAvail  = isAboveLeftAvail  && CU::isSameSubPic(*cuCurr, *cuAboveLeft);
816
0
      isAboveRightAvail = isAboveRightAvail && CU::isSameSubPic(*cuCurr, *cuAboveRight);
817
0
      isBelowLeftAvail  = isBelowLeftAvail  && CU::isSameSubPic(*cuCurr, *cuBelowLeft);
818
0
      isBelowRightAvail = isBelowRightAvail && CU::isSameSubPic(*cuCurr, *cuBelowRight);
819
0
    }
820
0
  }
821
0
}
822
823
bool SampleAdaptiveOffset::isProcessDisabled( int xPos, int yPos, int numVerVirBndry, int numHorVirBndry, int verVirBndryPos[], int horVirBndryPos[] )
824
0
{
825
0
  for (int i = 0; i < numVerVirBndry; i++)
826
0
  {
827
0
    if ((xPos == verVirBndryPos[i]) || (xPos == verVirBndryPos[i] - 1))
828
0
    {
829
0
      return true;
830
0
    }
831
0
  }
832
0
  for (int i = 0; i < numHorVirBndry; i++)
833
0
  {
834
0
    if ((yPos == horVirBndryPos[i]) || (yPos == horVirBndryPos[i] - 1))
835
0
    {
836
0
      return true;
837
0
    }
838
0
  }
839
840
0
  return false;
841
0
}
842
843
}