首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

VC 實現卡拉OK 字幕疊加技術詳解

     編輯:關於VC++

前言

在 Windows 平台上開發多媒體應用時,字幕疊加是常見需求。本文將深入探討如何使用 Visual C++ 結合 GDI(Graphics Device Interface)實現具有卡拉OK 效果的動態字幕疊加,包含逐字填充、節奏同步、勾邊顯示等核心功能,並提供完整的類設計與優化方案。

一、GDI 程式設計基礎回顧

GDI 是 Windows 圖形繪製的核心 API 集合,提供豐富的繪圖功能。主要函數分類包括:

  • 設備上下文(DC)管理:GetDC、CreateDC、DeleteDC
  • 繪圖基元:線條(LineTo、Polyline)、填充(Ellipse、FillRect)
  • 屬性設置:背景色(SetBkColor)、透明模式(SetBkMode)、文字色(SetTextColor)
  • 文字輸出:TextOut、GetTextExtentPoint32
  • 位圖操作:BitBlt、StretchBlt、SetPixel
  • 路徑與裁剪:BeginPath/EndPath、StrokeAndFillPath、SelectClipRgn、SelectClipPath

MFC 將這些 API 封裝為 CDC、CFont、CPen、CBrush、CBitmap、CRgn 等類,簡化了物件導向開發。繪圖流程通常為:取得 DC → 設定映射模式 → 執行繪圖 → 釋放 DC。

二、卡拉OK 字幕疊加核心原理

1. 空心字與填充字的繪製:路徑技術

利用 GDI 路徑功能可輕鬆實現字符輪廓提取與填充:

CClientDC* pDC = new CClientDC(mTargetWnd);
pDC->BeginPath();
pDC->TextOut(x, y, szSubtitleLine);
pDC->EndPath();
pDC->StrokeAndFillPath(); // 同時勾邊與填充

勾邊顏色由當前畫筆(CPen)決定,填充顏色由當前畫刷(CBrush)決定。

2. 逐字填充同步機制

根據歌詞時間軸,為每個字分配開始填充時間點與持續時間。例如「真的好想你」五字,分別分配 1500ms、300ms、1600ms、500ms、1000ms。播放時以固定頻率(如 25fps,40ms 間隔)計算當前「流時間」,換算出已演唱字符寬度 sungLength,作為左右區域分界線。

3. 雙區域裁剪渲染

將字幕行分為「已唱」(藍底白邊實心)與「未唱」(黑邊空心)兩區域:

CRgn sungRgn, singingRgn;
sungRgn.CreateRectRgn(xStart, yStart, xStart + sungLength, yStart + height);
singingRgn.CreateRectRgn(xStart + sungLength, yStart, xStart + totalWidth, yStart + height);

繪製流程:

  1. 選入「已唱」區域 → 設定藍畫刷、白畫筆 → 繪製全行文字路徑 → StrokeAndFillPath → SelectClipPath(RGN_AND) 取交集
  2. 選入「未唱」區域 → 設定空心畫刷、黑畫筆 → 繪製全行文字路徑 → StrokePath → SelectClipPath(RGN_AND)

此法可精確處理分界線落在單字內部的情況。

三、關鍵類實現:CSubtitleController

核心資料結構

  • mSubtitleArray:逐字存儲的 CString 陣列
  • mDurationArray:對應字符填充耗時(毫秒)
  • mFromToArray:記錄每字起始/結束時間、寬高等屬性
  • mClientDC:目標視窗 DC 指標
  • mSungRegion/mSingingRegion:雙裁剪區域

核心方法 DrawSubtitle(DWORD streamTime)

  1. 呼叫 LocateChar 依流時間定位當前字符索引 currentChar 及字內相對時間 timeInChar
  2. 計算 sungLength:前幾字總寬 + 當前字按比例填充寬度
  3. 建立左右矩形區域
  4. 選入字體、設定透明背景模式
  5. 依序繪製「已唱」與「未唱」兩部分,使用 SelectClipRgn + SelectClipPath(RGN_AND) 組合裁剪
  6. 還原 DC 狀態(畫筆、畫刷、字體、裁剪區域)

輔助方法 LocateChar

遍歷 mFromToArray,找出 from ≤ streamTime < to 的字符,回傳索引與字內偏移時間。

四、效能優化:雙緩衝繪圖消除閃爍

直接在視窗 DC 繪圖會造成明顯閃爍。解決方案:建立相容記憶體 DC(CDC memDC),配合相容位圖(CreateCompatibleBitmap),在記憶體 DC 完成所有繪圖後,一次 BitBlt 複製到視窗 DC。

CDC memDC;
memDC.CreateCompatibleDC(mClientDC);
HBITMAP membmp = CreateCompatibleBitmap(mClientDC->GetSafeHdc(), w, h);
HBITMAP oldbmp = (HBITMAP)memDC.SelectObject(membmp);
// ... 在 memDC 上執行完整 DrawSubtitle 邏輯 ...
mClientDC->BitBlt(0, 0, w, h, &memDC, 0, 0, SRCCOPY);
// 清理資源
memDC.SelectObject(oldbmp);
DeleteObject(membmp);
memDC.DeleteDC();

此法顯著提升幀率穩定性,適合即時卡拉OK 播放場景。

五、總結與擴展建議

本文示範了基於 GDI 路徑、區域裁剪、雙緩衝的卡拉OK 字幕疊加完整實現。實際專案可進一步擴展:

  • 支援多行字幕滾動、換行佈局
  • 整合 DirectShow / Media Foundation 實現音視頻同步
  • 加入字體特效(陰影、發光、描邊漸變)
  • 支援 ASS/SSA 等進階字幕格式解析

完整原始碼可作為多媒體應用開發的參考基礎。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved