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/MatrixIntraPrediction.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     MatrixIntraPrediction.cpp
44
\brief    matrix-based intra prediction class
45
*/
46
47
#include "MatrixIntraPrediction.h"
48
#include "dtrace_next.h"
49
50
#include "UnitTools.h"
51
#include "MipData.h"
52
53
namespace vvdec
54
{
55
56
namespace Mip
57
{
58
  PredictorMIP::PredictorMIP():
59
59.8k
    m_blockSize( 0, 0 ),
60
59.8k
    m_sizeId( 0 ),
61
59.8k
    m_reducedBdrySize( 0 ),
62
59.8k
    m_reducedPredSize( 0 ),
63
59.8k
    m_upsmpFactorHor( 0 ),
64
59.8k
    m_upsmpFactorVer( 0 )
65
59.8k
  {
66
59.8k
  }
67
68
  void PredictorMIP::deriveBoundaryData(const CPelBuf& src, const Area& block, const int bitDepth)
69
2.77k
  {
70
    // Step 1: Save block size and calculate dependent values
71
2.77k
    initPredBlockParams(block);
72
  
73
    // Step 2: Get the input data (left and top reference samples)
74
    //    m_refSamplesTop.resize(block.width);
75
67.9k
    for (int x = 0; x < block.width; x++)
76
65.2k
    {
77
65.2k
      m_refSamplesTop[x] = src.at(x + 1, 0);
78
65.2k
    }
79
  
80
    //    m_refSamplesLeft.resize(block.height);
81
62.2k
    for (int y = 0; y < block.height; y++)
82
59.5k
    {
83
59.5k
      m_refSamplesLeft[y] = src.at(0, y + 1);
84
59.5k
    }
85
  
86
    // Step 3: Compute the reduced boundary via Haar-downsampling (input for the prediction)
87
    //    m_reducedBoundary          .resize( m_reducedBoundarySize.width + m_reducedBoundarySize.height );
88
    //    m_reducedBoundaryTransposed.resize( m_reducedBoundarySize.width + m_reducedBoundarySize.height );
89
  
90
    //    m_reducedBoundary          .resize( inputSize );
91
    //    m_reducedBoundaryTransposed.resize( inputSize );
92
  
93
2.77k
    Pel* const topReduced = m_reducedBoundary;
94
2.77k
    boundaryDownsampling1D( topReduced, m_refSamplesTop, block.width, m_reducedBdrySize );
95
  
96
2.77k
    Pel* const leftReduced = m_reducedBoundary + m_reducedBdrySize;
97
2.77k
    boundaryDownsampling1D( leftReduced, m_refSamplesLeft, block.height, m_reducedBdrySize );
98
  
99
2.77k
    Pel* const leftReducedTransposed = m_reducedBoundaryTransposed;
100
2.77k
    Pel* const topReducedTransposed  = m_reducedBoundaryTransposed + m_reducedBdrySize;
101
13.7k
    for( int x = 0; x < m_reducedBdrySize; x++ )
102
10.9k
    {
103
10.9k
      topReducedTransposed[x] = topReduced[x];
104
10.9k
    }
105
13.7k
    for( int y = 0; y < m_reducedBdrySize; y++ )
106
10.9k
    {
107
10.9k
      leftReducedTransposed[y] = leftReduced[y];
108
10.9k
    }
109
110
  // Step 4: Rebase the reduced boundary
111
2.77k
    const int inputSize = 2 * m_reducedBdrySize;
112
  
113
2.77k
    m_inputOffset       = m_reducedBoundary[0];
114
2.77k
    m_inputOffsetTransp = m_reducedBoundaryTransposed[0];
115
  
116
2.77k
    const bool hasFirstCol = (m_sizeId < 2);
117
2.77k
    m_reducedBoundary          [0] = hasFirstCol ? ((1 << (bitDepth - 1)) - m_inputOffset      ) : 0; // first column of matrix not needed for large blocks
118
2.77k
    m_reducedBoundaryTransposed[0] = hasFirstCol ? ((1 << (bitDepth - 1)) - m_inputOffsetTransp) : 0;
119
21.9k
    for (int i = 1; i < inputSize; i++)
120
19.2k
    {
121
19.2k
      m_reducedBoundary          [i] -= m_inputOffset;
122
19.2k
      m_reducedBoundaryTransposed[i] -= m_inputOffsetTransp;
123
19.2k
    }
124
2.77k
  }
125
126
  void PredictorMIP::getPrediction(Pel* const result, const int modeIdx, const bool transpose, const int bitDepth)
127
2.77k
  {
128
2.77k
    const bool needUpsampling = ( m_upsmpFactorHor > 1 ) || ( m_upsmpFactorVer > 1 );
129
130
2.77k
    const uint8_t* matrix = getMatrixData( modeIdx );
131
132
2.77k
    Pel bufReducedPred[MIP_MAX_REDUCED_OUTPUT_SAMPLES];
133
2.77k
    Pel* const       reducedPred     = needUpsampling ? bufReducedPred : result;
134
2.77k
    const Pel* const reducedBoundary = transpose ? m_reducedBoundaryTransposed : m_reducedBoundary;
135
2.77k
    computeReducedPred( reducedPred, reducedBoundary, matrix, transpose, bitDepth );
136
2.77k
    if( needUpsampling )
137
2.72k
    {
138
2.72k
      predictionUpsampling( result, reducedPred );
139
2.72k
    }
140
2.77k
  }
141
142
143
  void PredictorMIP::initPredBlockParams(const Size& block)
144
2.77k
  {
145
2.77k
    m_blockSize = block;
146
    // init size index
147
2.77k
    m_sizeId = getMipSizeId( m_blockSize );
148
149
    // init reduced boundary size
150
2.77k
    m_reducedBdrySize = (m_sizeId == 0) ? 2 : 4;
151
152
    // init reduced prediction size
153
2.77k
    m_reducedPredSize = ( m_sizeId < 2 ) ? 4 : 8;
154
155
156
    // init upsampling factors
157
2.77k
    m_upsmpFactorHor = m_blockSize.width  / m_reducedPredSize;
158
2.77k
    m_upsmpFactorVer = m_blockSize.height / m_reducedPredSize;
159
160
2.77k
    CHECKD( (m_upsmpFactorHor < 1) || ((m_upsmpFactorHor & (m_upsmpFactorHor - 1)) != 0), "Need power of two horizontal upsampling factor." );
161
2.77k
    CHECKD( (m_upsmpFactorVer < 1) || ((m_upsmpFactorVer & (m_upsmpFactorVer - 1)) != 0), "Need power of two vertical upsampling factor." );
162
2.77k
  }
163
164
165
166
void PredictorMIP::boundaryDownsampling1D(Pel* reducedDst, const Pel* const fullSrc, const SizeType srcLen, const SizeType dstLen)
167
5.54k
{
168
5.54k
  if (dstLen < srcLen)
169
5.34k
  {
170
    // Create reduced boundary by downsampling
171
5.34k
      const SizeType downsmpFactor = srcLen / dstLen;
172
5.34k
      const int log2DownsmpFactor = getLog2(downsmpFactor);
173
5.34k
      const int roundingOffset = (1 << (log2DownsmpFactor - 1));
174
175
5.34k
      SizeType srcIdx = 0;
176
26.5k
      for( SizeType dstIdx = 0; dstIdx < dstLen; dstIdx++ )
177
21.1k
      {
178
21.1k
        int sum = 0;
179
145k
        for( int k = 0; k < downsmpFactor; k++ )
180
123k
        {
181
123k
          sum += fullSrc[srcIdx++];
182
123k
        }
183
21.1k
        reducedDst[dstIdx] = (sum + roundingOffset) >> log2DownsmpFactor;
184
21.1k
      }
185
5.34k
  }
186
197
  else
187
197
  {
188
197
    memcpy( reducedDst, fullSrc, dstLen * sizeof( Pel ) );
189
197
  }
190
5.54k
}
191
192
193
  void PredictorMIP::predictionUpsampling1D( Pel* const dst, const Pel* const src, const Pel* const bndry,
194
                                              const SizeType srcSizeUpsmpDim, const SizeType srcSizeOrthDim,
195
                                              const SizeType srcStep, const SizeType srcStride,
196
                                              const SizeType dstStep, const SizeType dstStride,
197
                                              const SizeType bndryStep,
198
                                              const unsigned int upsmpFactor )
199
4.26k
  {
200
4.26k
    const Pel log2UpsmpFactor = getLog2( upsmpFactor );
201
4.26k
    CHECKD( upsmpFactor <= 1, "Upsampling factor must be at least 2." );
202
4.26k
    const int roundingOffset = 1 << ( log2UpsmpFactor - 1 );
203
204
4.26k
    const Pel* srcLine   = src;
205
4.26k
          Pel* dstLine   = dst;
206
4.26k
    const Pel* bndryLine = bndry + bndryStep - 1;
207
208
72.0k
    for( int k = 0; k < srcSizeOrthDim; k++ )
209
67.7k
    {
210
67.7k
      const Pel* before  = bndryLine;
211
67.7k
      const Pel* behind  = srcLine;
212
67.7k
            Pel* currDst = dstLine;
213
214
596k
      for( int j = 0; j < srcSizeUpsmpDim; j++ )
215
528k
      {
216
528k
        Pel valBehind = *behind;
217
528k
        Pel valBefore = *before;
218
528k
        Pel valDiff   = valBehind - valBefore;
219
528k
        Pel scaledVal = valBefore * ( 1 << log2UpsmpFactor ) + roundingOffset;
220
221
3.06M
        for( int i = 0; i < upsmpFactor; i++ )
222
2.53M
        {
223
2.53M
          scaledVal += valDiff;
224
2.53M
          *currDst   = scaledVal >> log2UpsmpFactor;
225
2.53M
          currDst   += dstStep;
226
2.53M
        }
227
228
528k
        before  = behind;
229
528k
        behind += srcStep;
230
528k
      }
231
232
67.7k
      srcLine   += srcStride;
233
67.7k
      dstLine   += dstStride;
234
67.7k
      bndryLine += bndryStep;
235
67.7k
    }
236
4.26k
  }
237
238
239
  void PredictorMIP::predictionUpsampling( Pel* const dst, const Pel* const src ) const
240
2.72k
  {
241
2.72k
    const Pel* verSrc     = src;
242
2.72k
    SizeType   verSrcStep = m_blockSize.width;
243
  
244
2.72k
    if( m_upsmpFactorHor > 1 )
245
2.37k
    {
246
2.37k
      Pel* const horDst = dst + (m_upsmpFactorVer - 1) * m_blockSize.width;
247
2.37k
      verSrc = horDst;
248
2.37k
      verSrcStep *= m_upsmpFactorVer;
249
  
250
2.37k
      predictionUpsampling1D( horDst, src, m_refSamplesLeft,
251
2.37k
                              m_reducedPredSize, m_reducedPredSize,
252
2.37k
                              1, m_reducedPredSize, 1, verSrcStep,
253
2.37k
                              m_upsmpFactorVer, m_upsmpFactorHor );
254
2.37k
    }
255
  
256
2.72k
    if( m_upsmpFactorVer > 1 )
257
1.89k
    {
258
1.89k
      predictionUpsampling1D( dst, verSrc, m_refSamplesTop,
259
1.89k
                              m_reducedPredSize, m_blockSize.width,
260
1.89k
                              verSrcStep, 1, m_blockSize.width, 1,
261
1.89k
                              1, m_upsmpFactorVer );
262
1.89k
    }
263
2.72k
  }
264
265
  const uint8_t* PredictorMIP::getMatrixData(const int modeIdx) const
266
2.77k
  {
267
2.77k
      switch( m_sizeId )
268
2.77k
      {
269
49
        case 0: return &mipMatrix4x4[modeIdx][0][0];
270
271
423
        case 1: return &mipMatrix8x8[modeIdx][0][0];
272
273
2.29k
        case 2: return &mipMatrix16x16[modeIdx][0][0];
274
275
0
        default: THROW_FATAL( "Invalid mipSizeId" );
276
2.77k
      }
277
2.77k
  }
278
279
  void PredictorMIP::computeReducedPred( Pel* const result, const Pel* const input, const uint8_t* matrix, const bool transpose, const int bitDepth )
280
2.77k
  {
281
2.77k
    const int inputSize = 2 * m_reducedBdrySize;
282
283
    // use local buffer for transposed result
284
2.77k
    Pel        resBufTransposed[MIP_MAX_REDUCED_OUTPUT_SAMPLES];
285
2.77k
    Pel* const resPtr = ( transpose ) ? resBufTransposed : result;
286
287
2.77k
    int sum = 0;
288
24.7k
    for( int i = 0; i < inputSize; i++ )
289
21.9k
    {
290
21.9k
      sum += input[i];
291
21.9k
    }
292
2.77k
    const int offset = ( 1 << ( MIP_SHIFT_MATRIX - 1 ) ) - MIP_OFFSET_MATRIX * sum;
293
2.77k
    CHECK( inputSize != 4 * ( inputSize >> 2 ), "Error, input size not divisible by four" );
294
295
2.77k
    const uint8_t* weight      = matrix;
296
2.77k
    const int      inputOffset = transpose ? m_inputOffsetTransp : m_inputOffset;
297
298
2.77k
    const bool redSize = ( m_sizeId == 2 );
299
2.77k
    int        posRes  = 0;
300
23.0k
    for( int y = 0; y < m_reducedPredSize; y++ )
301
20.2k
    {
302
174k
      for( int x = 0; x < m_reducedPredSize; x++ )
303
154k
      {
304
154k
        int tmp0 = redSize ? 0 : ( input[0] * weight[0] );
305
154k
        int tmp1 = input[1] * weight[1 - redSize];
306
154k
        int tmp2 = input[2] * weight[2 - redSize];
307
154k
        int tmp3 = input[3] * weight[3 - redSize];
308
308k
        for( int i = 4; i < inputSize; i += 4 )
309
153k
        {
310
153k
          tmp0 += input[i    ] * weight[i     - redSize];
311
153k
          tmp1 += input[i + 1] * weight[i + 1 - redSize];
312
153k
          tmp2 += input[i + 2] * weight[i + 2 - redSize];
313
153k
          tmp3 += input[i + 3] * weight[i + 3 - redSize];
314
153k
        }
315
154k
        resPtr[posRes++] = ClipBD<int>( ( ( tmp0 + tmp1 + tmp2 + tmp3 + offset ) >> MIP_SHIFT_MATRIX ) + inputOffset, bitDepth );
316
317
154k
        weight += inputSize - redSize;
318
154k
      }
319
20.2k
    }
320
321
2.77k
    if( transpose )
322
1.16k
    {
323
9.60k
      for( int y = 0; y < m_reducedPredSize; y++ )
324
8.43k
      {
325
72.3k
        for( int x = 0; x < m_reducedPredSize; x++ )
326
63.9k
        {
327
63.9k
          result[y * m_reducedPredSize + x] = resPtr[x * m_reducedPredSize + y];
328
63.9k
        }
329
8.43k
      }
330
1.16k
    }
331
2.77k
  }
332
}   // namespace Mip
333
334
MatrixIntraPrediction::MatrixIntraPrediction()
335
59.8k
{
336
59.8k
}
337
338
void MatrixIntraPrediction::prepareInputForPred(const CPelBuf &src, const Area& puArea, const int bitDepth, const ComponentID compId)
339
2.77k
{
340
2.77k
  m_component = compId;
341
342
2.77k
  m_predictorMip.deriveBoundaryData(src, puArea, bitDepth);
343
2.77k
}
344
345
void MatrixIntraPrediction::predBlock( const Size &puSize, const int intraMode, PelBuf& dst, const bool transpose, const int bitDepth, const ComponentID compId, Pel* const resultMip )
346
2.77k
{
347
2.77k
  CHECK( m_component != compId, "Boundary has not been prepared for this component." );
348
349
2.77k
  m_predictorMip.getPrediction( resultMip, intraMode, transpose, bitDepth );
350
351
62.2k
  for( int y = 0; y < puSize.height; y++ )
352
59.5k
  {
353
59.5k
    Pel* const resultLine = &resultMip[y * puSize.width];
354
59.5k
    Pel*       dstLine    = dst.bufAt( 0, y );
355
356
599k
    for( int x = 0; x < puSize.width; x += 4 )
357
539k
    {
358
539k
      dstLine[x + 0] = Pel( resultLine[x + 0] );
359
539k
      dstLine[x + 1] = Pel( resultLine[x + 1] );
360
539k
      dstLine[x + 2] = Pel( resultLine[x + 2] );
361
539k
      dstLine[x + 3] = Pel( resultLine[x + 3] );
362
539k
    }
363
59.5k
  }
364
2.77k
}
365
366
}