Coverage Report

Created: 2026-04-01 07:49

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/vvenc/source/Lib/apputils/LogoRenderer.h
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) 2019-2026, Fraunhofer-Gesellschaft zur Förderung der angewandten Forschung e.V. & The VVenC 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
/** \file     LogoRenderer.h
43
    \brief    yuv logo renderer class (header)
44
*/
45
46
#pragma once
47
48
#include <iostream>
49
#include <fstream>
50
#include <sstream>
51
#include <iomanip>
52
#include <string>
53
#include <algorithm>
54
55
#include "vvenc/version.h"
56
#include "vvenc/vvenc.h"
57
58
#include "FileIOHelper.h"
59
60
#ifdef VVENC_ENABLE_THIRDPARTY_JSON
61
#include <nlohmann/json.hpp>
62
using nlohmann::json;
63
#endif
64
65
66
//! \ingroup Interface
67
//! \{
68
69
namespace apputils {
70
71
// ====================================================================================================================
72
73
struct LogoInputOptions
74
{
75
  std::string logoFilename;
76
  int         sourceWidth  = 0;
77
  int         sourceHeight = 0; 
78
  int         bitdepth     = 10;
79
  int         bgColorMin   = -1;
80
  int         bgColorMax   = -1;
81
};
82
83
struct LogoRenderOptions
84
{
85
  int  offsetHor  = 0; // horizontal offset ( >= 0 offset from left, < 0 offset from right)
86
  int  offsetVer  = 0; // vertical offset   ( >= 0 offset from top, < 0 offset from bottom)
87
  int  opacity    = 0;
88
};
89
90
struct LogoOverlay
91
{
92
  std::string        version = VVENC_VERSION;
93
  LogoInputOptions   inputOpts;
94
  LogoRenderOptions  renderOpts;
95
};
96
97
#ifdef VVENC_ENABLE_THIRDPARTY_JSON
98
inline void to_json( json& j, const LogoInputOptions& l)
99
0
{
100
0
  j = json{
101
0
    { "//LogoFilename",  "path to yuv/y4m logo file can be defined as absolute path or relative path from json file" },
102
0
    { "LogoFilename",  l.logoFilename },
103
0
    { "SourceWidth",   l.sourceWidth },
104
0
    { "SourceHeight",  l.sourceHeight },
105
0
    { "InputBitDepth", l.bitdepth },
106
0
    { "//BgColorMinMax",  "defines background color (min-max range inclusive). color range is removed when >= 0" },
107
0
    { "BgColorMin",    l.bgColorMin },
108
0
    { "BgColorMax",    l.bgColorMax }
109
0
  };
110
0
}
111
112
inline void to_json( json& j, const LogoRenderOptions& r)
113
0
{
114
0
  j = json{
115
0
    { "//OffsetHor",     "defines logo horizontal offset(x) in px. (clipped to pic borders), if >= 0 offset from left, < 0 offset from right+1" },
116
0
    { "//OffsetVer",     "defines logo   vertical offset(y) in px. (clipped to pic borders), if >= 0 offset from top, < 0 offset from bottom+1" },
117
0
    { "OffsetHor",        r.offsetHor },
118
0
    { "OffsetVer",        r.offsetVer },
119
0
    { "//Opacity",       "defines opacity level in range 0-100% (0: opaque - 100: transparent)" },
120
0
    { "Opacity",         r.opacity }
121
0
  };
122
0
}
123
124
inline void to_json( json& j, const LogoOverlay& l)
125
0
{
126
0
  j = json{
127
0
    { "version",      l.version     },
128
0
    { "input_opts",   l.inputOpts   },
129
0
    { "render_opts",  l.renderOpts  },
130
0
  };
131
0
}
132
133
inline void from_json(const json& j, LogoInputOptions& l)
134
0
{  
135
0
  j.at("LogoFilename").get_to(l.logoFilename);
136
0
  j.at("SourceWidth").get_to(l.sourceWidth);  
137
0
  j.at("SourceHeight").get_to(l.sourceHeight);
138
0
  j.at("InputBitDepth").get_to(l.bitdepth);
139
0
  j.at("BgColorMin").get_to(l.bgColorMin);
140
0
  j.at("BgColorMax").get_to(l.bgColorMax);
141
0
}
142
143
inline void from_json(const json& j, LogoRenderOptions& l )
144
0
{
145
0
  j.at("OffsetHor").get_to(l.offsetHor);
146
0
  j.at("OffsetVer").get_to(l.offsetVer);
147
0
  j.at("Opacity").get_to(l.opacity);
148
0
}
149
150
inline void from_json(const json& j, LogoOverlay& l )
151
0
{
152
0
  j.at("version").get_to(l.version);
153
0
  j.at("input_opts").get_to(l.inputOpts);
154
0
  j.at("render_opts").get_to(l.renderOpts);
155
0
}
156
#endif
157
158
class LogoRenderer
159
{
160
public:
161
  LogoRenderer()
162
0
  {
163
0
  }
164
165
  ~LogoRenderer()
166
0
  {
167
0
    if( m_bInitialized ){ uninit(); }
168
0
  }
169
  
170
  int init( const std::string &fileName, vvencChromaFormat chromaFormat, std::ostream& rcOstr )
171
0
  {
172
0
    if( m_bInitialized )
173
0
    { 
174
0
      rcOstr << "error: logo overlay already initialized." << std::endl;
175
0
      return -1;
176
0
    }
177
0
    #ifndef VVENC_ENABLE_THIRDPARTY_JSON
178
0
      rcOstr << "error: logo overlay is not supported. please compile with json enabled" << std::endl;
179
0
      return -1;
180
0
    #endif
181
0
   
182
0
    m_chromaFormat = chromaFormat;
183
0
    if( readLogoFile( fileName, rcOstr ) )
184
0
    {
185
0
      rcOstr << "error reading json logo file with logo description:" << std::endl;
186
0
      dumpOutput( rcOstr );
187
0
      return -1;
188
0
    }
189
0
    
190
0
    bool isY4mInput = false;
191
0
    if( FileIOHelper::isY4mInputFilename( m_cLogo.inputOpts.logoFilename ) )
192
0
    {
193
0
      vvenc_config c;
194
0
      if ( 0 > FileIOHelper::parseY4mHeader( m_cLogo.inputOpts.logoFilename, c, m_chromaFormat ) )
195
0
      {
196
0
        rcOstr << "cannot parse y4m information in file " << m_cLogo.inputOpts.logoFilename << std::endl;     
197
0
        return -1;
198
0
      }
199
0
      m_cLogo.inputOpts.sourceWidth  = c.m_SourceWidth;
200
0
      m_cLogo.inputOpts.sourceHeight = c.m_SourceHeight;
201
0
      m_cLogo.inputOpts.bitdepth     = c.m_inputBitDepth[0];
202
0
      isY4mInput = true;
203
0
    }   
204
0
205
0
    std::string cErr;
206
0
    if( !FileIOHelper::checkInputFile( m_cLogo.inputOpts.logoFilename, cErr ) )
207
0
    {
208
0
      rcOstr << "Logo input file error: " << cErr << std::endl;     
209
0
      return -1; 
210
0
    }
211
0
    
212
0
    if( m_cLogo.inputOpts.sourceWidth <= 0 || m_cLogo.inputOpts.sourceHeight <= 0 )
213
0
    {
214
0
      rcOstr << "Logo input file error: invalid size " << m_cLogo.inputOpts.sourceWidth  << "x" << m_cLogo.inputOpts.sourceHeight << std::endl;
215
0
      return -1; 
216
0
    }
217
0
    
218
0
    if( m_cLogo.inputOpts.bitdepth == 8 ) // input must be 10bit -> transpose min/max to 10bit
219
0
    {
220
0
      m_cLogo.inputOpts.bgColorMin = m_cLogo.inputOpts.bgColorMin << 2;
221
0
      m_cLogo.inputOpts.bgColorMax = m_cLogo.inputOpts.bgColorMax << 2;      
222
0
    }
223
0
       
224
0
    vvenc_YUVBuffer_default( &m_cYuvBufLogo );
225
0
    vvenc_YUVBuffer_alloc_buffer( &m_cYuvBufLogo, chromaFormat, m_cLogo.inputOpts.sourceWidth, m_cLogo.inputOpts.sourceHeight );
226
0
    
227
0
    if( m_cLogo.renderOpts.opacity >= 100 )
228
0
    {
229
0
      m_bBypass = true;
230
0
    }
231
0
    else
232
0
    {
233
0
      m_bBypass = false;
234
0
      if( (m_cLogo.inputOpts.bgColorMin >= 0 && m_cLogo.inputOpts.bgColorMax >= 0 ) || 
235
0
           m_cLogo.renderOpts.opacity > 0 )
236
0
      {
237
0
        m_bAlphaNeeded = true;
238
0
        vvenc_YUVBuffer_default( &m_cYuvBufAlpha );
239
0
        vvenc_YUVBuffer_alloc_buffer( &m_cYuvBufAlpha, VVENC_CHROMA_400, m_cLogo.inputOpts.sourceWidth, m_cLogo.inputOpts.sourceHeight );
240
0
      }
241
0
    }
242
0
243
0
    std::fstream cLogoHandle;
244
0
    cLogoHandle.open( m_cLogo.inputOpts.logoFilename.c_str(), std::ios::binary | std::ios::in );
245
0
    if( cLogoHandle.fail() )
246
0
    {
247
0
      rcOstr << "Failed to open logo overlay file: " << m_cLogo.inputOpts.logoFilename << std::endl;
248
0
      return -1;
249
0
    }
250
0
    
251
0
    if ( isY4mInput )
252
0
    {
253
0
      std::string headerline;
254
0
      getline(cLogoHandle, headerline);  // jump over y4m header
255
0
      
256
0
      std::string y4mPrefix;
257
0
      getline(cLogoHandle, y4mPrefix);   /* assume basic FRAME\n headers */
258
0
      if( y4mPrefix != "FRAME")
259
0
      {
260
0
        rcOstr << "Source image does not contain valid y4m header (FRAME) - end of stream" << std::endl;
261
0
        return -1;
262
0
      }
263
0
    }
264
0
265
0
    // read the logo int yuvBuffer
266
0
    bool is16bit       = m_cLogo.inputOpts.bitdepth > 8 ? true : false;
267
0
    int  bitdepthShift = 10  - m_cLogo.inputOpts.bitdepth;
268
0
    const LPel maxVal = ( 1 << 10 ) - 1;
269
0
    
270
0
    for( int comp = 0; comp < 3; comp++ )
271
0
    {
272
0
      vvencYUVPlane yuvPlane = m_cYuvBufLogo.planes[ comp ];   
273
0
      if ( ! FileIOHelper::readYuvPlane( cLogoHandle, yuvPlane, is16bit, m_cLogo.inputOpts.bitdepth, false, comp, m_chromaFormat, m_chromaFormat ) )
274
0
      {
275
0
        rcOstr << "Failed to read plane from logo overlay file: " << m_cLogo.inputOpts.logoFilename << std::endl;
276
0
        return -1;
277
0
      }
278
0
  
279
0
      if ( m_chromaFormat == VVENC_CHROMA_400 && comp)
280
0
        continue;
281
0
  
282
0
      if ( ! FileIOHelper::verifyYuvPlane( yuvPlane, m_cLogo.inputOpts.bitdepth ) )
283
0
      {
284
0
        rcOstr << "Logo image contains values outside the specified bit range!" << std::endl;
285
0
        return -1;
286
0
      }
287
0
  
288
0
      FileIOHelper::scaleYuvPlane( yuvPlane, yuvPlane, bitdepthShift, 0, maxVal );
289
0
    }
290
0
291
0
292
0
    if( !m_bBypass && m_bAlphaNeeded )
293
0
    {
294
0
      initAlphaMask();
295
0
    }
296
0
297
0
    m_bInitialized = true; 
298
0
    
299
0
    return 0;
300
0
  }
301
302
  int uninit()
303
0
  {
304
0
    if( !m_bInitialized )
305
0
    { 
306
0
      return -1;
307
0
    }
308
0
    
309
0
    vvenc_YUVBuffer_free_buffer( &m_cYuvBufLogo );
310
0
    if ( m_bAlphaNeeded )
311
0
    {
312
0
      vvenc_YUVBuffer_free_buffer( &m_cYuvBufAlpha );
313
0
    }
314
0
    
315
0
    m_bInitialized = false;
316
0
    return 0;
317
0
  }
318
319
0
  bool isInitialized()  const { return m_bInitialized; }
320
  
321
0
  LogoInputOptions getLogoInputOptions() { return m_cLogo.inputOpts; }
322
0
  vvencYUVBuffer* getLogoYuvBuffer()     { return &m_cYuvBufLogo; }
323
   
324
  void dumpOutput( std::ostream& rcOstr )
325
0
  {
326
0
#ifdef VVENC_ENABLE_THIRDPARTY_JSON  
327
0
    const json j { m_cLogo }; 
328
0
    rcOstr << j.dump(2) << std::endl;
329
0
#else
330
0
    rcOstr << std::endl;
331
0
#endif
332
0
  }
333
  
334
  int writeLogoFile( std::string fileName, std::ostream& rcOstr )
335
0
  {
336
0
#ifdef VVENC_ENABLE_THIRDPARTY_JSON
337
0
    std::fstream    logoFHandle;   
338
0
    logoFHandle.open( fileName, std::ios::out );
339
0
    if ( logoFHandle.fail() )
340
0
    {
341
0
      rcOstr << "error: cannot open logo overlay file '" << fileName << "'." << std::endl;
342
0
      return -1;
343
0
    }
344
0
    
345
0
    const json j { m_cLogo }; 
346
0
    logoFHandle << std::setw(4) << j << std::endl;
347
0
    
348
0
    if(  logoFHandle.is_open() )
349
0
      logoFHandle.close();
350
0
    return 0;
351
0
#else
352
0
    rcOstr << "error: cannot write logo overlay file '" << fileName << "'. json not enabled" << std::endl;
353
0
    return -1;
354
0
#endif
355
0
  }
356
  
357
  int readLogoFile( std::string fileName, std::ostream& rcOstr )
358
0
  {
359
0
#ifdef VVENC_ENABLE_THIRDPARTY_JSON
360
0
    std::fstream    logoFHandle;   
361
0
    logoFHandle.open( fileName, std::ios::in );
362
0
    if ( logoFHandle.fail() )
363
0
    {
364
0
      rcOstr << "error: cannot open logo overlay file '" << fileName << "'." << std::endl;
365
0
      return -1;
366
0
    }
367
0
       
368
0
    try
369
0
    {
370
0
      json j;
371
0
      logoFHandle >> j;
372
0
      std::vector<LogoOverlay> logoInput = j.get<std::vector<LogoOverlay>>();
373
0
      if( !logoInput.empty())
374
0
        m_cLogo = logoInput.at(0);
375
0
      else
376
0
      {
377
0
        rcOstr << "error: logo json parsing error in file '" << fileName << "'." << std::endl;
378
0
        return -1;
379
0
      }
380
0
    }
381
0
    catch (std::exception& e)
382
0
    {
383
0
      rcOstr << "logo json parsing error: " << e.what() << "\n";
384
0
      return -1;
385
0
    }
386
0
    
387
0
    if( m_cLogo.inputOpts.bgColorMin >= 0 && m_cLogo.inputOpts.bgColorMax < 0 )
388
0
    {
389
0
      rcOstr << "logo must define range of BgColorMin/BgColorMax. set BgColorMin but missing BgColorMax (min/max " << 
390
0
                 m_cLogo.inputOpts.bgColorMin << "/" << m_cLogo.inputOpts.bgColorMax << ")\n";
391
0
      return -1;
392
0
    }
393
0
    if( m_cLogo.inputOpts.bgColorMax >= 0 && m_cLogo.inputOpts.bgColorMin < 0 )
394
0
    {
395
0
      rcOstr << "logo must define range of BgColorMin/BgColorMax. set BgColorMax but missing BgColorMin (min/max " << 
396
0
                 m_cLogo.inputOpts.bgColorMin << "/" << m_cLogo.inputOpts.bgColorMax << ")\n";
397
0
      return -1;
398
0
    }
399
0
    if( m_cLogo.inputOpts.bgColorMin >= 0 && m_cLogo.inputOpts.bgColorMax < m_cLogo.inputOpts.bgColorMin )
400
0
    {
401
0
      rcOstr << "logo must define range of BgColorMin/BgColorMax. BgColorMax must be >= BgColorMin (min/max " << 
402
0
                 m_cLogo.inputOpts.bgColorMin << "/" << m_cLogo.inputOpts.bgColorMax << ")\n";
403
0
      return -1;
404
0
    }
405
0
406
0
    std::ifstream logofile(m_cLogo.inputOpts.logoFilename);
407
0
    if ( ! logofile.is_open() )
408
0
    {
409
0
      // check if logo path is relative from json file -> make it absolute
410
0
      size_t pos = fileName.find_last_of("/\\");
411
0
      if ( pos != std::string::npos )
412
0
      {
413
0
        std::string folder = fileName.substr(0,pos);
414
0
        std::string absPath = folder + "/" + m_cLogo.inputOpts.logoFilename;
415
0
        std::ifstream abslogofile(absPath);
416
0
        if ( abslogofile.is_open() )
417
0
        {
418
0
          m_cLogo.inputOpts.logoFilename = absPath;
419
0
          logofile.swap( abslogofile );
420
0
        }
421
0
      }
422
0
423
0
      if ( ! logofile.is_open() )
424
0
      {
425
0
        rcOstr << "Failed to open logo overlay file: " << m_cLogo.inputOpts.logoFilename << std::endl;
426
0
        return -1;
427
0
      }
428
0
    }
429
0
        
430
0
    // limit opacity in range 0-100 %
431
0
    m_cLogo.renderOpts.opacity = std::min( m_cLogo.renderOpts.opacity, 100 );
432
0
    m_cLogo.renderOpts.opacity = std::max( m_cLogo.renderOpts.opacity, 0 );
433
0
      
434
0
    if(  logoFHandle.is_open() )
435
0
      logoFHandle.close();
436
0
437
0
    return 0;
438
0
#else
439
0
    rcOstr << "error: json not enabled - cannot read logo overlay file '" << fileName << "'." << std::endl;
440
0
    return -1;
441
0
#endif
442
0
  }
443
   
444
  int renderLogo ( const vvencYUVBuffer& yuvDestBuf )
445
0
  {   
446
0
    if( !m_bInitialized )
447
0
    {
448
0
      return -1;
449
0
    }
450
0
451
0
    if( m_bBypass ){ return 0; }
452
0
    
453
0
    if ( m_cYuvBufLogo.planes[0].width > yuvDestBuf.planes[0].width || m_cYuvBufLogo.planes[0].height > yuvDestBuf.planes[0].height )
454
0
    {    
455
0
      return -1;
456
0
    }
457
0
    
458
0
    int logoPosX = 0;
459
0
    int logoPosY = 0;
460
0
    if( m_cLogo.renderOpts.offsetHor != 0 )
461
0
    {
462
0
      const int maxX = yuvDestBuf.planes[0].width - m_cYuvBufLogo.planes[0].width;      
463
0
      logoPosX = (m_cLogo.renderOpts.offsetHor >= 0) ? 
464
0
                  std::min( maxX, m_cLogo.renderOpts.offsetHor ) :
465
0
                  std::max( 0, maxX + m_cLogo.renderOpts.offsetHor+1);
466
0
      
467
0
    } 
468
0
    
469
0
    if( m_cLogo.renderOpts.offsetVer != 0 )
470
0
    {
471
0
      const int maxY = yuvDestBuf.planes[0].height - m_cYuvBufLogo.planes[0].height; 
472
0
      logoPosY = (m_cLogo.renderOpts.offsetVer >= 0) ? 
473
0
                  std::min( maxY, m_cLogo.renderOpts.offsetVer ) :
474
0
                  std::max( 0, maxY + m_cLogo.renderOpts.offsetVer+1);
475
0
    }
476
0
       
477
0
    const int numComp = (m_chromaFormat==VVENC_CHROMA_400) ? 1 : 3;   
478
0
    for( int comp = 0; comp < numComp; comp++ )
479
0
    {
480
0
      vvencYUVPlane yuvDes = yuvDestBuf.planes[ comp ];
481
0
      vvencYUVPlane yuvLogo = m_cYuvBufLogo.planes[ comp ];
482
0
      
483
0
      const int csx = ( (comp == 0) || (m_chromaFormat==VVENC_CHROMA_444) ) ? 0 : 1;
484
0
      const int csy = ( (comp == 0) || (m_chromaFormat!=VVENC_CHROMA_420) ) ? 0 : 1;
485
0
      const int16_t* src = yuvLogo.ptr;
486
0
      int16_t* dst = yuvDes.ptr + ( (logoPosY >> csy) * yuvDes.stride ) + (logoPosX >> csx);
487
0
488
0
      if( m_bAlphaNeeded && m_cYuvBufAlpha.planes[0].ptr )
489
0
      {
490
0
        vvencYUVPlane yuvAlpha = m_cYuvBufAlpha.planes[0];
491
0
        const int16_t* alpha = yuvAlpha.ptr;
492
0
        
493
0
        for( int y = 0; y < yuvLogo.height; y++ )
494
0
        {
495
0
          int xA = 0;
496
0
          for( int x = 0; x < yuvLogo.width; x++, xA += (1 << csx) )
497
0
          {         
498
0
            if( alpha[xA] >= 100 )
499
0
            {
500
0
              dst[x] = src[x];                                        
501
0
            }
502
0
            else if( alpha[xA] > 0 )
503
0
            {
504
0
              dst[x] = (( src[x] * alpha[xA] ) + ( dst[x] * (100-alpha[xA]) )) / 100 ;   
505
0
            }
506
0
          }
507
0
          src   += yuvLogo.stride;
508
0
          dst   += yuvDes.stride;
509
0
          alpha += ( (1 << csy) * yuvAlpha.stride);
510
0
        }
511
0
      }
512
0
      else
513
0
      {
514
0
        for( int y = 0; y < yuvLogo.height; y++ )
515
0
        {
516
0
          for( int x = 0; x < yuvLogo.width; x++ )
517
0
          {         
518
0
            dst[x] = src[x];            
519
0
          }
520
0
          src += yuvLogo.stride;
521
0
          dst += yuvDes.stride;
522
0
        }
523
0
      }
524
0
    }
525
0
    
526
0
    return 0;
527
0
  }
528
529
private:
530
531
  void initAlphaMask()
532
0
  {
533
0
    if( !m_bAlphaNeeded || m_bBypass ) return;
534
0
    
535
0
    // init alpha mask
536
0
    bool bgColorSet  = (m_cLogo.inputOpts.bgColorMin >= 0 && m_cLogo.inputOpts.bgColorMax >= 0 ) ? true : false;
537
0
    bool opacitySet  = (m_cLogo.renderOpts.opacity > 0) ? true : false;
538
0
    int transp = 100 - m_cLogo.renderOpts.opacity; // transparency level
539
0
540
0
    vvencYUVPlane yuvSrc = m_cYuvBufLogo.planes[0];
541
0
    vvencYUVPlane yuvDes = m_cYuvBufAlpha.planes[0];
542
0
    
543
0
    const int16_t* src = yuvSrc.ptr;
544
0
    int16_t*       dst = yuvDes.ptr;
545
0
    
546
0
    for( int y = 0; y < yuvSrc.height; y++ )
547
0
    {
548
0
      for( int x = 0; x < yuvSrc.width; x++ )
549
0
      {
550
0
        if( bgColorSet && ( src[x] >= m_cLogo.inputOpts.bgColorMin && src[x] <= m_cLogo.inputOpts.bgColorMax ))
551
0
        {
552
0
          dst[x] = 0; // ignore background ( transparent )
553
0
        }
554
0
        else if( opacitySet )
555
0
        {
556
0
          dst[x] = transp;
557
0
        }
558
0
        else
559
0
        {
560
0
          dst[x] = 100;  // opaque
561
0
        }
562
0
      }
563
0
      src += yuvSrc.stride;
564
0
      dst += yuvDes.stride;
565
0
    }
566
0
  } 
567
568
private:
569
  bool              m_bInitialized = false;
570
  vvencChromaFormat m_chromaFormat = VVENC_NUM_CHROMA_FORMAT;
571
  LogoOverlay       m_cLogo;
572
  vvencYUVBuffer    m_cYuvBufLogo;
573
  vvencYUVBuffer    m_cYuvBufAlpha;
574
  bool              m_bAlphaNeeded = false;
575
  bool              m_bBypass      = false;
576
};
577
578
} // namespace apputils
579
580
//! \}
581