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/WeightPrediction.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     WeightPrediction.h
44
    \brief    weighting prediction class (header)
45
*/
46
47
// Include files
48
#include "CommonDef.h"
49
#include "Unit.h"
50
#include "InterpolationFilter.h"
51
#include "WeightPrediction.h"
52
#include "CodingStructure.h"
53
54
namespace vvdec
55
{
56
57
// ====================================================================================================================
58
// Class definition
59
// ====================================================================================================================
60
61
WeightPrediction::WeightPrediction()
62
0
{
63
0
}
64
65
66
67
void  WeightPrediction::getWpScaling(const Slice                *pcSlice,
68
                                     const int                  &iRefIdx0,
69
                                     const int                  &iRefIdx1,
70
                                           WPScalingParam       *wp0,
71
                                           WPScalingParam       *wp1)
72
0
{
73
0
  CHECK(iRefIdx0 < 0 && iRefIdx1 < 0, "Both picture reference list indizes smaller than '0'");
74
75
0
  const bool wpBiPred        = pcSlice->getPPS()->getWPBiPred();
76
0
  const bool bBiPred         = (iRefIdx0 >= 0 && iRefIdx1 >= 0);
77
0
  const bool bUniPred        = !bBiPred;
78
79
0
  const WPScalingParam* wp0org;
80
0
  const WPScalingParam* wp1org;
81
82
0
  if (bUniPred || wpBiPred)
83
0
  {
84
    // explicit --------------------
85
0
    if (iRefIdx0 >= 0)
86
0
    {
87
0
      pcSlice->getWpScaling(REF_PIC_LIST_0, iRefIdx0, wp0org);
88
0
    }
89
0
    if (iRefIdx1 >= 0)
90
0
    {
91
0
      pcSlice->getWpScaling(REF_PIC_LIST_1, iRefIdx1, wp1org);
92
0
    }
93
0
  }
94
0
  else
95
0
  {
96
0
    THROW_RECOVERABLE( "Unsupported WP configuration" );
97
0
  }
98
99
0
  const uint32_t numValidComponent = getNumberValidComponents(pcSlice->getSPS()->getChromaFormatIdc());
100
101
0
  if( iRefIdx0 < 0 )
102
0
  {
103
0
    for( int yuv = 0; yuv < numValidComponent; yuv++ )
104
0
    {
105
0
      wp0[yuv].bPresentFlag = false;
106
0
    }
107
0
  }
108
0
  if( iRefIdx1 < 0 )
109
0
  {
110
0
    for( int yuv = 0; yuv < numValidComponent; yuv++ )
111
0
    {
112
0
      wp1[yuv].bPresentFlag = false;
113
0
    }
114
0
  }
115
116
0
  const bool bUseHighPrecisionPredictionWeighting = false; // pcSlice->getSPS()->getSpsRangeExtension().getHighPrecisionOffsetsEnabledFlag();
117
118
0
  if (bBiPred)
119
0
  {
120
    // Bi-predictive case
121
0
    for (int yuv = 0; yuv < numValidComponent; yuv++)
122
0
    {
123
0
      const int bitDepth = pcSlice->getSPS()->getBitDepth();
124
0
      const int offsetScalingFactor = bUseHighPrecisionPredictionWeighting ? 1 : (1 << (bitDepth - 8));
125
126
0
      wp0[yuv] = wp0org[yuv];
127
0
      wp1[yuv] = wp1org[yuv];
128
129
0
      wp0[yuv].w = wp0org[yuv].iWeight;
130
0
      wp1[yuv].w = wp1org[yuv].iWeight;
131
0
      wp0[yuv].o = wp0org[yuv].iOffset * offsetScalingFactor;
132
0
      wp1[yuv].o = wp1org[yuv].iOffset * offsetScalingFactor;
133
0
      wp0[yuv].offset = wp1[yuv].offset = wp0[yuv].o + wp1[yuv].o;
134
0
      wp0[yuv].shift  = wp1[yuv].shift  = wp0org[yuv].uiLog2WeightDenom + 1;
135
0
      wp0[yuv].round  = wp1[yuv].round  = ( 1 << wp0org[yuv].uiLog2WeightDenom );
136
0
    }
137
0
  }
138
0
  else
139
0
  {
140
    // UniPred
141
0
    const WPScalingParam* const pwporg = ( iRefIdx0 >= 0 ) ? wp0org : wp1org;
142
0
          WPScalingParam* const pwp    = ( iRefIdx0 >= 0 ) ? wp0 : wp1;
143
144
0
    for (int yuv = 0; yuv < numValidComponent; yuv++)
145
0
    {
146
0
      const int bitDepth            = pcSlice->getSPS()->getBitDepth();
147
0
      const int offsetScalingFactor = bUseHighPrecisionPredictionWeighting ? 1 : (1 << (bitDepth - 8));
148
      
149
0
      pwp[yuv]        = pwporg[yuv];
150
151
0
      pwp[yuv].w      = pwporg[yuv].iWeight;
152
0
      pwp[yuv].offset = pwporg[yuv].iOffset * offsetScalingFactor;
153
0
      pwp[yuv].shift  = pwporg[yuv].uiLog2WeightDenom;
154
0
      pwp[yuv].round  = ( pwporg[yuv].uiLog2WeightDenom >= 1 ) ? ( 1 << ( pwporg[yuv].uiLog2WeightDenom - 1 ) ) : ( 0 );
155
0
    }
156
0
  }
157
0
}
158
159
static inline Pel weightBidir( int w0, Pel P0, int w1, Pel P1, int round, int shift, int offset, const ClpRng& clpRng )
160
0
{
161
0
  return ClipPel( ( ( w0*( P0 + IF_INTERNAL_OFFS ) + w1 * ( P1 + IF_INTERNAL_OFFS ) + round + ( offset * ( 1 << ( shift - 1 ) ) ) ) >> shift ), clpRng );
162
0
}
163
164
void WeightPrediction::addWeightBi(const PelUnitBuf           &pcYuvSrc0,
165
                                   const PelUnitBuf           &pcYuvSrc1,
166
                                   const ClpRngs              &clpRngs,
167
                                   const WPScalingParam *const wp0,
168
                                   const WPScalingParam *const wp1,
169
                                         PelUnitBuf           &rpcYuvDst
170
                                  )
171
0
{
172
0
  const uint32_t numValidComponent = (const uint32_t)pcYuvSrc0.bufs.size();
173
174
0
  for (int componentIndex = 0; componentIndex < numValidComponent; componentIndex++)
175
0
  {
176
0
    const ComponentID compID = ComponentID(componentIndex);
177
178
0
    const Pel* pSrc0 = pcYuvSrc0.bufs[compID].buf;
179
0
    const Pel* pSrc1 = pcYuvSrc1.bufs[compID].buf;
180
0
          Pel* pDst  = rpcYuvDst.bufs[compID].buf;
181
182
0
    const ptrdiff_t iSrc0Stride = pcYuvSrc0.bufs[compID].stride;
183
0
    const ptrdiff_t iSrc1Stride = pcYuvSrc1.bufs[compID].stride;
184
0
    const ptrdiff_t iDstStride  = rpcYuvDst.bufs[compID].stride;
185
186
0
    const ClpRng& clpRng = clpRngs;
187
0
    const int  w0       = wp0[compID].w;
188
0
    const int  offset   = wp0[compID].offset;
189
0
    const int  clipBD   = clpRng.bd;
190
0
    const int  shiftNum = std::max<int>(2, (IF_INTERNAL_PREC - clipBD));
191
0
    const int  shift    = wp0[compID].shift + shiftNum;
192
0
    const int  round    = 1 << shift >> 1;
193
0
    const int  w1       = wp1[compID].w;
194
195
0
    const int  iHeight  = rpcYuvDst.bufs[compID].height;
196
0
    const int  iWidth   = rpcYuvDst.bufs[compID].width;
197
0
    const int  applyOffset = round + ( offset * ( 1 << ( shift - 1 ) ) ) + ( w0 + w1 ) * IF_INTERNAL_OFFS;
198
199
0
    if( ( iWidth & 7 ) == 0 )
200
0
    {
201
0
      g_pelBufOP.wghtAvg8( pSrc0, iSrc0Stride, pSrc1, iSrc1Stride, pDst, iDstStride, iWidth, iHeight, shift, applyOffset, w0, w1, clpRngs );
202
0
    }
203
0
    else if( ( iWidth & 3 ) == 0 )
204
0
      g_pelBufOP.wghtAvg4( pSrc0, iSrc0Stride, pSrc1, iSrc1Stride, pDst, iDstStride, iWidth, iHeight, shift, applyOffset, w0, w1, clpRngs );
205
0
    else
206
0
    {
207
0
      CHECK( iWidth != 2, "Should only happen for width '2'" );
208
209
0
      for (int y = iHeight - 1; y >= 0; y--)
210
0
      {
211
0
        pDst[0] = weightBidir(w0, pSrc0[0], w1, pSrc1[0], round, shift, offset, clpRng );;
212
0
        pDst[1] = weightBidir(w0, pSrc0[1], w1, pSrc1[1], round, shift, offset, clpRng );;
213
214
0
        pSrc0 += iSrc0Stride;
215
0
        pSrc1 += iSrc1Stride;
216
0
        pDst += iDstStride;
217
0
      } // y loop
218
0
    }
219
0
  } // compID loop
220
0
}
221
222
223
static inline Pel weightUnidir( int w0, Pel P0, int round, int shift, int offset, const ClpRng& clpRng )
224
0
{
225
0
  return ClipPel( ( ( w0*( P0 + IF_INTERNAL_OFFS ) + round ) >> shift ) + offset, clpRng );
226
0
}
227
228
static inline Pel noWeightUnidir( Pel P0, int round, int shift, int offset, const ClpRng& clpRng )
229
0
{
230
0
  return ClipPel( ( ( ( P0 + IF_INTERNAL_OFFS ) + round ) >> shift ) + offset, clpRng );
231
0
}
232
233
static inline Pel noWeightOffsetUnidir( Pel P0, int round, int shift, const ClpRng& clpRng )
234
0
{
235
0
  return ClipPel( ( ( ( P0 + IF_INTERNAL_OFFS ) + round ) >> shift ), clpRng );
236
0
}
237
238
void  WeightPrediction::addWeightUni(const PelUnitBuf           &pcYuvSrc0,
239
                                     const ClpRngs              &clpRngs,
240
                                     const WPScalingParam *const wp0,
241
                                           PelUnitBuf           &rpcYuvDst
242
                                    )
243
0
{
244
0
  const uint32_t numValidComponent = (const uint32_t)pcYuvSrc0.bufs.size();
245
246
0
  for (int componentIndex = 0; componentIndex < numValidComponent; componentIndex++)
247
0
  {
248
0
    const ComponentID compID = ComponentID(componentIndex);
249
250
0
    const Pel* pSrc0 = pcYuvSrc0.bufs[compID].buf;
251
0
          Pel* pDst  = rpcYuvDst.bufs[compID].buf;
252
253
    // Luma : --------------------------------------------
254
0
    const ClpRng& clpRng    = clpRngs;
255
0
    const int  w0           = wp0[compID].w;
256
0
    const int  offset       = wp0[compID].offset;
257
0
    const int  clipBD       = clpRng.bd;
258
0
    const int  shiftNum     = std::max<int>(2, (IF_INTERNAL_PREC - clipBD));
259
0
    const int  shift        = wp0[compID].shift + shiftNum;
260
0
    const ptrdiff_t iSrc0Stride  = pcYuvSrc0.bufs[compID].stride;
261
0
    const ptrdiff_t iDstStride   = rpcYuvDst.bufs[compID].stride;
262
0
    const int  iHeight      = rpcYuvDst.bufs[compID].height;
263
0
    const int  iWidth       = rpcYuvDst.bufs[compID].width;
264
265
0
    if (w0 != 1 << wp0[compID].shift)
266
0
    {
267
0
      const int  round = (shift > 0) ? (1 << (shift - 1)) : 0;
268
0
      for (int y = iHeight - 1; y >= 0; y--)
269
0
      {
270
0
        int x = iWidth - 1;
271
0
        for (; x >= 3; )
272
0
        {
273
0
          pDst[x] = weightUnidir(w0, pSrc0[x], round, shift, offset, clpRng); x--;
274
0
          pDst[x] = weightUnidir(w0, pSrc0[x], round, shift, offset, clpRng); x--;
275
0
          pDst[x] = weightUnidir(w0, pSrc0[x], round, shift, offset, clpRng); x--;
276
0
          pDst[x] = weightUnidir(w0, pSrc0[x], round, shift, offset, clpRng); x--;
277
0
        }
278
0
        for (; x >= 0; x--)
279
0
        {
280
0
          pDst[x] = weightUnidir(w0, pSrc0[x], round, shift, offset, clpRng);
281
0
        }
282
0
        pSrc0 += iSrc0Stride;
283
0
        pDst += iDstStride;
284
0
      }
285
0
    }
286
0
    else
287
0
    {
288
0
      const int  round = (shiftNum > 0) ? (1 << (shiftNum - 1)) : 0;
289
0
      if (offset == 0)
290
0
      {
291
0
        for (int y = iHeight - 1; y >= 0; y--)
292
0
        {
293
0
          int x = iWidth - 1;
294
0
          for (; x >= 3; )
295
0
          {
296
0
            pDst[x] = noWeightOffsetUnidir(pSrc0[x], round, shiftNum, clpRng); x--;
297
0
            pDst[x] = noWeightOffsetUnidir(pSrc0[x], round, shiftNum, clpRng); x--;
298
0
            pDst[x] = noWeightOffsetUnidir(pSrc0[x], round, shiftNum, clpRng); x--;
299
0
            pDst[x] = noWeightOffsetUnidir(pSrc0[x], round, shiftNum, clpRng); x--;
300
0
          }
301
0
          for (; x >= 0; x--)
302
0
          {
303
0
            pDst[x] = noWeightOffsetUnidir(pSrc0[x], round, shiftNum, clpRng);
304
0
          }
305
0
          pSrc0 += iSrc0Stride;
306
0
          pDst += iDstStride;
307
0
        }
308
0
      }
309
0
      else
310
0
      {
311
0
        for (int y = iHeight - 1; y >= 0; y--)
312
0
        {
313
0
          int x = iWidth - 1;
314
0
          for (; x >= 3; )
315
0
          {
316
0
            pDst[x] = noWeightUnidir(pSrc0[x], round, shiftNum, offset, clpRng); x--;
317
0
            pDst[x] = noWeightUnidir(pSrc0[x], round, shiftNum, offset, clpRng); x--;
318
0
            pDst[x] = noWeightUnidir(pSrc0[x], round, shiftNum, offset, clpRng); x--;
319
0
            pDst[x] = noWeightUnidir(pSrc0[x], round, shiftNum, offset, clpRng); x--;
320
0
          }
321
0
          for (; x >= 0; x--)
322
0
          {
323
0
            pDst[x] = noWeightUnidir(pSrc0[x], round, shiftNum, offset, clpRng);
324
0
          }
325
0
          pSrc0 += iSrc0Stride;
326
0
          pDst += iDstStride;
327
0
        }
328
0
      }
329
0
    }
330
0
  }
331
0
}
332
333
void  WeightPrediction::xWeightedPredictionUni(const CodingUnit       &cu,
334
                                               const PelUnitBuf           &pcYuvSrc,
335
                                               const RefPicList           &eRefPicList,
336
                                                     PelUnitBuf           &pcYuvPred,
337
                                               const int                   iRefIdx_input/* = -1*/
338
                                              )
339
0
{
340
0
  WPScalingParam  pwp[MAX_NUM_COMPONENT], pwpTmp[MAX_NUM_COMPONENT];
341
342
0
  int iRefIdx = iRefIdx_input;
343
0
  if (iRefIdx < 0)
344
0
  {
345
0
    iRefIdx = cu.refIdx[eRefPicList];
346
0
  }
347
348
0
  CHECK(iRefIdx < 0, "Negative reference picture list index");
349
350
0
  if (eRefPicList == REF_PIC_LIST_0)
351
0
  {
352
0
    getWpScaling(cu.slice, iRefIdx, -1, pwp, pwpTmp);
353
0
  }
354
0
  else
355
0
  {
356
0
    getWpScaling(cu.slice, -1, iRefIdx, pwpTmp, pwp);
357
0
  }
358
0
  addWeightUni(pcYuvSrc, cu.slice->clpRngs(), pwp, pcYuvPred);
359
0
}
360
361
void  WeightPrediction::xWeightedPredictionBi(const CodingUnit       &cu,
362
                                              const PelUnitBuf           &pcYuvSrc0,
363
                                              const PelUnitBuf           &pcYuvSrc1,
364
                                                    PelUnitBuf           &rpcYuvDst
365
                                             )
366
0
{
367
0
  const int iRefIdx0 = cu.refIdx[0];
368
0
  const int iRefIdx1 = cu.refIdx[1];
369
0
  WPScalingParam  pwp0[MAX_NUM_COMPONENT];
370
0
  WPScalingParam  pwp1[MAX_NUM_COMPONENT];
371
372
0
  CHECK( !cu.pps->getWPBiPred(), "Weighted Bi-prediction disabled" );
373
374
0
  getWpScaling(cu.slice, iRefIdx0, iRefIdx1, pwp0, pwp1);
375
376
0
  if (iRefIdx0 >= 0 && iRefIdx1 >= 0)
377
0
  {
378
0
    addWeightBi(pcYuvSrc0, pcYuvSrc1, cu.slice->clpRngs(), pwp0, pwp1, rpcYuvDst);
379
0
  }
380
0
  else if (iRefIdx0 >= 0 && iRefIdx1 < 0)
381
0
  {
382
0
    addWeightUni(pcYuvSrc0, cu.slice->clpRngs(), pwp0, rpcYuvDst);
383
0
  }
384
0
  else if (iRefIdx0 < 0 && iRefIdx1 >= 0)
385
0
  {
386
0
    addWeightUni(pcYuvSrc1, cu.slice->clpRngs(), pwp1, rpcYuvDst);
387
0
  }
388
0
  else
389
0
  {
390
0
    THROW_RECOVERABLE( "Both reference picture list indices are negative" );
391
0
  }
392
0
}
393
394
}