【发布时间】:2017-07-02 10:59:10
【问题描述】:
我的目标是动态地将一些任意文本放入 HICON 图像中(在运行时)。我正在使用以下代码:
//Error checks are omitted for brevity
//First create font
LOGFONT lf = {0};
lf.lfHeight = -58;
lf.lfWeight = FW_NORMAL;
lf.lfOutPrecision = OUT_TT_PRECIS; //Use TrueType fonts for anti-alliasing
lf.lfQuality = CLEARTYPE_QUALITY;
lstrcpy(lf.lfFaceName, L"Segoe UI");
HFONT hFont = ::CreateFontIndirect(&lf);
//HICON hIcon = original icon to use as a source
//I'm using a large 256x256 pixel icon
hIcon = (HICON)::LoadImage(theApp.m_hInstance, MAKEINTRESOURCE(IDI_ICON_GREEN_DIAMOND), IMAGE_ICON, 256, 256, LR_DEFAULTCOLOR);
ICONINFO ii = {0};
::GetIconInfo(hIcon, &ii);
BITMAP bm = {0};
::GetObject(ii.hbmColor, sizeof(bm), &bm);
SIZE szBmp = {bm.bmWidth, bm.bmHeight};
HDC hDc = ::GetDC(hWnd);
HDC hMemDC = ::CreateCompatibleDC(hDc);
HGDIOBJ hOldBmp = ::SelectObject(hMemDC, ii.hbmColor);
HGDIOBJ hOldFont = ::SelectObject(hMemDC, hFont);
::SetBkMode(hMemDC, TRANSPARENT);
::SetTextColor(hMemDC, RGB(255, 0, 0)); //Red text
//Draw text
//NOTE that DrawText API behaves in a similar way
::TextOut(hMemDC, 0, 0, L"Hello", 5);
::SelectObject(hMemDC, hOldFont);
::SelectObject(hMemDC, hOldBmp);
//We need a simple mask bitmap for the icon
HBITMAP hBmpMsk = ::CreateBitmap(szBmp.cx, szBmp.cy, 1, 1, NULL);
ICONINFO ii2 = {0};
ii2.fIcon = TRUE;
ii2.hbmColor = ii.hbmColor;
ii2.hbmMask = hBmpMsk;
//Create updated icon
HICON hIcon2 = ::CreateIconIndirect(&ii2);
//Cleanup
::DeleteObject(hBmpMsk);
::DeleteDC(hMemDC);
::ReleaseDC(hWnd, hDc);
::DeleteObject(ii.hbmColor);
::DeleteObject(ii.hbmMask);
::DeleteObject(hFont);
然后我可以在我的窗口中显示来自OnPaint() 处理程序的图标(这样我就可以看到结果如何):
::DrawIconEx(dc.GetSafeHdc(), 0, 0,
hIcon2,
256, 256, NULL,
::GetSysColorBrush(COLOR_BTNFACE),
DI_NORMAL);
这就是我得到的:
要查看我的hIcon2 中像素方面的情况,我从上面的代码中调用了GetDIBits 的ii.hbmColor。应该显示我的单词“Hello”的结果像素数组如下所示:
像素在该内存转储中被编码为BGRA,因此每个 DWORD 中的第 4 个字节代表透明度:0=透明,FF=不透明。但是在这种情况下TextOut 没有填写透明度,或者将其保留为 0,这被解释为“完全透明”。相反,它似乎将其预乘到 RGB 颜色本身。
请注意,如果我继续向下看同一个位图,即绿色菱形开始的位置,图像像素似乎具有正确设置的透明度字节:
知道如何绘制文本以便 API 可以设置这些透明度字节吗?
编辑:如下所示,我尝试了以下 GDI+ 方法:
HGDIOBJ hOldBmp = ::SelectObject(hMemDC, ii.hbmColor);
Graphics grpx(hMemDC);
RectF rcfTxt(0.0f, 0.0f, (REAL)szBmp.cx, (REAL)szBmp.cy);
Font gdiFont(L"Segoe UI", 58.0f, FontStyleRegular, UnitPixel);
SolidBrush gdiBrush(Color(255, 0, 0));
StringFormat gdiSF;
gdiSF.SetAlignment(StringAlignmentNear);
gdiSF.SetFormatFlags(StringFormatFlagsNoWrap);
gdiSF.SetHotkeyPrefix(HotkeyPrefixNone);
//The reason I was using GDI was because I was setting
//spacing between letters using SetTextCharacterExtra()
//Unfortunately with GDI+ this does not work!
HDC hTmpDC = grpx.GetHDC();
::SetTextCharacterExtra(hTmpDC, -4); //This doesn't do anything!
grpx.ReleaseHDC(hTmpDC);
grpx.DrawString(L"Hello", 5, &gdiFont, rcfTxt, &gdiSF, &gdiBrush);
::SelectObject(hMemDC, hOldBmp);
除了无法设置字符间距(我可以使用 GDI 使用 SetTextCharacterExtra)之外,这是我得到的(为了可见性而略微放大):
很明显,透明度仍然是个问题。
【问题讨论】:
-
GDI 不理解 alpha;请考虑使用 gdiplus。
-
@JonathanPotter:谢谢。是的,看起来 GDI+ 是支持 Alpha 通道的唯一方法。虽然,我选择普通 GDI 的原因是因为它提供了
SetTextCharacterExtra函数来更改字符间距。你知道我是否还能在 GDI+ 中使用它吗?
标签: c++ windows winapi text gdi