Coverage Report

Created: 2026-04-01 07:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/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()
343
0
{
344
0
  destroy();
345
0
}
346
347
void SampleAdaptiveOffset::create( int picWidth, int picHeight, ChromaFormat format, uint32_t maxCUWidth, uint32_t maxCUHeight, uint32_t maxCUDepth, uint32_t bitShift, PelUnitBuf& unitBuf )
348
0
{
349
0
  offsetBlock = offsetBlock_core;
350
0
#if ENABLE_SIMD_OPT_SAO && defined( TARGET_SIMD_X86 )
351
0
  initSampleAdaptiveOffsetX86();
352
0
#endif
353
354
0
  m_tempBuf = unitBuf;
355
356
  //bit-depth related
357
0
  m_offsetStepLog2 = bitShift;
358
0
  m_numberOfComponents = getNumberValidComponents(format);
359
0
}
360
361
void SampleAdaptiveOffset::destroy()
362
0
{
363
0
}
364
365
#if 0
366
void SampleAdaptiveOffset::SAOProcess( CodingStructure& cs )
367
{
368
  const PreCalcValues& pcv = *cs.pcv;
369
  for( uint32_t yPos = 0; yPos < pcv.lumaHeight; yPos += pcv.maxCUHeight )
370
  {
371
    for( uint32_t xPos = 0; xPos < pcv.lumaWidth; xPos += pcv.maxCUWidth )
372
    {
373
      const uint32_t width  = (xPos + pcv.maxCUWidth  > pcv.lumaWidth)  ? (pcv.lumaWidth - xPos)  : pcv.maxCUWidth;
374
      const uint32_t height = (yPos + pcv.maxCUHeight > pcv.lumaHeight) ? (pcv.lumaHeight - yPos) : pcv.maxCUHeight;
375
376
      SAOProcessCTU( cs, UnitArea(cs.area.chromaFormat, Area(xPos , yPos, width, height)) );
377
    }
378
  }
379
380
  DTRACE_UPDATE(g_trace_ctx, (std::make_pair("poc", cs.slice->getPOC())));
381
  DTRACE_PIC_COMP(D_REC_CB_LUMA_SAO, cs, cs.getRecoBuf(), COMPONENT_Y);
382
  DTRACE_PIC_COMP(D_REC_CB_CHROMA_SAO, cs, cs.getRecoBuf(), COMPONENT_Cb);
383
  DTRACE_PIC_COMP(D_REC_CB_CHROMA_SAO, cs, cs.getRecoBuf(), COMPONENT_Cr);
384
385
  DTRACE    ( g_trace_ctx, D_CRC, "SAO" );
386
  DTRACE_CRC( g_trace_ctx, D_CRC, cs, cs.getRecoBuf() );
387
}
388
#endif
389
390
void SampleAdaptiveOffset::SAOPrepareCTULine( CodingStructure &cs, const UnitArea &lineArea )
391
0
{
392
0
  PROFILER_SCOPE_AND_STAGE( 1, g_timeProfiler, P_SAO );
393
394
0
  const PreCalcValues& pcv = *cs.pcv;
395
0
  PelUnitBuf           rec = cs.getRecoBuf();
396
397
0
  const int height = lineArea.lumaSize().height;
398
0
  const int y      = lineArea.lumaPos().y;
399
400
0
  const int cpyY       = y == 0 ? 0 : y + 2;
401
0
        int cpyHeight  = height;
402
0
            cpyHeight -= y == 0 ? 0 : 2;
403
0
            cpyHeight -= cpyY + cpyHeight >= pcv.lumaHeight ? 0 : 2;
404
0
            cpyHeight  = std::min<int>( cpyHeight, pcv.lumaHeight - cpyY );
405
406
0
  for( int x = 0; x < pcv.lumaWidth; x += pcv.maxCUWidth )
407
0
  {
408
0
    const int ctuRsAddr = getCtuAddr( Position( x, y ), *cs.pcv );
409
410
0
    SAOBlkParam* mergeList[NUM_SAO_MERGE_TYPES] = { nullptr, nullptr };
411
0
    getMergeList( cs, ctuRsAddr, mergeList );
412
413
0
    reconstructBlkSAOParam( cs.m_ctuData[ctuRsAddr].saoParam, mergeList );
414
0
  }
415
416
0
  const int numComp = getNumberValidComponents( pcv.chrFormat );
417
418
0
  for( int i = 0; i < numComp; i++ )
419
0
  {
420
0
    const ComponentID compID = ComponentID( i );
421
422
0
    const int currCpyY =   cpyY            >> getComponentScaleY( compID, pcv.chrFormat );
423
0
    const int currCpyH =   cpyHeight       >> getComponentScaleY( compID, pcv.chrFormat );
424
0
    const int currCtuW =   pcv.maxCUWidth  >> getComponentScaleX( compID, pcv.chrFormat );
425
0
    const int currPicW =   pcv.lumaWidth   >> getComponentScaleX( compID, pcv.chrFormat );
426
    
427
0
    const ptrdiff_t src_stride =       rec.bufs[ compID ].stride;
428
0
    const ptrdiff_t dst_stride = m_tempBuf.bufs[ compID ].stride;
429
430
0
    const Pel* src =       rec.bufs[ compID ].bufAt( 0, currCpyY );
431
0
          Pel* dst = m_tempBuf.bufs[ compID ].bufAt( 0, currCpyY );
432
433
0
    for( int j = currCpyY; j < ( currCpyY + currCpyH ); j++, src += src_stride, dst += dst_stride )
434
0
    {
435
0
      int numCpy   = 0;
436
0
      int cpyStart = 0;
437
438
0
      int ctuRsAddr = getCtuAddr( Position( 0, y ), *cs.pcv );
439
440
0
      for( int x = 0, currCpyX = 0; x <= pcv.widthInCtus; x++, currCpyX += currCtuW, ctuRsAddr++ )
441
0
      {
442
0
        if( x < pcv.widthInCtus && cs.m_ctuData[ctuRsAddr].saoParam[i].modeIdc != SAO_MODE_OFF )
443
0
        {
444
0
          if( !numCpy ) cpyStart = currCpyX - !!x;
445
0
          numCpy++;
446
0
        }
447
0
        else if( numCpy )
448
0
        {
449
          // copy
450
0
          int cpyLen = numCpy * currCtuW + !!cpyStart + 1;
451
0
          if( cpyStart + cpyLen > currPicW ) cpyLen = currPicW - cpyStart;
452
0
          memcpy( dst + cpyStart, src + cpyStart, cpyLen * sizeof( Pel ) );
453
0
          numCpy = 0;
454
0
        }
455
0
      }
456
0
    }
457
0
  }
458
459
0
  const int cpyY2      = cpyY + cpyHeight;
460
0
  const int cpyHeight2 = 4;
461
  
462
0
  if( cpyY2 < pcv.lumaHeight )
463
0
  {
464
0
    const UnitArea cpyLine2( pcv.chrFormat, Area( 0, cpyY2, pcv.lumaWidth, cpyHeight2 ) );
465
  
466
0
    m_tempBuf.subBuf( cpyLine2 ).copyFrom( rec.subBuf( cpyLine2 ) );
467
0
  }
468
0
}
469
470
void SampleAdaptiveOffset::SAOProcessCTULine( CodingStructure &cs, const UnitArea &lineArea )
471
0
{
472
0
  PROFILER_SCOPE_AND_STAGE( 1, g_timeProfiler, P_SAO );
473
474
0
  const PreCalcValues& pcv = *cs.pcv;
475
0
  PelUnitBuf           rec = cs.getRecoBuf();
476
477
0
  const int height         = lineArea.lumaSize().height;
478
0
  const int y              = lineArea.lumaPos().y;
479
  
480
0
  bool anySaoBlk = false;
481
  
482
0
  for( int x = 0; x < pcv.lumaWidth; x += pcv.maxCUWidth )
483
0
  {
484
0
    const int ctuRsAddr = getCtuAddr( Position( x, y ), *cs.pcv );
485
486
0
    for( int i = 0; i < MAX_NUM_COMPONENT; i++ )
487
0
    {
488
0
      if( cs.m_ctuData[ctuRsAddr].saoParam[i].modeIdc != SAO_MODE_OFF )
489
0
      {
490
0
        anySaoBlk = true;
491
0
      }
492
0
    }
493
0
  }
494
495
0
  if( !anySaoBlk ) return;
496
497
0
  std::vector<int8_t> signLineBuf1;
498
0
  std::vector<int8_t> signLineBuf2;
499
500
0
  for( int x = 0; x < pcv.lumaWidth; x += pcv.maxCUWidth )
501
0
  {
502
0
    const int width = ( x + pcv.maxCUWidth > pcv.lumaWidth ) ? ( pcv.lumaWidth - x ) : pcv.maxCUWidth;
503
504
0
    const UnitArea ctuArea( pcv.chrFormat, Area( x, y, width, height ) );
505
506
0
    const int ctuRsAddr = getCtuAddr( ctuArea.lumaPos(), *cs.pcv );
507
508
0
    offsetCTU( ctuArea, m_tempBuf, rec, cs.m_ctuData[ctuRsAddr].saoParam, cs, signLineBuf1, signLineBuf2 );
509
0
  }
510
0
}
511
512
void SampleAdaptiveOffset::SAOProcessCTU( CodingStructure &cs, const UnitArea &ctuArea )
513
0
{
514
0
  PROFILER_SCOPE_AND_STAGE( 1, g_timeProfiler, P_SAO );
515
516
0
  PelUnitBuf           rec = cs.getRecoBuf();
517
518
0
  const int ctuRsAddr = getCtuAddr( ctuArea.lumaPos(), *cs.pcv );
519
520
0
  bool anySaoBlk = false;
521
522
0
  for( int i = 0; i < MAX_NUM_COMPONENT; i++ )
523
0
  {
524
0
    if( cs.m_ctuData[ctuRsAddr].saoParam[i].modeIdc != SAO_MODE_OFF )
525
0
    {
526
0
      anySaoBlk = true;
527
0
    }
528
0
  }
529
530
0
  if( !anySaoBlk ) return;
531
532
0
  std::vector<int8_t> signLineBuf1;
533
0
  std::vector<int8_t> signLineBuf2;
534
535
0
  offsetCTU( ctuArea, m_tempBuf, rec, cs.m_ctuData[ctuRsAddr].saoParam, cs, signLineBuf1, signLineBuf2 );
536
0
}
537
538
void SampleAdaptiveOffset::invertQuantOffsets(ComponentID compIdx, int typeIdc, int typeAuxInfo, int* dstOffsets, int* srcOffsets) const
539
0
{
540
0
  int codedOffset[MAX_NUM_SAO_CLASSES];
541
542
0
  ::memcpy(codedOffset, srcOffsets, sizeof(int)*MAX_NUM_SAO_CLASSES);
543
0
  ::memset(dstOffsets, 0, sizeof(int)*MAX_NUM_SAO_CLASSES);
544
545
0
  if(typeIdc == SAO_TYPE_START_BO)
546
0
  {
547
0
    for(int i=0; i< 4; i++)
548
0
    {
549
0
      dstOffsets[(typeAuxInfo+ i)%NUM_SAO_BO_CLASSES] = codedOffset[(typeAuxInfo+ i)%NUM_SAO_BO_CLASSES]*(1<<m_offsetStepLog2);
550
0
    }
551
0
  }
552
0
  else //EO
553
0
  {
554
0
    for(int i=0; i< NUM_SAO_EO_CLASSES; i++)
555
0
    {
556
0
      dstOffsets[i] = codedOffset[i] *(1<<m_offsetStepLog2);
557
0
    }
558
0
    CHECK(dstOffsets[SAO_CLASS_EO_PLAIN] != 0, "EO offset is not '0'"); //keep EO plain offset as zero
559
0
  }
560
561
0
}
562
563
int SampleAdaptiveOffset::getMergeList(CodingStructure& cs, int ctuRsAddr, SAOBlkParam* mergeList[NUM_SAO_MERGE_TYPES])
564
0
{
565
0
  const PreCalcValues& pcv = *cs.pcv;
566
567
0
  const int ctuX       = ctuRsAddr % pcv.widthInCtus;
568
0
  const int ctuY       = ctuRsAddr / pcv.widthInCtus;
569
0
  const CodingUnit& cu = *cs.getCtuData( ctuRsAddr ).cuPtr[CH_L][0];
570
0
  int mergedCTUPos;
571
0
  int numValidMergeCandidates = 0;
572
573
0
  for( auto mergeType: { SAO_MERGE_LEFT, SAO_MERGE_ABOVE } )
574
0
  {
575
0
    SAOBlkParam* mergeCandidate = NULL;
576
577
0
    switch(mergeType)
578
0
    {
579
0
    case SAO_MERGE_ABOVE:
580
0
      if( ctuY > 0 )
581
0
      {
582
0
        mergedCTUPos = ctuRsAddr - pcv.widthInCtus;
583
0
        if( cu.above )
584
0
        {
585
0
          mergeCandidate = &( cs.m_ctuData[mergedCTUPos].saoParam );
586
0
        }
587
0
      }
588
0
      break;
589
0
    case SAO_MERGE_LEFT:
590
0
      if( ctuX > 0 )
591
0
      {
592
0
        mergedCTUPos = ctuRsAddr - 1;
593
0
        if( cu.left )
594
0
        {
595
0
          mergeCandidate = &( cs.m_ctuData[mergedCTUPos].saoParam );
596
0
        }
597
0
      }
598
0
      break;
599
0
    default:
600
0
      THROW_FATAL( "not a supported merge type" );
601
0
    }
602
603
0
    mergeList[mergeType] = mergeCandidate;
604
0
    if (mergeCandidate != NULL)
605
0
    {
606
0
      numValidMergeCandidates++;
607
0
    }
608
0
  }
609
610
0
  return numValidMergeCandidates;
611
0
}
612
613
614
void SampleAdaptiveOffset::reconstructBlkSAOParam(SAOBlkParam& recParam, SAOBlkParam* mergeList[NUM_SAO_MERGE_TYPES]) const
615
0
{
616
0
  const int numberOfComponents = m_numberOfComponents;
617
0
  for(int compIdx = 0; compIdx < numberOfComponents; compIdx++)
618
0
  {
619
0
    const ComponentID component = ComponentID(compIdx);
620
0
    SAOOffset& offsetParam = recParam[component];
621
622
0
    if(offsetParam.modeIdc == SAO_MODE_OFF)
623
0
    {
624
0
      continue;
625
0
    }
626
627
0
    switch(offsetParam.modeIdc)
628
0
    {
629
0
    case SAO_MODE_NEW:
630
0
      {
631
0
        invertQuantOffsets(component, offsetParam.typeIdc, offsetParam.typeAuxInfo, offsetParam.offset, offsetParam.offset);
632
0
      }
633
0
      break;
634
0
    case SAO_MODE_MERGE:
635
0
      {
636
0
        const SAOBlkParam* mergeTarget = mergeList[offsetParam.typeIdc];
637
0
        CHECK(mergeTarget == NULL, "Merge target does not exist");
638
639
0
        offsetParam = (*mergeTarget)[component];
640
0
      }
641
0
      break;
642
0
    default:
643
0
      {
644
0
        THROW_RECOVERABLE( "Not a supported mode" );
645
0
      }
646
0
    }
647
0
  }
648
0
}
649
650
651
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 )
652
0
{
653
0
  const uint32_t numberOfComponents = getNumberValidComponents( area.chromaFormat );
654
0
  if( std::all_of( &saoblkParam[0],
655
0
                   &saoblkParam[numberOfComponents],
656
0
                   [](const SAOOffset & p){ return p.modeIdc == SAO_MODE_OFF; } ) )
657
0
  {
658
0
    return;
659
0
  }
660
661
0
  bool isLeftAvail, isRightAvail, isAboveAvail, isBelowAvail, isAboveLeftAvail, isAboveRightAvail, isBelowLeftAvail, isBelowRightAvail;
662
663
  // block boundary availability
664
0
  deriveLoopFilterBoundaryAvailibility( cs, area.Y(), isLeftAvail, isRightAvail, isAboveAvail, isBelowAvail, isAboveLeftAvail, isAboveRightAvail, isBelowLeftAvail, isBelowRightAvail );
665
666
0
  const size_t lineBufferSize = area.Y().width + 1;
667
0
  if (signLineBuf1.size() < lineBufferSize)
668
0
  {
669
0
    signLineBuf1.resize(lineBufferSize);
670
0
    signLineBuf2.resize(lineBufferSize);
671
0
  }
672
673
0
  int  numHorVirBndry       = 0;
674
0
  int  numVerVirBndry       = 0;
675
0
  int  horVirBndryPos[]     = { -1, -1, -1 };
676
0
  int  verVirBndryPos[]     = { -1, -1, -1 };
677
0
  int  horVirBndryPosComp[] = { -1, -1, -1 };
678
0
  int  verVirBndryPosComp[] = { -1, -1, -1 };
679
0
  bool isCtuCrossedByVirtualBoundaries = isCrossedByVirtualBoundaries( cs.picHeader.get(),
680
0
                                                                       area.Y(),
681
0
                                                                       numHorVirBndry, numVerVirBndry,
682
0
                                                                       horVirBndryPos, verVirBndryPos );
683
0
  if( isCtuCrossedByVirtualBoundaries )
684
0
  {
685
0
    CHECK( numHorVirBndry >= (int)( sizeof(horVirBndryPos) / sizeof(horVirBndryPos[0]) ), "Too many virtual boundaries" );
686
0
    CHECK( numHorVirBndry >= (int)( sizeof(verVirBndryPos) / sizeof(verVirBndryPos[0]) ), "Too many virtual boundaries" );
687
0
  }
688
689
0
  for(int compIdx = 0; compIdx < numberOfComponents; compIdx++)
690
0
  {
691
0
    const ComponentID compID = ComponentID(compIdx);
692
0
    const CompArea& compArea = area.block(compID);
693
0
    SAOOffset& ctbOffset     = saoblkParam[compIdx];
694
695
0
    if(ctbOffset.modeIdc != SAO_MODE_OFF)
696
0
    {
697
0
      ptrdiff_t  srcStride = src.get(compID).stride;
698
0
      const Pel* srcBlk    = src.get(compID).bufAt(compArea);
699
0
      ptrdiff_t  resStride = res.get(compID).stride;
700
0
      Pel* resBlk          = res.get(compID).bufAt(compArea);
701
702
0
      for (int i = 0; i < numHorVirBndry; i++)
703
0
      {
704
0
        horVirBndryPosComp[i] = (horVirBndryPos[i] >> getComponentScaleY(compID, area.chromaFormat)) - compArea.y;
705
0
      }
706
0
      for (int i = 0; i < numVerVirBndry; i++)
707
0
      {
708
0
        verVirBndryPosComp[i] = (verVirBndryPos[i] >> getComponentScaleX(compID, area.chromaFormat)) - compArea.x;
709
0
      }
710
711
0
      offsetBlock( cs.sps->getBitDepth(),
712
0
                   cs.getCtuData(cs.ctuRsAddr(area.Y().pos(), CH_L)).cuPtr[0][0]->slice->clpRng(compID),
713
0
                   ctbOffset.typeIdc, ctbOffset.offset,ctbOffset.typeAuxInfo
714
0
                  , srcBlk, resBlk, srcStride, resStride, compArea.width, compArea.height
715
0
                  , isLeftAvail, isRightAvail
716
0
                  , isAboveAvail, isBelowAvail
717
0
                  , isAboveLeftAvail, isAboveRightAvail
718
0
                  , isBelowLeftAvail, isBelowRightAvail
719
0
                  , &signLineBuf1
720
0
                  , &signLineBuf2
721
0
                  , isCtuCrossedByVirtualBoundaries
722
0
                  , horVirBndryPosComp
723
0
                  , verVirBndryPosComp
724
0
                  , numHorVirBndry
725
0
                  , numVerVirBndry
726
0
                  );
727
0
    }
728
0
  } //compIdx
729
0
}
730
731
void SampleAdaptiveOffset::deriveLoopFilterBoundaryAvailibility( CodingStructure& cs,
732
                                                                 const Position&  pos,
733
                                                                 bool&            isLeftAvail,
734
                                                                 bool&            isRightAvail,
735
                                                                 bool&            isAboveAvail,
736
                                                                 bool&            isBelowAvail,
737
                                                                 bool&            isAboveLeftAvail,
738
                                                                 bool&            isAboveRightAvail,
739
                                                                 bool&            isBelowLeftAvail,
740
                                                                 bool&            isBelowRightAvail ) const
741
0
{
742
0
  const int ctusz  = cs.pcv->maxCUWidth;
743
0
  const int ctuX   = pos.x / ctusz;
744
0
  const int ctuY   = pos.y / ctusz;
745
0
  const int width  = cs.pcv->widthInCtus;
746
0
  const int height = cs.pcv->heightInCtus;
747
748
0
  const CodingUnit* cuCurr        =                       cs.getCtuData( ctuX,     ctuY     ).cuPtr[0][0];
749
0
  const CodingUnit* cuLeft        = ctuX     > 0        ? cs.getCtuData( ctuX - 1, ctuY     ).cuPtr[0][0] : nullptr;
750
0
  const CodingUnit* cuRight       = ctuX + 1 < width    ? cs.getCtuData( ctuX + 1, ctuY     ).cuPtr[0][0] : nullptr;
751
0
  const CodingUnit* cuAbove       = ctuY     > 0        ? cs.getCtuData( ctuX,     ctuY - 1 ).cuPtr[0][0] : nullptr;
752
0
  const CodingUnit* cuBelow       = ctuY + 1 < height   ? cs.getCtuData( ctuX,     ctuY + 1 ).cuPtr[0][0] : nullptr;
753
0
  const CodingUnit* cuAboveLeft   = cuLeft  && cuAbove  ? cs.getCtuData( ctuX - 1, ctuY - 1 ).cuPtr[0][0] : nullptr;
754
0
  const CodingUnit* cuAboveRight  = cuRight && cuAbove  ? cs.getCtuData( ctuX + 1, ctuY - 1 ).cuPtr[0][0] : nullptr;
755
0
  const CodingUnit* cuBelowLeft   = cuLeft  && cuBelow  ? cs.getCtuData( ctuX - 1, ctuY + 1 ).cuPtr[0][0] : nullptr;
756
0
  const CodingUnit* cuBelowRight  = cuRight && cuBelow  ? cs.getCtuData( ctuX + 1, ctuY + 1 ).cuPtr[0][0] : nullptr;
757
758
0
  isLeftAvail       = (cuLeft       != NULL);
759
0
  isAboveAvail      = (cuAbove      != NULL);
760
0
  isRightAvail      = (cuRight      != NULL);
761
0
  isBelowAvail      = (cuBelow      != NULL);
762
0
  isAboveLeftAvail  = (cuAboveLeft  != NULL);
763
0
  isAboveRightAvail = (cuAboveRight != NULL);
764
0
  isBelowLeftAvail  = (cuBelowLeft  != NULL);
765
0
  isBelowRightAvail = (cuBelowRight != NULL);
766
767
  // check cross slice flags
768
0
  const bool isLoopFilterAcrossSlicePPS = cs.pps->getLoopFilterAcrossSlicesEnabledFlag();
769
0
  if (!isLoopFilterAcrossSlicePPS)
770
0
  {
771
0
    isLeftAvail       = isLeftAvail       && CU::isSameSlice(*cuCurr, *cuLeft);
772
0
    isAboveAvail      = isAboveAvail      && CU::isSameSlice(*cuCurr, *cuAbove);
773
0
    isRightAvail      = isRightAvail      && CU::isSameSlice(*cuCurr, *cuRight);
774
0
    isBelowAvail      = isBelowAvail      && CU::isSameSlice(*cuCurr, *cuBelow);
775
0
    isAboveLeftAvail  = isAboveLeftAvail  && CU::isSameSlice(*cuCurr, *cuAboveLeft);
776
0
    isAboveRightAvail = isAboveRightAvail && CU::isSameSlice(*cuCurr, *cuAboveRight);
777
0
    isBelowLeftAvail  = isBelowLeftAvail  && CU::isSameSlice(*cuCurr, *cuBelowLeft);
778
0
    isBelowRightAvail = isBelowRightAvail && CU::isSameSlice(*cuCurr, *cuBelowRight);
779
0
  }
780
781
  // check cross tile flags
782
0
  const bool isLoopFilterAcrossTilePPS = cs.pps->getLoopFilterAcrossTilesEnabledFlag();
783
0
  if (!isLoopFilterAcrossTilePPS)
784
0
  {
785
0
    isLeftAvail       = isLeftAvail       && CU::isSameTile(*cuCurr, *cuLeft);
786
0
    isAboveAvail      = isAboveAvail      && CU::isSameTile(*cuCurr, *cuAbove);
787
0
    isRightAvail      = isRightAvail      && CU::isSameTile(*cuCurr, *cuRight);
788
0
    isBelowAvail      = isBelowAvail      && CU::isSameTile(*cuCurr, *cuBelow);
789
0
    isAboveLeftAvail  = isAboveLeftAvail  && CU::isSameTile(*cuCurr, *cuAboveLeft);
790
0
    isAboveRightAvail = isAboveRightAvail && CU::isSameTile(*cuCurr, *cuAboveRight);
791
0
    isBelowLeftAvail  = isBelowLeftAvail  && CU::isSameTile(*cuCurr, *cuBelowLeft);
792
0
    isBelowRightAvail = isBelowRightAvail && CU::isSameTile(*cuCurr, *cuBelowRight);
793
0
  }
794
795
  // check cross subpic flags
796
0
  if( cs.sps->getSubPicInfoPresentFlag() )
797
0
  {
798
0
    const SubPic& curSubPic = cs.pps->getSubPicFromCU(*cuCurr);
799
0
    if( !curSubPic.getloopFilterAcrossSubPicEnabledFlag() )
800
0
    {
801
0
      isLeftAvail       = isLeftAvail       && CU::isSameSubPic(*cuCurr, *cuLeft);
802
0
      isAboveAvail      = isAboveAvail      && CU::isSameSubPic(*cuCurr, *cuAbove);
803
0
      isRightAvail      = isRightAvail      && CU::isSameSubPic(*cuCurr, *cuRight);
804
0
      isBelowAvail      = isBelowAvail      && CU::isSameSubPic(*cuCurr, *cuBelow);
805
0
      isAboveLeftAvail  = isAboveLeftAvail  && CU::isSameSubPic(*cuCurr, *cuAboveLeft);
806
0
      isAboveRightAvail = isAboveRightAvail && CU::isSameSubPic(*cuCurr, *cuAboveRight);
807
0
      isBelowLeftAvail  = isBelowLeftAvail  && CU::isSameSubPic(*cuCurr, *cuBelowLeft);
808
0
      isBelowRightAvail = isBelowRightAvail && CU::isSameSubPic(*cuCurr, *cuBelowRight);
809
0
    }
810
0
  }
811
0
}
812
813
bool SampleAdaptiveOffset::isProcessDisabled( int xPos, int yPos, int numVerVirBndry, int numHorVirBndry, int verVirBndryPos[], int horVirBndryPos[] )
814
0
{
815
0
  for (int i = 0; i < numVerVirBndry; i++)
816
0
  {
817
0
    if ((xPos == verVirBndryPos[i]) || (xPos == verVirBndryPos[i] - 1))
818
0
    {
819
0
      return true;
820
0
    }
821
0
  }
822
0
  for (int i = 0; i < numHorVirBndry; i++)
823
0
  {
824
0
    if ((yPos == horVirBndryPos[i]) || (yPos == horVirBndryPos[i] - 1))
825
0
    {
826
0
      return true;
827
0
    }
828
0
  }
829
830
0
  return false;
831
0
}
832
833
}