我正在尝试编写一个文本叠加功能,在视口的右上角生成一个带有文本的半透明背景。我写了一个测试MFC应用程序项目,主要是默认设置(我不记得确切,但AFAIK,没有任何设置应该导致我看到的问题)。
以下是代码:
void DrawSemitransparentRect(CDC& destDC, CRect rect, float percentGrayBackground, COLORREF overlayColour, float overlayPercentOpaque)
{
rect.NormalizeRect();
CDC temp_dc; // Temp dc for semitransparent text background
temp_dc.CreateCompatibleDC(&destDC);
CBitmap layer; // Layer for semitransparent text background
layer.CreateCompatibleBitmap(&destDC, 1, 1);
CBitmap* pOldBitmap = temp_dc.SelectObject(&layer);
BLENDFUNCTION blendFunction = { AC_SRC_OVER, 0, 0, 0 };
auto DrawSemitransparentRectHelper = [&](COLORREF colour, float transparency)
{
temp_dc.SetPixel(0, 0, colour);
blendFunction.SourceConstantAlpha = BYTE(transparency * 255 / 100);
// Draw semitransparent background
VERIFY(destDC.AlphaBlend(rect.left, rect.top, rect.Width(), rect.Height()
, &temp_dc, 0, 0, 1, 1, blendFunction));
};
// Lighten up the area to make more opaque without changing overlay colour.
DrawSemitransparentRectHelper(RGB(255, 255, 255), percentGrayBackground);
// Draw overlay colour
DrawSemitransparentRectHelper(overlayColour, overlayPercentOpaque);
temp_dc.SelectObject(pOldBitmap);
}
void DrawOverlayText(CDC & dc, CFont &windowFont, CRect const& windowRectDP, CString const& overlayText, CRect* pBoundingRectDP)
{
static bool debug = true;
int savedDC = dc.SaveDC();
::SetMapMode(dc.GetSafeHdc(), MM_TWIPS);
// Reset the window and viewport origins to (0, 0).
CPoint windowOrg, viewportOrg;
::SetWindowOrgEx(dc.GetSafeHdc(), 0, 0, &windowOrg);
::SetViewportOrgEx(dc.GetSafeHdc(), 0, 0, &viewportOrg);
LOGFONT logFont;// = { 12 * 10, 0, 0, 0, 100, 0, 0, 0, 0, 0, 0, 0, 255, _T("Times New Roman") };
windowFont.GetLogFont(&logFont);
logFont.lfHeight = 12 * 10; // 12 point font? Why isn't this *20? TWIPS are 20ths of a point.
// Font for the overlay text
CFont font;
font.CreatePointFontIndirect(&logFont, &dc);
CFont* pOldFont = dc.SelectObject(&font);
// window rect in Logical Points
CRect windowRect(windowRectDP);
dc.DPtoLP(windowRect);
// Get text extent in Logical Points
CRect textRect;
dc.DrawText(overlayText, textRect, DT_CALCRECT);
// inflation rectangle to add pixels around text
CRect inflate(8, 0, 8, 4);
dc.DPtoLP(&inflate);
// Create the bounding rect on the right hand of the view, making it a few pixels wider.
CRect boundingRect(textRect);
if (!debug)
{
boundingRect.InflateRect(inflate);
}
boundingRect.NormalizeRect();
boundingRect += CPoint(windowRect.Width() - boundingRect.Width(), 0);
CRect boundingRectDP(boundingRect);
if (pBoundingRectDP || !debug)
{
// Get the bounding rect in device coordinates
dc.LPtoDP(boundingRectDP);
*pBoundingRectDP = boundingRectDP;
}
if (!debug)
{
// round the bottom corners of the text box by clipping it
CRgn clip;
boundingRectDP.NormalizeRect();
clip.CreateRoundRectRgn(
boundingRectDP.left + 1 // +1 needed to make rounding coner match more closely to bottom right coner
, boundingRectDP.top - boundingRectDP.Height() // Getting rid of top rounded corners
, boundingRectDP.right
, boundingRectDP.bottom + 1
, 16, 16 // rounding corner may have to be more dynamic for different DPI screens
);
::SelectClipRgn(dc.GetSafeHdc(), (HRGN)clip.GetSafeHandle());
clip.DeleteObject();
}
// Calculatte centre position of text
CPoint centrePos(
boundingRect.left + (boundingRect.Width() - textRect.Width()) / 2 + 1
, boundingRect.top + (boundingRect.Height() - textRect.Height()) / 2 + 1);
if (debug)
{
// in debug mode, output text and then put semitransparent bounding rect over it.
dc.SetBkMode(debug ? OPAQUE : TRANSPARENT);
dc.SetBkColor(RGB(255, 0, 0));
dc.SetTextColor(RGB(0, 0, 0));
dc.TextOut(centrePos.x, centrePos.y, overlayText);
DrawSemitransparentRect(dc, boundingRect, 60, RGB(0, .25 * 255, .75 * 255), 40);
}
else
{
// 2 pixel offset in Logical Points
CPoint textShadowOffset(2, 2);
dc.DPtoLP(&textShadowOffset);
// in !debug mode, output semitransparent bounding rect and then put text over it.
DrawSemitransparentRect(dc, boundingRect, 60, RGB(0, .25 * 255, .75 * 255), 40);
dc.SetBkMode(debug ? OPAQUE : TRANSPARENT);
dc.SetTextColor(RGB(0, 0, 0));
dc.TextOut(centrePos.x, centrePos.y, overlayText);
dc.SetTextColor(RGB(255, 255, 255));
dc.TextOut(centrePos.x - textShadowOffset.x, centrePos.y - textShadowOffset.y, overlayText);
}
// Restore DC's state
dc.SelectObject(pOldFont);
dc.RestoreDC(savedDC);
}
// OnPaint() function for CView derived class.
void COverlayOnCViewView::OnPaint()
{
CPaintDC dc(this); // device context for painting
CString m_overlayText = _T("abcdefg ABCDEFG");
CFont windowFont;
LOGFONT logFont = { -12, 0, 0, 0, 400, 0, 0, 0, DEFAULT_CHARSET, 0, 0, CLEARTYPE_QUALITY, 0, _T("Segoe UI") };
windowFont.CreatePointFontIndirect(&logFont, &dc);
CRect windowRect;
GetClientRect(windowRect);
DrawOverlayText(dc, windowFont, windowRect, m_overlayText, nullptr);
}
现在,这在默认项目中非常有效,我得到以下内容:
但是当我把它放到另一个预先存在的项目中时,我得到了这个:
您可以看到文本实际位于半透明矩形上方。
如果我将矩形向下移动到文本框的高度,则通过更改
boundingRect += CPoint(windowRect.Width() - boundingRect.Width(), 0);
到
boundingRect += CPoint(windowRect.Width() - boundingRect.Width(), textRect.Height());
我明白了:
就像文本功能指定左下角而不是左上角进行放置。
我编写了自由函数,以便它可以与任何DC一起工作,即使DC已经操纵了它的坐标系,但也许我忘了重置某些东西?
默认项目使用的是MFC 14.0.24212.0,但我尝试将此代码导入的项目使用的是MFC 12.0.21005.1。这可能是个问题吗?我不确定如何更改默认项目以使用早期版本的MFC来测试它。
请注意,在默认项目中,我可以将代码放入OnDraw()
函数中,如下所示:
void COverlayOnCViewView::OnDraw(CDC* pDC)
{
COverlayOnCViewDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (!pDoc)
return;
// TODO: add draw code for native data here
CString m_overlayText = _T("abcdefg ABCDEFG");
CFont windowFont;
LOGFONT logFont = { -12, 0, 0, 0, 400, 0, 0, 0, DEFAULT_CHARSET, 0, 0, CLEARTYPE_QUALITY, 0, _T("Segoe UI") };
windowFont.CreatePointFontIndirect(&logFont, pDC);
CRect windowRect;
GetClientRect(windowRect);
DrawOverlayText(*pDC, windowFont, windowRect, m_overlayText, nullptr);
}
我之所以不这样做的唯一原因是因为我将这个应用程序放入其中并没有一个,我想尽可能地模仿该项目。如果您创建一个默认应用程序来测试它,请记住将ON_WM_PAINT()
宏放在MESSAGE MAP
中或使用显示的OnDraw()
函数。它们似乎在默认项目中都有相同的结果。