四. 性能优化
我们在演示中发现,频繁地直接在窗口DC中画图会带来一定的闪烁感。对此,我们可以进行一下优化,即首先创建一个与目标窗口DC兼容的内存DC,在这个内存DC中画好字幕后,再将字幕位图从内存DC拷贝到目标窗口DC中去。
我们可以参考CSubtitleController类的DrawSubtitle2函数的实现:
BOOL CSubtitleController::DrawSubtitle2(DWORD inStreamTime) { ASSERT(mClientDC);
RECT bounds; mTargetWnd->GetClientRect(&bounds); int wndWidth = bounds.right - bounds.left; int wndHeight = bounds.bottom - bounds.top;
CDC memDC; // 创建与目标窗口DC兼容的内存DC memDC.CreateCompatibleDC(mClientDC); // 创建与目标窗口DC兼容的位图 HBITMAP membmp = CreateCompatibleBitmap(mClientDC->GetSafeHdc(),wndWidth,wndHeight); // 将位图选入内存DC HBITMAP oldbmp = (HBITMAP) memDC.SelectObject(membmp); FillRect(memDC.GetSafeHdc(), &bounds, (HBRUSH)GetStockObject(LTGRAY_BRUSH));
/*----------------- 以下字幕操作都在内存DC中进行 ----------------*/ DWORD timeInChar = 0; LONG sungLength = 0; int currentChar = LocateChar(inStreamTime, timeInChar); if (currentChar != -1) { sungLength = mFromToArray[currentChar].size.cx * timeInChar; sungLength = sungLength / mFromToArray[currentChar].duration; for (int i = 0; i < currentChar; i++) { sungLength += mFromToArray[i].size.cx; } } else { sungLength = mTotalWidth; }
CFont * pOldFont = (CFont *) memDC.SelectObject(&mTextFont); memDC.SetBkMode(TRANSPARENT);
mSungRegion.CreateRectRgn(mStartPoint.x, mStartPoint.y, mStartPoint.x + sungLength, mStartPoint.y + mFromToArray[0].size.cy); mSingingRegion.CreateRectRgn(mStartPoint.x + sungLength, mStartPoint.y, mStartPoint.x + mTotalWidth, mStartPoint.y + mFromToArray[0].size.cy);
// Draw the first part which has been sung int ret = memDC.SelectClipRgn(&mSungRegion, RGN_COPY); memDC.SetPolyFillMode(WINDING); HPEN pOldPen = (HPEN) memDC.SelectObject(mSungBoundaryPen); HBRUSH pOldBrush = (HBRUSH) memDC.SelectObject(mSungTextBrush); memDC.BeginPath(); memDC.TextOut(mStartPoint.x, mStartPoint.y, mSubtitleLine); memDC.EndPath(); memDC.StrokeAndFillPath(); memDC.SelectClipPath(RGN_AND); memDC.SelectObject(pOldPen); memDC.SelectObject(pOldBrush);
// Draw the second part which is waiting for being sung pOldPen = (HPEN) memDC.SelectObject(mSingingBoundaryPen); pOldBrush = (HBRUSH) memDC.SelectObject(mSingingTextBrush); memDC.SelectClipRgn(&mSingingRegion, RGN_COPY); memDC.BeginPath(); memDC.TextOut(mStartPoint.x, mStartPoint.y, mSubtitleLine); memDC.EndPath(); memDC.StrokePath(); memDC.SelectClipPath(RGN_AND); memDC.SelectObject(pOldBrush); memDC.SelectObject(pOldPen); mSungRegion.DeleteObject(); mSingingRegion.DeleteObject();
memDC.SelectObject(pOldFont); // 将内存DC中的位图拷贝到目标窗口DC中 mClientDC->BitBlt(0, 0, wndWidth, wndHeight, &memDC, 0, 0, SRCCOPY); // 删除内存DC及使用的资源 memDC.SelectObject(oldbmp); DeleteObject(membmp); memDC.DeleteDC();
return (currentChar != -1); }
五. 结束语
本文介绍了卡拉OK字幕叠加的一般原理以及VC上使用GDI的一种简单实现,并且提供了完整的示例源代码,希望能够对读者朋友们有所启示。
下载本文源代码
|