科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件VC实现卡拉OK字幕叠加

VC实现卡拉OK字幕叠加

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

本文介绍了卡拉OK字幕叠加的一般原理以及VC上使用GDI的一种简单实现。

作者:陆其明 来源:VCHelp 2007年10月21日

关键字:

  • 评论
  • 分享微博
  • 分享邮件
二. 实现原理

  字幕叠加,最基本的一种是在静态图像上进行的,一般就是直接在图像上输出标准的字符串,以合成新的图像帧;而视频上的字幕叠加,则是在连续的图像帧序列上进行的,单帧上的叠加与静态图像上的叠加类似。本文所要讲述的卡拉OK字幕叠加,就是一种在视频上进行的字幕叠加。

  在视频上进行叠加的字幕,一般可以呈现出多种动态效果,比如滚动、旋转等;卡拉OK字幕需要表达更多的内容,它至少包括:

  1.根据进度,显示不同的字幕内容(即歌词);

  2.字幕上应该表达出卡拉OK的音乐节奏;
 
  3.对字幕进行勾边或其他效果处理,以突出显示。

  以下是卡拉OK字幕效果的演示图:



图3 卡拉OK字幕效果图

  简单的字幕叠加我们就可以通过GDI函数来实现。我们知道,字符的输出可以使用TextOut函数;但是,如何输出空心字,如何填充空心字呢?我们这里要用到路径。字符路径的绘制过程参考如下:

CClientDC * pClientDC = new CClientDC(mTargetWnd);
// ......
pClientDC->BeginPath();
pClientDC->TextOut(x, y, szSubtitleLine);
pClientDC->EndPath();
// pClientDC->StrokePath();
pClientDC->StrokeAndFillPath();

  我们看到,在TextOut函数调用前后分别调用了BeginPath函数和EndPath函数,以记录字符输出的路径(实际上就是字符的轮廓);然后调用StrokePath函数将路径勾勒出来,或者调用 StrokeAndFillPath函数在勾勒路径的同时进行填充。需要注意的是,路径勾勒的颜色由DC中当前选入的画笔决定,填充的颜色由DC中当前选入的画刷决定。

  那么,我们如何在字幕上表示演唱进度呢?根据音乐的节奏,我们需要为每个字符确定开始填充的时刻,并且指定该字符完成填充需要的时间。比如上述“真的好想你”一句歌词,我们从时刻0开始填充,让“真”显示1500毫秒,“的”显示300毫秒,“好”显示1600毫秒,“想”显示500毫秒,“你”显示1000毫秒。于是,我们可以从开始播放时进行计时,并且以一定的频率刷新当前播放到的时间点;表现在卡拉OK字幕上,就是不断地更新已经唱过的字幕和尚未唱过的字幕之间的分界线。从视觉效果上,我们看到的是填充色随着音乐从左到右地行进;并且单个字符的行进速度,也因该字符上分配的总的填充时间不同而不同,从而体现出应有的节奏感。

  另外,我们从上述卡拉OK字幕效果图中不难看出,已经唱过的字幕和尚未唱过的字幕的画法是不一样的:前半部分是蓝色填充、白色勾边,后半部分是黑色勾边的空心字。而且,这两部分之间的分界线有可能位于某个字符中(不会总是刚好在相邻字符的间隙中)。那么,如何准确地画出这两部分字幕呢?我们这里可以使用GDI的区域、路径裁剪操作。首先,根据当前进度,将窗口分成左右两个矩形区域:

// xStart, yStart为字幕行第一个字符显示的(x, y)坐标
// pregress为当前进度坐标(已经唱过的宽度)
// sz为SIZE类型的变量,记录整行字幕的宽、高
CRgn region1, region2;
region1.CreateRectRgn(xStart, yStart,
xStart + pregress,
yStart + sz.cy);
region2.CreateRectRgn(xStart + pregress, yStart,
xStart + sz.cx,
yStart + sz.cy);

  在画两部分字幕的路径之前,分别调用SelectClipRgn函数选入各自的区域;等到字幕路径画完之后,再调用SelectClipPath函数跟先前选入的区域进行“与”操作,即提取两者的公共部分。整个过程参考如下:

pClientDC->SelectClipRgn(&region1, RGN_COPY);
// 1.选入用于画已经唱过字幕的画笔、画刷
// 2.画字幕路径
// ......
pClientDC->SelectClipPath(RGN_AND);

pClientDC->SelectClipRgn(&region2, RGN_COPY);
// 1.选入用于画尚未唱过字幕的画笔、画刷
// 2.画字幕路径
// ......
pClientDC->SelectClipPath(RGN_AND);
    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章