Coverage Report

Created: 2026-06-16 07:20

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( bool enableOpt )
343
5.61k
{
344
5.61k
  offsetBlock = offsetBlock_core;
345
346
5.61k
  if( enableOpt )
347
5.61k
  {
348
5.61k
#if ENABLE_SIMD_OPT_SAO && defined( TARGET_SIMD_X86 )
349
5.61k
    initSampleAdaptiveOffsetX86();
350
5.61k
#endif
351
#if ENABLE_SIMD_OPT_SAO && defined( TARGET_SIMD_ARM )
352
    initSampleAdaptiveOffsetARM();
353
#endif
354
5.61k
  }
355
5.61k
}
356
357
SampleAdaptiveOffset::~SampleAdaptiveOffset()
358
5.61k
{
359
5.61k
  destroy();
360
5.61k
}
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
713
{
364
713
  m_tempBuf = unitBuf;
365
366
  //bit-depth related
367
713
  m_offsetStepLog2 = bitShift;
368
713
  m_numberOfComponents = getNumberValidComponents(format);
369
713
}
370
371
void SampleAdaptiveOffset::destroy()
372
5.61k
{
373
5.61k
}
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
6
{
402
6
  PROFILER_SCOPE_AND_STAGE( 1, g_timeProfiler, P_SAO );
403
404
6
  const PreCalcValues& pcv = *cs.pcv;
405
6
  PelUnitBuf           rec = cs.getRecoBuf();
406
407
6
  const int height = lineArea.lumaSize().height;
408
6
  const int y      = lineArea.lumaPos().y;
409
410
6
  const int cpyY       = y == 0 ? 0 : y + 2;
411
6
        int cpyHeight  = height;
412
6
            cpyHeight -= y == 0 ? 0 : 2;
413
6
            cpyHeight -= cpyY + cpyHeight >= pcv.lumaHeight ? 0 : 2;
414
6
            cpyHeight  = std::min<int>( cpyHeight, pcv.lumaHeight - cpyY );
415
416
24
  for( int x = 0; x < pcv.lumaWidth; x += pcv.maxCUWidth )
417
18
  {
418
18
    const int ctuRsAddr = getCtuAddr( Position( x, y ), *cs.pcv );
419
420
18
    SAOBlkParam* mergeList[NUM_SAO_MERGE_TYPES] = { nullptr, nullptr };
421
18
    getMergeList( cs, ctuRsAddr, mergeList );
422
423
18
    reconstructBlkSAOParam( cs.m_ctuData[ctuRsAddr].saoParam, mergeList );
424
18
  }
425
426
6
  const int numComp = getNumberValidComponents( pcv.chrFormat );
427
428
24
  for( int i = 0; i < numComp; i++ )
429
18
  {
430
18
    const ComponentID compID = ComponentID( i );
431
432
18
    const int currCpyY =   cpyY            >> getComponentScaleY( compID, pcv.chrFormat );
433
18
    const int currCpyH =   cpyHeight       >> getComponentScaleY( compID, pcv.chrFormat );
434
18
    const int currCtuW =   pcv.maxCUWidth  >> getComponentScaleX( compID, pcv.chrFormat );
435
18
    const int currPicW =   pcv.lumaWidth   >> getComponentScaleX( compID, pcv.chrFormat );
436
    
437
18
    const ptrdiff_t src_stride =       rec.bufs[ compID ].stride;
438
18
    const ptrdiff_t dst_stride = m_tempBuf.bufs[ compID ].stride;
439
440
18
    const Pel* src =       rec.bufs[ compID ].bufAt( 0, currCpyY );
441
18
          Pel* dst = m_tempBuf.bufs[ compID ].bufAt( 0, currCpyY );
442
443
1.14k
    for( int j = currCpyY; j < ( currCpyY + currCpyH ); j++, src += src_stride, dst += dst_stride )
444
1.12k
    {
445
1.12k
      int numCpy   = 0;
446
1.12k
      int cpyStart = 0;
447
448
1.12k
      int ctuRsAddr = getCtuAddr( Position( 0, y ), *cs.pcv );
449
450
6.38k
      for( int x = 0, currCpyX = 0; x <= pcv.widthInCtus; x++, currCpyX += currCtuW, ctuRsAddr++ )
451
5.25k
      {
452
5.25k
        if( x < pcv.widthInCtus && cs.m_ctuData[ctuRsAddr].saoParam[i].modeIdc != SAO_MODE_OFF )
453
2.06k
        {
454
2.06k
          if( !numCpy ) cpyStart = currCpyX - !!x;
455
2.06k
          numCpy++;
456
2.06k
        }
457
3.19k
        else if( numCpy )
458
564
        {
459
          // copy
460
564
          int cpyLen = numCpy * currCtuW + !!cpyStart + 1;
461
564
          if( cpyStart + cpyLen > currPicW ) cpyLen = currPicW - cpyStart;
462
564
          memcpy( dst + cpyStart, src + cpyStart, cpyLen * sizeof( Pel ) );
463
564
          numCpy = 0;
464
564
        }
465
5.25k
      }
466
1.12k
    }
467
18
  }
468
469
6
  const int cpyY2      = cpyY + cpyHeight;
470
6
  const int cpyHeight2 = 4;
471
  
472
6
  if( cpyY2 < pcv.lumaHeight )
473
3
  {
474
3
    const UnitArea cpyLine2( pcv.chrFormat, Area( 0, cpyY2, pcv.lumaWidth, cpyHeight2 ) );
475
  
476
3
    m_tempBuf.subBuf( cpyLine2 ).copyFrom( rec.subBuf( cpyLine2 ) );
477
3
  }
478
6
}
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
18
{
524
18
  PROFILER_SCOPE_AND_STAGE( 1, g_timeProfiler, P_SAO );
525
526
18
  PelUnitBuf           rec = cs.getRecoBuf();
527
528
18
  const int ctuRsAddr = getCtuAddr( ctuArea.lumaPos(), *cs.pcv );
529
530
18
  bool anySaoBlk = false;
531
532
69
  for( int i = 0; i < MAX_NUM_COMPONENT; i++ )
533
51
  {
534
51
    if( cs.m_ctuData[ctuRsAddr].saoParam[i].modeIdc != SAO_MODE_OFF )
535
17
    {
536
17
      anySaoBlk = true;
537
17
    }
538
51
  }
539
540
18
  if( !anySaoBlk ) return;
541
542
18
  std::vector<int8_t> signLineBuf1;
543
18
  std::vector<int8_t> signLineBuf2;
544
545
18
  offsetCTU( ctuArea, m_tempBuf, rec, cs.m_ctuData[ctuRsAddr].saoParam, cs, signLineBuf1, signLineBuf2 );
546
18
}
547
548
void SampleAdaptiveOffset::invertQuantOffsets(ComponentID compIdx, int typeIdc, int typeAuxInfo, int* dstOffsets, int* srcOffsets) const
549
8
{
550
8
  int codedOffset[MAX_NUM_SAO_CLASSES];
551
552
8
  ::memcpy(codedOffset, srcOffsets, sizeof(int)*MAX_NUM_SAO_CLASSES);
553
8
  ::memset(dstOffsets, 0, sizeof(int)*MAX_NUM_SAO_CLASSES);
554
555
8
  if(typeIdc == SAO_TYPE_START_BO)
556
3
  {
557
15
    for(int i=0; i< 4; i++)
558
12
    {
559
12
      dstOffsets[(typeAuxInfo+ i)%NUM_SAO_BO_CLASSES] = codedOffset[(typeAuxInfo+ i)%NUM_SAO_BO_CLASSES]*(1<<m_offsetStepLog2);
560
12
    }
561
3
  }
562
5
  else //EO
563
5
  {
564
30
    for(int i=0; i< NUM_SAO_EO_CLASSES; i++)
565
25
    {
566
25
      dstOffsets[i] = codedOffset[i] *(1<<m_offsetStepLog2);
567
25
    }
568
5
    CHECK(dstOffsets[SAO_CLASS_EO_PLAIN] != 0, "EO offset is not '0'"); //keep EO plain offset as zero
569
5
  }
570
571
8
}
572
573
int SampleAdaptiveOffset::getMergeList(CodingStructure& cs, int ctuRsAddr, SAOBlkParam* mergeList[NUM_SAO_MERGE_TYPES])
574
18
{
575
18
  const PreCalcValues& pcv = *cs.pcv;
576
577
18
  const int ctuX       = ctuRsAddr % pcv.widthInCtus;
578
18
  const int ctuY       = ctuRsAddr / pcv.widthInCtus;
579
18
  const CodingUnit& cu = *cs.getCtuData( ctuRsAddr ).cuPtr[CH_L][0];
580
18
  int mergedCTUPos;
581
18
  int numValidMergeCandidates = 0;
582
583
18
  for( auto mergeType: { SAO_MERGE_LEFT, SAO_MERGE_ABOVE } )
584
36
  {
585
36
    SAOBlkParam* mergeCandidate = NULL;
586
587
36
    switch(mergeType)
588
36
    {
589
18
    case SAO_MERGE_ABOVE:
590
18
      if( ctuY > 0 )
591
12
      {
592
12
        mergedCTUPos = ctuRsAddr - pcv.widthInCtus;
593
12
        if( cu.above )
594
12
        {
595
12
          mergeCandidate = &( cs.m_ctuData[mergedCTUPos].saoParam );
596
12
        }
597
12
      }
598
18
      break;
599
18
    case SAO_MERGE_LEFT:
600
18
      if( ctuX > 0 )
601
12
      {
602
12
        mergedCTUPos = ctuRsAddr - 1;
603
12
        if( cu.left )
604
12
        {
605
12
          mergeCandidate = &( cs.m_ctuData[mergedCTUPos].saoParam );
606
12
        }
607
12
      }
608
18
      break;
609
0
    default:
610
0
      THROW_FATAL( "not a supported merge type" );
611
36
    }
612
613
36
    mergeList[mergeType] = mergeCandidate;
614
36
    if (mergeCandidate != NULL)
615
24
    {
616
24
      numValidMergeCandidates++;
617
24
    }
618
36
  }
619
620
18
  return numValidMergeCandidates;
621
18
}
622
623
624
void SampleAdaptiveOffset::reconstructBlkSAOParam(SAOBlkParam& recParam, SAOBlkParam* mergeList[NUM_SAO_MERGE_TYPES]) const
625
18
{
626
18
  const int numberOfComponents = m_numberOfComponents;
627
72
  for(int compIdx = 0; compIdx < numberOfComponents; compIdx++)
628
54
  {
629
54
    const ComponentID component = ComponentID(compIdx);
630
54
    SAOOffset& offsetParam = recParam[component];
631
632
54
    if(offsetParam.modeIdc == SAO_MODE_OFF)
633
16
    {
634
16
      continue;
635
16
    }
636
637
38
    switch(offsetParam.modeIdc)
638
38
    {
639
8
    case SAO_MODE_NEW:
640
8
      {
641
8
        invertQuantOffsets(component, offsetParam.typeIdc, offsetParam.typeAuxInfo, offsetParam.offset, offsetParam.offset);
642
8
      }
643
8
      break;
644
30
    case SAO_MODE_MERGE:
645
30
      {
646
30
        const SAOBlkParam* mergeTarget = mergeList[offsetParam.typeIdc];
647
30
        CHECK(mergeTarget == NULL, "Merge target does not exist");
648
649
30
        offsetParam = (*mergeTarget)[component];
650
30
      }
651
0
      break;
652
0
    default:
653
0
      {
654
0
        THROW_RECOVERABLE( "Not a supported mode" );
655
30
      }
656
38
    }
657
38
  }
658
18
}
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
18
{
663
18
  const uint32_t numberOfComponents = getNumberValidComponents( area.chromaFormat );
664
18
  if( std::all_of( &saoblkParam[0],
665
18
                   &saoblkParam[numberOfComponents],
666
18
                   [](const SAOOffset & p){ return p.modeIdc == SAO_MODE_OFF; } ) )
667
0
  {
668
0
    return;
669
0
  }
670
671
18
  bool isLeftAvail, isRightAvail, isAboveAvail, isBelowAvail, isAboveLeftAvail, isAboveRightAvail, isBelowLeftAvail, isBelowRightAvail;
672
673
  // block boundary availability
674
18
  deriveLoopFilterBoundaryAvailibility( cs, area.Y(), isLeftAvail, isRightAvail, isAboveAvail, isBelowAvail, isAboveLeftAvail, isAboveRightAvail, isBelowLeftAvail, isBelowRightAvail );
675
676
18
  const size_t lineBufferSize = area.Y().width + 1;
677
18
  if (signLineBuf1.size() < lineBufferSize)
678
18
  {
679
18
    signLineBuf1.resize(lineBufferSize);
680
18
    signLineBuf2.resize(lineBufferSize);
681
18
  }
682
683
18
  int  numHorVirBndry       = 0;
684
18
  int  numVerVirBndry       = 0;
685
18
  int  horVirBndryPos[]     = { -1, -1, -1 };
686
18
  int  verVirBndryPos[]     = { -1, -1, -1 };
687
18
  int  horVirBndryPosComp[] = { -1, -1, -1 };
688
18
  int  verVirBndryPosComp[] = { -1, -1, -1 };
689
18
  bool isCtuCrossedByVirtualBoundaries = isCrossedByVirtualBoundaries( cs.picHeader.get(),
690
18
                                                                       area.Y(),
691
18
                                                                       numHorVirBndry, numVerVirBndry,
692
18
                                                                       horVirBndryPos, verVirBndryPos );
693
18
  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
70
  for(int compIdx = 0; compIdx < numberOfComponents; compIdx++)
700
52
  {
701
52
    const ComponentID compID = ComponentID(compIdx);
702
52
    const CompArea& compArea = area.block(compID);
703
52
    SAOOffset& ctbOffset     = saoblkParam[compIdx];
704
705
52
    if(ctbOffset.modeIdc != SAO_MODE_OFF)
706
18
    {
707
18
      ptrdiff_t  srcStride = src.get(compID).stride;
708
18
      const Pel* srcBlk    = src.get(compID).bufAt(compArea);
709
18
      ptrdiff_t  resStride = res.get(compID).stride;
710
18
      Pel* resBlk          = res.get(compID).bufAt(compArea);
711
712
18
      for (int i = 0; i < numHorVirBndry; i++)
713
0
      {
714
0
        horVirBndryPosComp[i] = (horVirBndryPos[i] >> getComponentScaleY(compID, area.chromaFormat)) - compArea.y;
715
0
      }
716
18
      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
18
      offsetBlock( cs.sps->getBitDepth(),
722
18
                   cs.getCtuData(cs.ctuRsAddr(area.Y().pos(), CH_L)).cuPtr[0][0]->slice->clpRng(compID),
723
18
                   ctbOffset.typeIdc, ctbOffset.offset,ctbOffset.typeAuxInfo
724
18
                  , srcBlk, resBlk, srcStride, resStride, compArea.width, compArea.height
725
18
                  , isLeftAvail, isRightAvail
726
18
                  , isAboveAvail, isBelowAvail
727
18
                  , isAboveLeftAvail, isAboveRightAvail
728
18
                  , isBelowLeftAvail, isBelowRightAvail
729
18
                  , &signLineBuf1
730
18
                  , &signLineBuf2
731
18
                  , isCtuCrossedByVirtualBoundaries
732
18
                  , horVirBndryPosComp
733
18
                  , verVirBndryPosComp
734
18
                  , numHorVirBndry
735
18
                  , numVerVirBndry
736
18
                  );
737
18
    }
738
52
  } //compIdx
739
18
}
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
17
{
752
17
  const int ctusz  = cs.pcv->maxCUWidth;
753
17
  const int ctuX   = pos.x / ctusz;
754
17
  const int ctuY   = pos.y / ctusz;
755
17
  const int width  = cs.pcv->widthInCtus;
756
17
  const int height = cs.pcv->heightInCtus;
757
758
17
  const CodingUnit* cuCurr        =                       cs.getCtuData( ctuX,     ctuY     ).cuPtr[0][0];
759
17
  const CodingUnit* cuLeft        = ctuX     > 0        ? cs.getCtuData( ctuX - 1, ctuY     ).cuPtr[0][0] : nullptr;
760
17
  const CodingUnit* cuRight       = ctuX + 1 < width    ? cs.getCtuData( ctuX + 1, ctuY     ).cuPtr[0][0] : nullptr;
761
17
  const CodingUnit* cuAbove       = ctuY     > 0        ? cs.getCtuData( ctuX,     ctuY - 1 ).cuPtr[0][0] : nullptr;
762
17
  const CodingUnit* cuBelow       = ctuY + 1 < height   ? cs.getCtuData( ctuX,     ctuY + 1 ).cuPtr[0][0] : nullptr;
763
17
  const CodingUnit* cuAboveLeft   = cuLeft  && cuAbove  ? cs.getCtuData( ctuX - 1, ctuY - 1 ).cuPtr[0][0] : nullptr;
764
17
  const CodingUnit* cuAboveRight  = cuRight && cuAbove  ? cs.getCtuData( ctuX + 1, ctuY - 1 ).cuPtr[0][0] : nullptr;
765
17
  const CodingUnit* cuBelowLeft   = cuLeft  && cuBelow  ? cs.getCtuData( ctuX - 1, ctuY + 1 ).cuPtr[0][0] : nullptr;
766
17
  const CodingUnit* cuBelowRight  = cuRight && cuBelow  ? cs.getCtuData( ctuX + 1, ctuY + 1 ).cuPtr[0][0] : nullptr;
767
768
17
  isLeftAvail       = (cuLeft       != NULL);
769
17
  isAboveAvail      = (cuAbove      != NULL);
770
17
  isRightAvail      = (cuRight      != NULL);
771
17
  isBelowAvail      = (cuBelow      != NULL);
772
17
  isAboveLeftAvail  = (cuAboveLeft  != NULL);
773
17
  isAboveRightAvail = (cuAboveRight != NULL);
774
17
  isBelowLeftAvail  = (cuBelowLeft  != NULL);
775
17
  isBelowRightAvail = (cuBelowRight != NULL);
776
777
  // check cross slice flags
778
17
  const bool isLoopFilterAcrossSlicePPS = cs.pps->getLoopFilterAcrossSlicesEnabledFlag();
779
17
  if (!isLoopFilterAcrossSlicePPS)
780
18
  {
781
18
    isLeftAvail       = isLeftAvail       && CU::isSameSlice(*cuCurr, *cuLeft);
782
18
    isAboveAvail      = isAboveAvail      && CU::isSameSlice(*cuCurr, *cuAbove);
783
18
    isRightAvail      = isRightAvail      && CU::isSameSlice(*cuCurr, *cuRight);
784
18
    isBelowAvail      = isBelowAvail      && CU::isSameSlice(*cuCurr, *cuBelow);
785
18
    isAboveLeftAvail  = isAboveLeftAvail  && CU::isSameSlice(*cuCurr, *cuAboveLeft);
786
18
    isAboveRightAvail = isAboveRightAvail && CU::isSameSlice(*cuCurr, *cuAboveRight);
787
18
    isBelowLeftAvail  = isBelowLeftAvail  && CU::isSameSlice(*cuCurr, *cuBelowLeft);
788
18
    isBelowRightAvail = isBelowRightAvail && CU::isSameSlice(*cuCurr, *cuBelowRight);
789
18
  }
790
791
  // check cross tile flags
792
17
  const bool isLoopFilterAcrossTilePPS = cs.pps->getLoopFilterAcrossTilesEnabledFlag();
793
17
  if (!isLoopFilterAcrossTilePPS)
794
18
  {
795
18
    isLeftAvail       = isLeftAvail       && CU::isSameTile(*cuCurr, *cuLeft);
796
18
    isAboveAvail      = isAboveAvail      && CU::isSameTile(*cuCurr, *cuAbove);
797
18
    isRightAvail      = isRightAvail      && CU::isSameTile(*cuCurr, *cuRight);
798
18
    isBelowAvail      = isBelowAvail      && CU::isSameTile(*cuCurr, *cuBelow);
799
18
    isAboveLeftAvail  = isAboveLeftAvail  && CU::isSameTile(*cuCurr, *cuAboveLeft);
800
18
    isAboveRightAvail = isAboveRightAvail && CU::isSameTile(*cuCurr, *cuAboveRight);
801
18
    isBelowLeftAvail  = isBelowLeftAvail  && CU::isSameTile(*cuCurr, *cuBelowLeft);
802
18
    isBelowRightAvail = isBelowRightAvail && CU::isSameTile(*cuCurr, *cuBelowRight);
803
18
  }
804
805
  // check cross subpic flags
806
17
  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
17
}
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
}