簡體   English   中英

如何使用 GDI 繪制具有透明度的文本?

[英]How to draw text with transparency using GDI?

我的目標是將一些任意文本動態放入 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像素方面的情況,我從上面的代碼中調用了ii.hbmColor上的GetDIBits 應該顯示我的單詞“Hello”的結果像素陣列如下所示:

在此處輸入圖片說明

像素在該內存轉儲中被編碼為BGRA ,因此每個 DWORD 中的第 4 個字節代表透明度:0=透明,FF=不透明。 但在這種情況下, TextOut不會填充透明度,或者將其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);

除了無法設置字符間距(我可以使用SetTextCharacterExtra使用 GDI SetTextCharacterExtra )這是我得到的(稍微放大以提高可見性):

在此處輸入圖片說明

很明顯,透明度仍然是一個問題。

從舊的文章由微軟MVP邁克·d薩頓采取這里

當您創建 DC 時,它最初會選擇默認的“庫存”對象,包括庫存 1*1*1 位圖。 由於在您調用 DrawText() 時已經選擇了一個位圖到 DC 中,它仍然會嘗試渲染它,即使幾乎所有內容(除了一個像素)都將被剪裁。

您需要做的是創建一個位圖,無論是 DDB 還是 DIBSection,然后在繪制之前將其選擇到您的 DC 中。

首先雖然您需要找到位圖的大小,因為您希望它足夠大以顯示您的文本,因此您在初始 DC 上再次使用 DrawText() 調用,但包括 DT_CALCRECT 標志。 它所做的不是繪制任何東西,而是簡單地測量文本的大小並將其轉儲到您傳遞調用的 RECT 中。 從這里您可以繼續使用這些維度創建您的 DIBSection 並將其選擇到您的 DC 中。 最后執行您現有的 DrawText () 調用(您可能還想使用 SetBkMode/Color()),它將將文本呈現到您可以從中獲取數據的 DIBSection。

這似乎在這里工作得很好:

 HBITMAP CreateAlphaTextBitmap(LPCSTR inText, HFONT inFont, COLORREF inColour) { int TextLength = (int)strlen(inText); if (TextLength <= 0) return NULL; // Create DC and select font into it HDC hTextDC = CreateCompatibleDC(NULL); HFONT hOldFont = (HFONT)SelectObject(hTextDC, inFont); HBITMAP hMyDIB = NULL; // Get text area RECT TextArea = {0, 0, 0, 0}; DrawText(hTextDC, inText, TextLength, &TextArea, DT_CALCRECT); if ((TextArea.right > TextArea.left) && (TextArea.bottom > TextArea.top)) { BITMAPINFOHEADER BMIH; memset(&BMIH, 0x0, sizeof(BITMAPINFOHEADER)); void *pvBits = NULL; // Specify DIB setup BMIH.biSize = sizeof(BMIH); BMIH.biWidth = TextArea.right - TextArea.left; BMIH.biHeight = TextArea.bottom - TextArea.top; BMIH.biPlanes = 1; BMIH.biBitCount = 32; BMIH.biCompression = BI_RGB; // Create and select DIB into DC hMyDIB = CreateDIBSection(hTextDC, (LPBITMAPINFO)&BMIH, 0, (LPVOID*)&pvBits, NULL, 0); HBITMAP hOldBMP = (HBITMAP)SelectObject(hTextDC, hMyDIB); if (hOldBMP != NULL) { // Set up DC properties SetTextColor(hTextDC, 0x00FFFFFF); SetBkColor(hTextDC, 0x00000000); SetBkMode(hTextDC, OPAQUE); // Draw text to buffer DrawText(hTextDC, inText, TextLength, &TextArea, DT_NOCLIP); BYTE* DataPtr = (BYTE*)pvBits; BYTE FillR = GetRValue(inColour); BYTE FillG = GetGValue(inColour); BYTE FillB = GetBValue(inColour); BYTE ThisA; for (int LoopY = 0; LoopY < BMIH.biHeight; LoopY++) { for (int LoopX = 0; LoopX < BMIH.biWidth; LoopX++) { ThisA = *DataPtr; // Move alpha and pre-multiply with RGB *DataPtr++ = (FillB * ThisA) >> 8; *DataPtr++ = (FillG * ThisA) >> 8; *DataPtr++ = (FillR * ThisA) >> 8; *DataPtr++ = ThisA; // Set Alpha } } // De-select bitmap SelectObject(hTextDC, hOldBMP); } } // De-select font and destroy temp DC SelectObject(hTextDC, hOldFont); DeleteDC(hTextDC); // Return DIBSection return hMyDIB; }

如果您需要一個如何調用它的示例,請嘗試這樣的操作(inDC 是要渲染到的 DC):

 void TestAlphaText(HDC inDC, int inX, int inY) { const char *DemoText = "Hello World!\\0"; RECT TextArea = {0, 0, 0, 0}; HFONT TempFont = CreateFont(50, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "Arial\\0"); HBITMAP MyBMP = CreateAlphaTextBitmap(DemoText, TempFont, 0xFF); DeleteObject(TempFont); if (MyBMP) { // Create temporary DC and select new Bitmap into it HDC hTempDC = CreateCompatibleDC(inDC); HBITMAP hOldBMP = (HBITMAP)SelectObject(hTempDC, MyBMP); if (hOldBMP) { BITMAP BMInf; // Get Bitmap image size GetObject(MyBMP, sizeof(BITMAP), &BMInf); // Fill blend function and blend new text to window BLENDFUNCTION bf; bf.BlendOp = AC_SRC_OVER; bf.BlendFlags = 0; bf.SourceConstantAlpha = 0x80; bf.AlphaFormat = AC_SRC_ALPHA; AlphaBlend(inDC, inX, inY, BMInf.bmWidth, BMInf.bmHeight, hTempDC, 0, 0, BMInf.bmWidth, BMInf.bmHeight, bf); // Clean up SelectObject(hTempDC, hOldBMP); DeleteObject(MyBMP); DeleteDC(hTempDC); } } }

答案和代碼的所有功勞都歸於該論壇上的原始海報,我只是重新發布了它,以便在鏈接失效時此答案仍然有效。

這個回復是在問題發布將近 3 年后才出現的,但人們仍然會在很長一段時間內咨詢這些事情。 所以我會解釋發生了什么。

DrawText(和其他 GDI 文本函數)將在透明位圖上工作。 即使以這種方式顯示,文本也不會變黑。 文本繪制到的所有像素上的 alpha 通道設置為 0,覆蓋您之前設置的任何 alpha。 如果您在 SetTextColor 中設置了 alpha 值,則文本將呈現為全黑。 如果您感到雄心勃勃,您可以逐個像素地運行並定位任何不是您的填充顏色(需要單一填充顏色)的內容,但問題隨后成為 ClearType 被覆蓋的性質之一,並且所有 alpha 都設置為您設置的任何內容他們到。 文本最終看起來非常時髦。 如果您對背景填充使用常量 alpha,則可以在繪制文本並重置所有 alpha 值后簡單地對整個位圖的位進行全面運行。 由於您必須讀取一個字節來確定它是否是背景,因此您不妨將每個像素的 alpha 設置為該圖像的標准 alpha 值並繞過慢速比較。 這相當有效,我發現它是非常可接受的。 在這個時代,MS 早就應該解決這個問題,但事實並非如此。

https://docs.microsoft.com/en-us/windows/win32/gdiplus/-gdiplus-antialiasing-with-text-use

    Gdiplus::Bitmap bmp( your_Width, your_Height, PixelFormat64bppARGB);
//PixelFormat64bppARGB ARGB needed

FontFamily      fontFamily(L"Arial");
    Font            font(&fontFamily, 29, FontStyleRegular, UnitPoint);
    Gdiplus::RectF  rectF(00.0f, 10.0f, your_Width, your_Height);
    StringFormat    stringFormat;
    SolidBrush      solidBrush(Color(63, 0, 0, 255));
    stringFormat.SetAlignment(StringAlignmentCenter);
//solidBrush Color(63, 0, 0, 255) ARGB neede

graphics.SetTextRenderingHint(TextRenderingHintAntiAlias);
    graphics.DrawString("your_text", -1, &font, rectF, &stringFormat, &solidBrush);
//TextRenderingHintAntiAlias this needed 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM