這是我的朋友,是一個將是漫長的...GDI +字體渲染,尤其是在分層窗口
我得到了一些非常奇怪的行爲,當我試圖呈現分層窗口的文本。
奇怪的是,對於font/font-style/font-size的某些組合,GDI +更改了渲染方法。對於尺寸爲之間的Tahoma-Bold字體,在 8.49和16.49(像素單位)之間,包括「失敗」。對於其他字體和樣式,我會以不同的尺寸「失敗」。
爲清楚起見我提供了一個完整的可執行例子進一步下跌。一起玩的兩個關鍵參數是在23行:
Color g_oTextColor(255, 240, 0, 0); // Simply change Color to (254, 240, 0, 0) [to add slight transparency] and everything will work!
#define USE_LAYERED_WINDOW // or just comment this line out [to use a regular window], and everything will work!
當使用分層的窗口和完全不透明的字體繪製背景透明的「洞」。但是,如果我爲文本顏色(alpha通道= 254)添加了一點透明度,字體就變得不透明。或者,如果我使用常規(非分層)窗口,字體呈現不透明。 這裏發生了什麼?
即使沒有分層/透明度問題,很明顯這裏發生了一些奇怪的事情。字體大小8.49 - 16.48獲得渲染像素完美,其他字體有輕微模糊的質量,特別是小的。所以看起來系統採用不同的方法來渲染這些中等尺寸。 有人可以解釋這一點,我怎麼可能渲染例如字體大小8.0像素沒有上面的模糊?我已經試過各種設置SetTextRenderingHint()
和SetTextContrast()
但沒有對我試圖宋體&大小爲8的字體宋體只有...
邊問題1看着脆: 我想用純粹的GDI +用於屏幕外繪圖,但我無法通過簡單地創建對象來獲得它的工作。我仍然不得不使用舊的GDI東西來創建一個DC並選擇HBitmap。我如何在GDI +中做到這一切?
邊問題2(怪才只): 我也試圖將字體繪製在良好的老GDI但我得到了一些更離奇的效果:(1)在分層窗口中的文本變得透明但在添加劑的方式。 (所以如果後面的窗口很暗,紅色的文字看起來會很好,但如果窗口後面的窗口是白色的,文本就會完全消失!)另外,如果我用自己的半透明正方形填充自己的窗口,那麼表現會像預期的那樣。 (如果它後面的窗口是黑色的,紅色方塊會變成深紅色,並且方塊會在白色窗口上變成淺紅色)。我可以在一個分層窗口中同時觀察這兩種行爲。和(2)作爲一個非常不受歡迎的獎金的繪製文本失去了它的命中測試,併成爲無法點擊? 有什麼解釋嗎?
如果你已經閱讀了這麼多,感謝你的忍耐,謝謝你的答案!
// Create as a console application project
// + Unicode charset
// + Precompiled headers off
// + make sure to add linker input: gdiplus.lib
#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later.
#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows.
#endif
// Standard and GDI+ stuffstuff
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <iostream>
#include <cassert>
#include <Gdiplus.h>
using namespace Gdiplus;
GdiplusStartupInput g_oGdiPlusStartupInput;
ULONG_PTR g_pGdiPlusToken = NULL;
// #*#*#*#*#*#*#*#*# LINES TO CHANGE ---------->---------->---------->
Color g_oTextColor(255, 240, 0, 0); // Simply change Color to (254, 240, 0, 0) [to add slight transparency] and everything will work!
#define USE_LAYERED_WINDOW // or just comment this line out [to use a regular window], and everything will work!
// Forward declarations
void RegWndClass();
LRESULT CALLBACK WndProc(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam);
void CreateWindows();
void Draw();
void MsgLoop();
// Other Globals
ATOM g_iWndClass = 0;
HWND g_hWndGdiPlus = NULL;
HWND g_hWndGdi = NULL;
const wchar_t* g_pWndClass = L"TST";
int g_iWidth = 200;
int g_iHeight = 200;
// Main entry-point
int _tmain(int argc, _TCHAR* argv[])
{
GdiplusStartup(&g_pGdiPlusToken, &g_oGdiPlusStartupInput, NULL);
RegWndClass();
CreateWindows();
Draw();
MsgLoop();
::UnregisterClass(g_pWndClass, NULL);
::Sleep(500);
GdiplusShutdown(g_pGdiPlusToken);
return 0;
} // _tmain
void CreateWindows()
{
#ifdef USE_LAYERED_WINDOW
// The key trick is to create a window with style WS_EX_LAYERED, but WITHOUT any subsequent calls to SetLayeredWindowAttributes()
// This gives us a magic window that must be updated with UpdateLayeredWindow() (and it does NOT recieve any WM_PAINT messages)
// as brilliantly described in: http://alexkr.com/source-code/50/layered-windows-and-updatelayeredwindow/
g_hWndGdiPlus = ::CreateWindowEx(WS_EX_LAYERED, g_pWndClass, L"", WS_POPUP | WS_VISIBLE, 1000, 200, g_iWidth, g_iHeight, NULL, NULL, NULL, NULL);
#else
g_hWndGdiPlus = ::CreateWindowEx(0, g_pWndClass, L"", WS_OVERLAPPEDWINDOW | WS_POPUP | WS_VISIBLE, 1000, 200, g_iWidth, g_iHeight, NULL, NULL, NULL, NULL);
#endif
//g_hWndGdi = ::CreateWindowEx(WS_EX_LAYERED, g_pWndClass, L"", WS_POPUP | WS_VISIBLE, 720, 500, 200, 200, NULL, NULL, NULL, NULL);
} // CreateWindows
void Draw()
{
// Init GDI+ surface
HDC hOff = ::CreateCompatibleDC(NULL);
Bitmap oDaBigOne(g_iWidth, g_iHeight, PixelFormat32bppARGB);
HBITMAP hBMit = NULL;
Color oCol(0, 0, 0, 0);
oDaBigOne.GetHBITMAP(oCol, &hBMit);
HGDIOBJ hSave = ::SelectObject(hOff, hBMit);
#ifdef USE_LAYERED_WINDOW
Graphics oGraph(hOff);
#else
Graphics oGraph(g_hWndGdiPlus);
#endif
oGraph.Clear(Color(255, 55, 155, 255));
// Draw text
oGraph.SetTextRenderingHint(TextRenderingHintAntiAliasGridFit);
oGraph.SetTextContrast(0xffffffff);
oGraph.SetCompositingMode(CompositingModeSourceOver);
oGraph.SetCompositingQuality(CompositingQualityHighQuality);
oGraph.SetPixelOffsetMode(PixelOffsetModeHighQuality);
const FontFamily oFamily(L"Tahoma", NULL);
#if 1 // Use bold
Font oF600(&oFamily, 6.00, FontStyle::FontStyleBold, Unit::UnitPixel);
Font oF800(&oFamily, 8.00, FontStyle::FontStyleBold, Unit::UnitPixel);
Font oF848(&oFamily, 8.48, FontStyle::FontStyleBold, Unit::UnitPixel);
Font oF849(&oFamily, 8.49, FontStyle::FontStyleBold, Unit::UnitPixel);
Font oF1200(&oFamily, 12.00, FontStyle::FontStyleBold, Unit::UnitPixel);
Font oF1500(&oFamily, 15.00, FontStyle::FontStyleBold, Unit::UnitPixel);
Font oF1648(&oFamily, 16.48, FontStyle::FontStyleBold, Unit::UnitPixel);
Font oF1649(&oFamily, 16.49, FontStyle::FontStyleBold, Unit::UnitPixel);
#else // Use regular
Font oF600(&oFamily, 6.00, FontStyle::FontStyleRegular, Unit::UnitPixel);
Font oF800(&oFamily, 8.00, FontStyle::FontStyleRegular, Unit::UnitPixel);
Font oF848(&oFamily, 8.48, FontStyle::FontStyleRegular, Unit::UnitPixel);
Font oF849(&oFamily, 8.49, FontStyle::FontStyleRegular, Unit::UnitPixel);
Font oF1200(&oFamily, 12.00, FontStyle::FontStyleRegular, Unit::UnitPixel);
Font oF1500(&oFamily, 15.00, FontStyle::FontStyleRegular, Unit::UnitPixel);
Font oF1648(&oFamily, 16.48, FontStyle::FontStyleRegular, Unit::UnitPixel);
Font oF1649(&oFamily, 16.49, FontStyle::FontStyleRegular, Unit::UnitPixel);
#endif
assert(oF600.GetLastStatus() == Ok); // Make sure font is OK
SolidBrush oBrush(g_oTextColor);
double dy = 1.0;
oGraph.DrawString(L"Size 6.00", -1, &oF600, PointF(30.0, dy += 18.0), &oBrush);
oGraph.DrawString(L"Size 8.00", -1, &oF800, PointF(30.0, dy += 18.0), &oBrush);
oGraph.DrawString(L"Size 8.48", -1, &oF848, PointF(30.0, dy += 18.0), &oBrush);
oGraph.DrawString(L"Size 8.49", -1, &oF849, PointF(30.0, dy += 18.0), &oBrush);
oGraph.DrawString(L"Size 12.00", -1, &oF1200, PointF(30.0, dy += 18.0), &oBrush);
oGraph.DrawString(L"Size 15.00", -1, &oF1500, PointF(30.0, dy += 18.0), &oBrush);
oGraph.DrawString(L"Size 16.48", -1, &oF1648, PointF(30.0, dy += 18.0), &oBrush);
oGraph.DrawString(L"Size 16.49", -1, &oF1649, PointF(30.0, dy += 18.0), &oBrush);
#ifndef USE_LAYERED_WINDOW
return;
#endif
// Do da layered window magic stuff
BLENDFUNCTION oBF = { 0 };
oBF.BlendOp = AC_SRC_OVER;
oBF.BlendFlags = 0;
oBF.SourceConstantAlpha = 255;
oBF.AlphaFormat = AC_SRC_ALPHA;
SIZE oSize = { 0 };
oSize.cx = g_iWidth;
oSize.cy = g_iHeight;
POINT oPTZero = { 0 };
RECT oRect = { 0 };
::GetWindowRect(g_hWndGdiPlus, &oRect);
POINT oPTWnd = { 0 };
oPTWnd.x = oRect.left;
oPTWnd.y = oRect.top;
//HDC hDC = oGraph.GetHDC();
BOOL bOK = ::UpdateLayeredWindow(g_hWndGdiPlus,
NULL, //HDC hdcDst,
&oPTWnd, // POINT &oPtNull,
&oSize, // SIZE *psize,
hOff, // HDC hdcSrc,
&oPTZero, // POINT *pptSrc,
RGB(255,255,255), // COLORREF crKey,
&oBF, // BLENDFUNCTION *pblend,
ULW_ALPHA // DWORD dwFlags
);
} // Draw
void MsgLoop()
{
::SetTimer(g_hWndGdiPlus, 0, 19999, NULL); // Self-destruct timer
MSG msg = { 0 };
while (::GetMessage(&msg, NULL, 0, 0))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
} // MsgLoop
void RegWndClass()
{
WNDCLASSEX wcex = { 0 };
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 8; // 8 bytes, to allow for 64-bit architecture
wcex.hInstance = NULL; // CHECK
wcex.hIcon = NULL;
wcex.hCursor = ::LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)NULL_BRUSH; // CHECK
wcex.lpszMenuName = NULL;
wcex.lpszClassName = g_pWndClass;
wcex.hIconSm = NULL;
g_iWndClass = ::RegisterClassEx(&wcex);
} // RegWndClass
LRESULT CALLBACK WndProc(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
switch(uiMsg)
{
case WM_TIMER:
{
std::wstring s;
std::wcout << L"Let´s quit" ;
::PostQuitMessage(0);
return 0;
}
case WM_PAINT:
Draw();
break;
default:
{
return DefWindowProc(hWnd, uiMsg, wParam, lParam);
}
}
return DefWindowProc(hWnd, uiMsg, wParam, lParam);
} // WndProc
[編輯]問題解決了! 以下代碼根據Rodrogo´s優秀建議。 榮譽和對他的巨大感謝。我很感激。
所有編輯都標有//#MOD
// Create as a console application project
// + Unicode charset
// + Precompiled headers off
// + make sure to add linker input: gdiplus.lib
#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later.
#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows.
#endif
// Standard stuff
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <iostream>
#include <cassert>
// GDI+ stuff
#include <Gdiplus.h>
using namespace Gdiplus;
GdiplusStartupInput g_oGdiPlusStartupInput;
ULONG_PTR g_pGdiPlusToken = NULL;
// #*#*#*#*#*#*#*#*# LINES TO CHANGE ---------->---------->---------->
Color g_oTextColor(255, 240, 0, 0); // Simply change Color to (254, 240, 0, 0) [to add slight transparency] and everything will work!
#define USE_LAYERED_WINDOW // or just omment this line [to use a regular window], and everything will work!
// Forward declarations
void RegWndClass();
LRESULT CALLBACK WndProc(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam);
void CreateWindows();
void Draw();
void MsgLoop();
// Other Globals
ATOM g_iWndClass = 0;
HWND g_hWndGdiPlus = NULL;
HWND g_hWndGdi = NULL;
const wchar_t* g_pWndClass = L"TST";
int g_iWidth = 200;
int g_iHeight = 200;
// Main entry-point
int _tmain(int argc, _TCHAR* argv[])
{
GdiplusStartup(&g_pGdiPlusToken, &g_oGdiPlusStartupInput, NULL);
RegWndClass();
CreateWindows();
Draw();
MsgLoop();
::UnregisterClass(g_pWndClass, NULL);
::Sleep(500);
GdiplusShutdown(g_pGdiPlusToken);
return 0;
} // _tmain
void CreateWindows()
{
#ifdef USE_LAYERED_WINDOW
// The key trick is to create a window with style WS_EX_LAYERED, but WITHOUT any subsequent calls to SetLayeredWindowAttributes()
// This gives us a magic window that must be updated with UpdateLayeredWindow() (and it does NOT recieve any WM_PAINT messages)
// as brilliantly described in: http://alexkr.com/source-code/50/layered-windows-and-updatelayeredwindow/
g_hWndGdiPlus = ::CreateWindowEx(WS_EX_LAYERED, g_pWndClass, L"", WS_POPUP | WS_VISIBLE, 1000, 200, g_iWidth, g_iHeight, NULL, NULL, NULL, NULL);
#else
g_hWndGdiPlus = ::CreateWindowEx(0, g_pWndClass, L"", WS_OVERLAPPEDWINDOW | WS_POPUP | WS_VISIBLE, 1000, 200, g_iWidth, g_iHeight, NULL, NULL, NULL, NULL);
#endif
//g_hWndGdi = ::CreateWindowEx(WS_EX_LAYERED, g_pWndClass, L"", WS_POPUP | WS_VISIBLE, 720, 500, 200, 200, NULL, NULL, NULL, NULL);
} // CreateWindows
void Draw()
{
// Init GDI+ surface
HDC hOff = ::CreateCompatibleDC(NULL);
Bitmap oDaBigOne(g_iWidth, g_iHeight, PixelFormat32bppARGB);
HBITMAP hBMit = NULL;
Color oCol(0, 0, 0, 0);
// oDaBigOne.GetHBITMAP(oCol, &hBMit); //#MOD
// HGDIOBJ hSave = ::SelectObject(hOff, hBMit); //#MOD
{ // Limit oGraph scope //#MOD
#ifdef USE_LAYERED_WINDOW
//Graphics oGraph(hOff); //#MOD
Graphics oGraph(&oDaBigOne); //#MOD
#else
Graphics oGraph(g_hWndGdiPlus);
#endif
oGraph.Clear(Color(255, 55, 155, 255));
// Draw text
oGraph.SetTextRenderingHint(TextRenderingHintAntiAliasGridFit);
oGraph.SetCompositingMode(CompositingModeSourceOver);
oGraph.SetCompositingQuality(CompositingQualityHighQuality);
oGraph.SetPixelOffsetMode(PixelOffsetModeHighQuality);
const FontFamily oFamily(L"Tahoma", NULL);
#if 1 // Use bold
Font oF600(&oFamily, 6.00, FontStyle::FontStyleBold, Unit::UnitPixel);
Font oF848(&oFamily, 8.48, FontStyle::FontStyleBold, Unit::UnitPixel);
Font oF849(&oFamily, 8.49, FontStyle::FontStyleBold, Unit::UnitPixel);
Font oF1200(&oFamily, 12.00, FontStyle::FontStyleBold, Unit::UnitPixel);
Font oF1500(&oFamily, 15.00, FontStyle::FontStyleBold, Unit::UnitPixel);
Font oF1648(&oFamily, 16.48, FontStyle::FontStyleBold, Unit::UnitPixel);
Font oF1649(&oFamily, 16.49, FontStyle::FontStyleBold, Unit::UnitPixel);
#else // Use regular
Font oF600(&oFamily, 6.00, FontStyle::FontStyleRegular, Unit::UnitPixel);
Font oF848(&oFamily, 8.48, FontStyle::FontStyleRegular, Unit::UnitPixel);
Font oF849(&oFamily, 8.49, FontStyle::FontStyleRegular, Unit::UnitPixel);
Font oF1200(&oFamily, 12.00, FontStyle::FontStyleRegular, Unit::UnitPixel);
Font oF1500(&oFamily, 15.00, FontStyle::FontStyleRegular, Unit::UnitPixel);
Font oF1648(&oFamily, 16.48, FontStyle::FontStyleRegular, Unit::UnitPixel);
Font oF1649(&oFamily, 16.49, FontStyle::FontStyleRegular, Unit::UnitPixel);
#endif
assert(oF600.GetLastStatus() == Ok); // Make sure font is OK
SolidBrush oBrush(g_oTextColor);
double dy = 10.0;
oGraph.DrawString(L"Size 6.00", -1, &oF600, PointF(30.0, dy += 18.0), &oBrush);
oGraph.DrawString(L"Size 8.48", -1, &oF848, PointF(30.0, dy += 18.0), &oBrush);
oGraph.DrawString(L"Size 8.49", -1, &oF849, PointF(30.0, dy += 18.0), &oBrush);
oGraph.DrawString(L"Size 12.00", -1, &oF1200, PointF(30.0, dy += 18.0), &oBrush);
oGraph.DrawString(L"Size 15.00", -1, &oF1500, PointF(30.0, dy += 18.0), &oBrush);
oGraph.DrawString(L"Size 16.48", -1, &oF1648, PointF(30.0, dy += 18.0), &oBrush);
oGraph.DrawString(L"Size 16.49", -1, &oF1649, PointF(30.0, dy += 18.0), &oBrush);
#ifndef USE_LAYERED_WINDOW
return;
#endif
} // Limit oGraph scope //#MOD
// Do da layered window magic stuff
BLENDFUNCTION oBF = { 0 };
oBF.BlendOp = AC_SRC_OVER;
oBF.BlendFlags = 0;
oBF.SourceConstantAlpha = 255;
oBF.AlphaFormat = AC_SRC_ALPHA;
SIZE oSize = { 0 };
oSize.cx = g_iWidth;
oSize.cy = g_iHeight;
POINT oPTZero = { 0 };
RECT oRect = { 0 };
::GetWindowRect(g_hWndGdiPlus, &oRect);
POINT oPTWnd = { 0 };
oPTWnd.x = oRect.left;
oPTWnd.y = oRect.top;
oDaBigOne.GetHBITMAP(oCol, &hBMit); //#MOD
HGDIOBJ hSave = ::SelectObject(hOff, hBMit); //#MOD
//HDC hDC = oGraph.GetHDC();
BOOL bOK = ::UpdateLayeredWindow(g_hWndGdiPlus,
NULL, //HDC hdcDst,
&oPTWnd, // POINT &oPtNull,
&oSize, // SIZE *psize,
hOff, // HDC hdcSrc,
&oPTZero, // POINT *pptSrc,
RGB(255,255,255), // COLORREF crKey,
&oBF, // BLENDFUNCTION *pblend,
ULW_ALPHA // DWORD dwFlags
);
} // Draw
void MsgLoop()
{
::SetTimer(g_hWndGdiPlus, 0, 19999, NULL); // Self-destruct timer
MSG msg = { 0 };
while (::GetMessage(&msg, NULL, 0, 0))
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
} // MsgLoop
void RegWndClass()
{
WNDCLASSEX wcex = { 0 };
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 8; // 8 bytes, to allow for 64-bit architecture
wcex.hInstance = NULL; // CHECK
wcex.hIcon = NULL;
wcex.hCursor = ::LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)NULL_BRUSH; // CHECK
wcex.lpszMenuName = NULL;
wcex.lpszClassName = g_pWndClass;
wcex.hIconSm = NULL;
g_iWndClass = ::RegisterClassEx(&wcex);
} // RegWndClass
LRESULT CALLBACK WndProc(HWND hWnd, UINT uiMsg, WPARAM wParam, LPARAM lParam)
{
switch(uiMsg)
{
case WM_TIMER:
{
std::wstring s;
std::wcout << L"Let´s quit" ;
::PostQuitMessage(0);
return 0;
}
case WM_PAINT:
Draw();
break;
default:
{
return DefWindowProc(hWnd, uiMsg, wParam, lParam);
}
}
return DefWindowProc(hWnd, uiMsg, wParam, lParam);
} // WndProc
THX!聽起來不錯。恐怕本週不能嘗試這個,但我會遲一點。併發布反饋。乾杯。 – Adam 2011-08-09 06:30:18
羅德里戈,其實我找了一點時間來嘗試一下。對不起,沒有雪茄......如果我沒有選擇DC,則GDI +操作將呈現:無。 (事實上 - 我意識到 - 很久以前,我已經嘗試了這一點,正如上面的「側面問題#1」所暗示的那樣)。所以我仍然有問題的GDI +渲染出錯的一些字體大小的Tahoma。還有待嘗試的是一種純粹的GDI方法,如以下答覆中所述:http://www.gamedev.net/topic/333453-32-bit-alpha-bitmaps-and-gdi-fun-alert/ – Adam 2011-08-09 08:00:54
好奇,但我試過了,它爲我工作。你能用這種方法發佈代碼嗎?或者,如果你願意,我可以發佈我的;-) – rodrigo 2011-08-09 13:45:28