Coverage Report

Created: 2026-05-30 06:10

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