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