如何解决GDI +字体渲染,尤其是在分层窗口中
| 我的朋友们,这将是一个漫长的... 当我尝试在分层窗口中渲染文本时,我得到一些非常奇怪的行为。 奇怪的是,对于字体/字体样式/字体大小的某些组合,GDI +更改了呈现方法。对于Tahoma-Bold字体,字体大小在8.49和16.49(像素单位)之间(包括“失败”)。对于其他字体和样式,我得到不同大小的“失败”字样。 为了清楚起见,我在下面提供了完整的可执行示例。第23行显示两个要使用的关键参数:Color g_oTextColor( 255,240,0 ); // Simply change Color to ( 254,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号字体来说,它们看起来都不清晰。我只尝试了Tahoma&Arial ...
补充问题1:
我想使用纯GDI +进行屏幕外绘图,但仅创建Bitmap
和Graphics
对象就无法使其工作。我仍然必须使用旧的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,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 );
#else
g_hWndGdiPlus = ::CreateWindowEx( 0,WS_OVERLAPPEDWINDOW | WS_POPUP | WS_VISIBLE,NULL );
#endif
//g_hWndGdi = ::CreateWindowEx( WS_EX_layered,720,500,NULL );
} // CreateWindows
void Draw()
{
// Init GDI+ surface
HDC hOff = ::CreateCompatibleDC( NULL );
Bitmap oDaBigOne( g_iWidth,PixelFormat32bppARGB );
HBITMAP hBMit = NULL;
Color oCol( 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,Unit::UnitPixel );
Font oF848( &oFamily,8.48,Unit::UnitPixel );
Font oF849( &oFamily,8.49,Unit::UnitPixel );
Font oF1200( &oFamily,12.00,Unit::UnitPixel );
Font oF1500( &oFamily,15.00,Unit::UnitPixel );
Font oF1648( &oFamily,16.48,Unit::UnitPixel );
Font oF1649( &oFamily,16.49,Unit::UnitPixel );
#else // Use regular
Font oF600( &oFamily,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\",&oF800,&oBrush );
oGraph.DrawString( L\"Size 8.48\",&oF848,&oBrush );
oGraph.DrawString( L\"Size 8.49\",&oF849,&oBrush );
oGraph.DrawString( L\"Size 12.00\",&oF1200,&oBrush );
oGraph.DrawString( L\"Size 15.00\",&oF1500,&oBrush );
oGraph.DrawString( L\"Size 16.48\",&oF1648,&oBrush );
oGraph.DrawString( L\"Size 16.49\",&oF1649,&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,//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,19999,NULL ); // Self-destruct timer
MSG msg = { 0 };
while ( ::GetMessage( &msg,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,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,lParam );
} // WndProc
[编辑]问题解决了!
根据Rodrogo的出色建议,编写以下代码。
感谢他,并深表感谢。我真的很感激
所有编辑都标记为//#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,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,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,255 ) );
// Draw text
oGraph.SetTextRenderingHint( TextRenderingHintAntiAliasGridFit );
oGraph.SetCompositingMode( CompositingModeSourceOver );
oGraph.SetCompositingQuality( CompositingQualityHighQuality );
oGraph.SetPixelOffsetMode( PixelOffsetModeHighQuality );
const FontFamily oFamily( L\"Tahoma\",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\",&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,lParam );
} // WndProc
解决方法
我认为问题在于,GDI(不带+)不能很好地支持alpha透明度。充其量,它保持alpha通道不变。
当您使用具有选定位图且具有Alpha通道的HDC使用HDC构建图形对象时,事情会变得很糟。我的猜测是,光栅化的GDI +字体会根据许多参数来决定使用哪种方法进行渲染。然后,如果GDI恰好支持此方法,则将使用它(并且忽略Alpha通道);如果GDI不支持render方法,它将回退到逐像素或类似的渲染,并且可以正确使用alpha通道。
因此,为了获得正确的结果,您不应使用HDC修改Alpha通道。尝试以下更改:
使用位图创建Graphics对象,而不是HDC:
Graphics oGraph( &oDaBigOne );
仅在渲染完成后,才将位图选择到HDC hOff中。为确保销毁Graphics对象,最好用{...}限制其范围。
[编辑]
使用新代码后,解决方案很容易:不仅应在绘图之后将调用移至SelectObject(),还应将GetBitmap()移至。也就是说,这两个函数应该在:UpdateLayeredWindow()调用之前:
oDaBigOne.GetHBITMAP( oCol,&hBMit );
HGDIOBJ hSave = ::SelectObject( hOff,hBMit );
,我想,昨天我找到了一个(部分)解决方案,比rodrigo建议的解决方案更容易实现:您可以在传递给DrawString
的StringFormat
实例中简单地将StringFormatFlags::StringFormatFlagsBypassGDI
指定为格式标志。瞧:一切都是使用具有Alpha功能的字体渲染器绘制的。这至少解决了Alpha通道问题。
,我暂时无法找到这种奇怪行为的解释。但是,我确实发现使用默认渲染值可以对某些字体提供更好的效果,请尝试使用以下设置进行Arial:
oGraph.SetTextRenderingHint( TextRenderingHintAntiAliasGridFit );
oGraph.SetPixelOffsetMode( PixelOffsetModeDefault );
oGraph.SetCompositingMode( CompositingModeSourceOver );
oGraph.SetCompositingQuality( CompositingQualityDefault );
,尝试将PixelFormat32bppARGB
换成PixelFormat32bppPARGB
。 BLENDFUNCTION
结构的文档指出,它需要在源文件中预乘alpha。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。