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/Reshape.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     Reshape.cpp
44
    \brief    common reshaper class
45
*/
46
#include "Reshape.h"
47
#include <stdio.h>
48
#include <string.h>
49
#include <math.h>
50
#include <UnitTools.h>
51
#include "CommonLib/TimeProfiler.h"
52
53
#if ENABLE_SIMD_OPT_BUFFER && defined( TARGET_SIMD_X86 )
54
#include <CommonDefX86.h>
55
#include <simde/x86/sse.h>
56
#endif
57
58
namespace vvdec
59
{
60
61
// ====================================================================================================================
62
// Constructor / destructor / create / destroy
63
// ====================================================================================================================
64
65
Reshape::Reshape()
66
0
{
67
0
  m_invLUT = nullptr;
68
0
  m_lumaBD=0;
69
0
  m_chromaScale = 1 << CSCALE_FP_PREC;
70
0
  m_vpduX = -1;
71
0
  m_vpduY = -1;
72
0
}
73
74
Reshape::~Reshape()
75
0
{
76
0
  destroy();
77
0
}
78
79
void  Reshape::createDec(int bitDepth)
80
0
{
81
  //sanity check
82
0
  if (m_lumaBD != bitDepth)
83
0
  {
84
0
    if (m_invLUT )
85
0
    {
86
0
      xFree( m_invLUT );
87
0
      m_invLUT = nullptr;
88
0
    }
89
0
  }
90
0
  m_lumaBD = bitDepth;
91
0
  m_reshapeLUTSize = 1 << m_lumaBD;
92
0
  m_initCW = m_reshapeLUTSize / PIC_CODE_CW_BINS;
93
0
  if( !m_invLUT )
94
0
  {
95
0
    m_invLUT = ( Pel* ) xMalloc( Pel, m_reshapeLUTSize + 1 );
96
0
    memset( m_invLUT, 0, ( m_reshapeLUTSize + 1 ) * sizeof( Pel ) );
97
0
  }
98
0
  if (m_binCW.empty())
99
0
    m_binCW.resize(PIC_CODE_CW_BINS, 0);
100
0
  if (m_inputPivot.empty())
101
0
    m_inputPivot.resize(PIC_CODE_CW_BINS + 1, 0);
102
0
  if (m_fwdScaleCoef.empty())
103
0
    m_fwdScaleCoef.resize(PIC_CODE_CW_BINS, 1 << FP_PREC);
104
0
  if (m_invScaleCoef.empty())
105
0
    m_invScaleCoef.resize(PIC_CODE_CW_BINS, 1 << FP_PREC);
106
0
  if (m_reshapePivot.empty())
107
0
    m_reshapePivot.resize(PIC_CODE_CW_BINS + 1, 0);
108
0
  if (m_chromaAdjHelpLUT.empty())
109
0
    m_chromaAdjHelpLUT.resize(PIC_CODE_CW_BINS, 1<<CSCALE_FP_PREC);
110
0
}
111
112
void  Reshape::destroy()
113
0
{
114
0
  xFree( m_invLUT );
115
0
  m_invLUT = nullptr;
116
0
}
117
118
void  Reshape::initSlice( int nalUnitLayerId, const PicHeader& picHeader, const VPS* vps )
119
0
{
120
0
  if( picHeader.getLmcsEnabledFlag() )
121
0
  {
122
0
    if( nalUnitLayerId != picHeader.getLmcsAPS()->getLayerId() )
123
0
    {
124
0
      for (int i = 0; vps && i < vps->getNumOutputLayerSets(); i++ )
125
0
      {
126
0
        bool isCurrLayerInOls = false;
127
0
        bool isRefLayerInOls = false;
128
0
        for( int j = vps->getNumLayersInOls(i) - 1; j >= 0; j-- )
129
0
        {
130
0
          if( vps->getLayerIdInOls(i, j) == nalUnitLayerId )
131
0
          {
132
0
            isCurrLayerInOls = true;
133
0
          }
134
0
          if( vps->getLayerIdInOls(i, j) == picHeader.getLmcsAPS()->getLayerId() )
135
0
          {
136
0
            isRefLayerInOls = true;
137
0
          }
138
0
        }
139
0
        CHECK( isCurrLayerInOls && !isRefLayerInOls, "When VCL NAl unit in layer A refers to APS in layer B, all OLS that contains layer A shall also contains layer B" );
140
0
      }
141
0
    }
142
143
0
    const SliceReshapeInfo& sInfo = picHeader.getLmcsAPS()->getReshaperAPSInfo();
144
0
    m_sliceReshapeInfo.sliceReshaperEnableFlag       = true;
145
0
    m_sliceReshapeInfo.sliceReshaperModelPresentFlag = true;
146
0
    m_sliceReshapeInfo.enableChromaAdj               = picHeader.getLmcsChromaResidualScaleFlag();
147
0
    m_sliceReshapeInfo.reshaperModelMaxBinIdx        = sInfo.reshaperModelMaxBinIdx;
148
0
    m_sliceReshapeInfo.reshaperModelMinBinIdx        = sInfo.reshaperModelMinBinIdx;
149
0
    m_sliceReshapeInfo.maxNbitsNeededDeltaCW         = sInfo.maxNbitsNeededDeltaCW;
150
0
    m_sliceReshapeInfo.chrResScalingOffset           = sInfo.chrResScalingOffset;
151
0
    memcpy( m_sliceReshapeInfo.reshaperModelBinCWDelta, sInfo.reshaperModelBinCWDelta, sizeof( int ) * ( PIC_CODE_CW_BINS ) );
152
0
    constructReshaper();
153
0
  }
154
0
  else
155
0
  {
156
0
    m_sliceReshapeInfo.sliceReshaperEnableFlag       = false;
157
0
    m_sliceReshapeInfo.enableChromaAdj               = false;
158
0
    m_sliceReshapeInfo.sliceReshaperModelPresentFlag = false;
159
0
  }
160
0
  m_vpduX = -1;
161
0
  m_vpduY = -1;
162
0
}
163
164
bool Reshape::getCTUFlag( const Slice& slice ) const
165
0
{
166
0
  if( (slice.getSliceType() == I_SLICE) && m_sliceReshapeInfo.sliceReshaperEnableFlag )
167
0
  {
168
0
    return false;
169
0
  }
170
0
  else
171
0
  {
172
0
    return m_sliceReshapeInfo.sliceReshaperEnableFlag;
173
0
  }
174
0
}
175
176
177
178
/** compute chroma residuce scale for TU
179
* \param average luma pred of TU
180
* \return chroma residue scale
181
*/
182
int  Reshape::calculateChromaAdj(Pel avgLuma) const
183
0
{
184
0
  int iAdj = m_chromaAdjHelpLUT[getPWLIdxInv(avgLuma)];
185
0
  return(iAdj);
186
0
}
187
188
/** compute chroma residuce scale for TU
189
* \param average luma pred of TU
190
* \return chroma residue scale
191
*/
192
int  Reshape::calculateChromaAdjVpduNei(TransformUnit &tu, const Position pos)
193
0
{
194
0
  CodingStructure &cs = *tu.cu->cs;
195
0
  int xPos = pos.x;
196
0
  int yPos = pos.y;
197
0
  int ctuSize = cs.sps->getCTUSize();
198
0
  int numNeighbor = std::min(64, ctuSize);
199
0
  int numNeighborLog = getLog2(numNeighbor);
200
0
  if (ctuSize == 128)
201
0
  {
202
0
    xPos &= ~63;
203
0
    yPos &= ~63;
204
0
  }
205
0
  else
206
0
  {
207
0
    xPos &= ~( ctuSize - 1 );
208
0
    yPos &= ~( ctuSize - 1 );
209
0
  }
210
211
0
  if( isVPDUprocessed( xPos, yPos ) )
212
0
  {
213
0
    return getChromaScale();
214
0
  }
215
0
  else
216
0
  {
217
0
    setVPDULoc(xPos, yPos);
218
0
    Position topLeft(xPos, yPos);
219
0
    CodingUnit *topLeftLuma;
220
0
    const CodingUnit *cuAbove, *cuLeft;
221
222
0
    topLeftLuma = cs.getCU( topLeft, CHANNEL_TYPE_LUMA );
223
0
    cuAbove     = cs.getCURestricted( topLeftLuma->lumaPos().offset( 0, -1 ), *topLeftLuma, CHANNEL_TYPE_LUMA, topLeftLuma->ly() == yPos ? topLeftLuma : topLeftLuma->above );
224
0
    cuLeft      = cs.getCURestricted( topLeftLuma->lumaPos().offset( -1, 0 ), *topLeftLuma, CHANNEL_TYPE_LUMA, topLeftLuma->lx() == xPos ? topLeftLuma : topLeftLuma->left  );
225
226
0
    xPos = topLeftLuma->lumaPos().x;
227
0
    yPos = topLeftLuma->lumaPos().y;
228
229
0
    CompArea lumaArea = CompArea(COMPONENT_Y, topLeftLuma->lumaPos(), topLeftLuma->lumaSize());
230
0
    PelBuf piRecoY = cs.picture->getRecoBuf(lumaArea);
231
0
    ptrdiff_t strideY = piRecoY.stride;
232
0
    int chromaScale = (1 << CSCALE_FP_PREC);
233
0
    int lumaValue = -1;
234
235
0
    Pel* recSrc0 = piRecoY.bufAt(0, 0);
236
0
    const uint32_t picH = tu.cu->cs->picture->lheight();
237
0
    const uint32_t picW = tu.cu->cs->picture->lwidth();
238
0
    const Pel   valueDC = 1 << (tu.cu->sps->getBitDepth() - 1);
239
0
    int32_t recLuma = 0;
240
0
    int pelnum = 0;
241
0
    if (cuLeft != nullptr)
242
0
    {
243
0
      for (int i = 0; i < numNeighbor; i++)
244
0
      {
245
0
        int k = (yPos + i) >= picH ? (picH - yPos - 1) : i;
246
0
        recLuma += recSrc0[-1 + k * strideY];
247
0
        pelnum++;
248
0
      }
249
0
    }
250
0
    if (cuAbove != nullptr)
251
0
    {
252
0
      for (int i = 0; i < numNeighbor; i++)
253
0
      {
254
0
        int k = (xPos + i) >= picW ? (picW - xPos - 1) : i;
255
0
        recLuma += recSrc0[-strideY + k];
256
0
        pelnum++;
257
0
      }
258
0
    }
259
0
    if (pelnum == numNeighbor)
260
0
    {
261
0
      lumaValue = (recLuma + (1 << (numNeighborLog - 1))) >> numNeighborLog;
262
0
    }
263
0
    else if (pelnum == (numNeighbor << 1))
264
0
    {
265
0
      lumaValue = (recLuma + (1 << numNeighborLog)) >> (numNeighborLog + 1);
266
0
    }
267
0
    else
268
0
    {
269
0
      CHECK(pelnum != 0, "");
270
0
      lumaValue = valueDC;
271
0
    }
272
0
    chromaScale = calculateChromaAdj(lumaValue);
273
0
    setChromaScale(chromaScale);
274
0
    return(chromaScale);
275
0
  }
276
0
}
277
/** find inx of PWL for inverse mapping
278
* \param average luma pred of TU
279
* \return idx of PWL for inverse mapping
280
*/
281
int Reshape::getPWLIdxInv(int lumaVal) const
282
0
{
283
0
  int idxS = 0;
284
0
  for (idxS = m_sliceReshapeInfo.reshaperModelMinBinIdx; (idxS <= m_sliceReshapeInfo.reshaperModelMaxBinIdx); idxS++)
285
0
  {
286
0
    if (lumaVal < m_reshapePivot[idxS + 1])     break;
287
0
  }
288
0
  return std::min(idxS, PIC_CODE_CW_BINS-1);
289
0
}
290
291
/**
292
-copy Slice reshaper info structure
293
\param   tInfo describing the target Slice reshaper info structure
294
\param   sInfo describing the source Slice reshaper info structure
295
*/
296
void Reshape::copySliceReshaperInfo(SliceReshapeInfo& tInfo, SliceReshapeInfo& sInfo)
297
0
{
298
0
  tInfo.sliceReshaperModelPresentFlag = sInfo.sliceReshaperModelPresentFlag;
299
0
  if (sInfo.sliceReshaperModelPresentFlag)
300
0
  {
301
0
    tInfo.reshaperModelMaxBinIdx = sInfo.reshaperModelMaxBinIdx;
302
0
    tInfo.reshaperModelMinBinIdx = sInfo.reshaperModelMinBinIdx;
303
0
    memcpy(tInfo.reshaperModelBinCWDelta, sInfo.reshaperModelBinCWDelta, sizeof(int)*(PIC_CODE_CW_BINS));
304
0
    tInfo.maxNbitsNeededDeltaCW = sInfo.maxNbitsNeededDeltaCW;
305
0
    tInfo.chrResScalingOffset = sInfo.chrResScalingOffset;
306
0
  }
307
0
  tInfo.sliceReshaperEnableFlag = sInfo.sliceReshaperEnableFlag;
308
0
  if (sInfo.sliceReshaperEnableFlag)
309
0
    tInfo.enableChromaAdj = sInfo.enableChromaAdj;
310
0
  else
311
0
    tInfo.enableChromaAdj = 0;
312
0
}
313
314
/** Construct reshaper from syntax
315
* \param void
316
* \return void
317
*/
318
void Reshape::constructReshaper()
319
0
{
320
0
  int pwlFwdLUTsize = PIC_CODE_CW_BINS;
321
0
  int pwlFwdBinLen = m_reshapeLUTSize / PIC_CODE_CW_BINS;
322
323
0
  for (int i = 0; i < m_sliceReshapeInfo.reshaperModelMinBinIdx; i++)
324
0
    m_binCW[i] = 0;
325
0
  for (int i = m_sliceReshapeInfo.reshaperModelMaxBinIdx + 1; i < PIC_CODE_CW_BINS; i++)
326
0
    m_binCW[i] = 0;
327
0
  for (int i = m_sliceReshapeInfo.reshaperModelMinBinIdx; i <= m_sliceReshapeInfo.reshaperModelMaxBinIdx; i++)
328
0
    m_binCW[i] = (uint16_t)(m_sliceReshapeInfo.reshaperModelBinCWDelta[i] + (int)m_initCW);
329
330
0
  for (int i = 0; i < pwlFwdLUTsize; i++)
331
0
  {
332
0
    m_reshapePivot[i + 1] = m_reshapePivot[i] + m_binCW[i];
333
0
    m_inputPivot[i + 1] = m_inputPivot[i] + m_initCW;
334
0
    m_fwdScaleCoef[i] = ((int32_t)m_binCW[i] * (1 << FP_PREC) + (1 << (getLog2(pwlFwdBinLen) - 1))) >> getLog2(pwlFwdBinLen);
335
0
    if (m_binCW[i] == 0)
336
0
    {
337
0
      m_invScaleCoef[i] = 0;
338
0
      m_chromaAdjHelpLUT[i] = 1 << CSCALE_FP_PREC;
339
0
    }
340
0
    else
341
0
    {
342
0
      CHECK( m_initCW * (1 << FP_PREC) / m_binCW[i] > (1 << 15) - 1, "Inverse scale coeff doesn't fit in a short!" );
343
0
      m_invScaleCoef[i] = (int32_t)(m_initCW * (1 << FP_PREC) / m_binCW[i]);
344
0
      m_chromaAdjHelpLUT[i] = (int32_t)(m_initCW * (1 << FP_PREC) / ( m_binCW[i] + m_sliceReshapeInfo.chrResScalingOffset ) );
345
0
    }
346
0
  }
347
0
  for (int lumaSample = 0; lumaSample < m_reshapeLUTSize; lumaSample++)
348
0
  {
349
0
    int idxYInv = getPWLIdxInv(lumaSample);
350
0
    int invSample = m_inputPivot[idxYInv] + ((m_invScaleCoef[idxYInv] * (lumaSample - m_reshapePivot[idxYInv]) + (1 << (FP_PREC - 1))) >> FP_PREC);
351
0
    m_invLUT[lumaSample] = Clip3((Pel)0, (Pel)((1 << m_lumaBD) - 1), (Pel)(invSample));
352
0
  }
353
0
}
354
355
void Reshape::rspCtuBcw( CodingStructure& cs, int col, int ln ) const
356
0
{
357
0
  if( !cs.sps->getUseReshaper() || !m_sliceReshapeInfo.sliceReshaperEnableFlag )
358
0
  {
359
0
    return;
360
0
  }
361
362
0
  const Slice* slice = cs.getCtuData( col, ln ).slice;
363
0
  if( !slice->getLmcsEnabledFlag() )
364
0
  {
365
0
    return;
366
0
  }
367
368
0
  PROFILER_SCOPE_AND_STAGE_EXT( 1, g_timeProfiler, P_RESHAPER, cs, CH_L );
369
370
0
  const PreCalcValues& pcv = *cs.pcv;
371
372
0
  int xPos = pcv.maxCUWidth * col;
373
0
  int lw = std::min( pcv.lumaWidth - xPos, pcv.maxCUWidth );
374
375
0
  int yPos = ln * pcv.maxCUHeight;
376
0
  int lh = std::min( pcv.lumaHeight - yPos, pcv.maxCUHeight );
377
378
0
  PelBuf picYuvRec = cs.getRecoBuf( COMPONENT_Y ).subBuf( Position( xPos, yPos ), Size( lw, lh ) );
379
380
0
#if 1
381
0
  if( g_pelBufOP.rspBcw )
382
0
    g_pelBufOP.rspBcw( picYuvRec.buf, picYuvRec.stride, picYuvRec.width, picYuvRec.height, m_lumaBD, m_sliceReshapeInfo.reshaperModelMinBinIdx, m_sliceReshapeInfo.reshaperModelMaxBinIdx, m_reshapePivot.data(), m_invScaleCoef.data(), m_inputPivot.data() );
383
0
  else
384
0
#endif
385
0
    g_pelBufOP.applyLut( picYuvRec.buf, picYuvRec.stride, picYuvRec.width, picYuvRec.height, m_invLUT );
386
0
}
387
388
void Reshape::rspBufFwd( PelBuf& buf ) const
389
0
{
390
0
  g_pelBufOP.rspFwd( buf.buf, buf.stride, buf.width, buf.height, m_lumaBD, m_initCW, m_reshapePivot.data(), m_fwdScaleCoef.data(), m_inputPivot.data() );
391
0
}
392
393
}