GDI+字体渲染,特别是在分层窗口中的应用

9
这是一篇比较长的文章,我将为您翻译。这篇文章与IT技术有关,在渲染分层窗口文本时出现了一些奇怪的行为。
奇怪的是,对于某些字体/字形/字号的组合,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号字体来说,没有一个看起来清晰。我只尝试过Tahoma和Arial...


附加问题1: 我想使用纯GDI+进行离屏绘制,但我无法通过简单创建BitmapGraphics对象来使其工作。我仍然需要使用旧的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

[编辑] 问题解决! 按照Rodrigo的极佳建议重新编写代码。 向他致以敬意和无限感谢,我非常感激。

所有编辑都标有//#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
4个回答

7
我认为问题在于GDI(不带+)对Alpha透明度的支持不够好。最好的情况是,它保持Alpha通道不变。
当您使用具有Alpha通道的选择位图的HDC构建Graphics对象时......会出现混合问题。我猜测GDI+字体光栅化器会根据许多参数决定使用哪种方法进行渲染;然后,如果这种方法恰好受到GDI支持,它将被使用(而Alpha通道将被忽略);如果渲染方法在GDI中不受支持,它将回退到像素级渲染或类似的方法,并且Alpha通道将被正确使用。
因此,要获得正确的结果,您不应该使用HDC来修改Alpha通道。尝试以下更改:
  • Use the bitmap to create the Graphics object, not the HDC:

    Graphics oGraph( &oDaBigOne );
    
  • Select the bitmap into the HDC hOff only after the render has been done. To be sure it is better that the Graphics object is destroyed, limiting its scope with { ... }.

[编辑]

在新代码之后,解决方案很简单:不仅应该在绘图之后调用SelectObject(),还应该在GetBitmap()函数之后调用。也就是说,这两个函数应该在调用UpdateLayeredWindow()之前:

oDaBigOne.GetHBITMAP( oCol, &hBMit );
HGDIOBJ hSave = ::SelectObject( hOff, hBMit );

谢谢!听起来很有趣。我这周恐怕无法尝试,但以后会的。然后会发布反馈。干杯。 - Adam
Rodrigo,我找到了一点时间来尝试这个。很抱歉,没有成功......如果我没有选择DC,则GDI+操作将呈现:无内容。(事实上-我意识到-我曾经尝试过这个,就像上面的“附带问题#1”所暗示的那样)。因此,对于某些Tahoma字体大小,GDI+渲染出现问题仍然存在。有待尝试的是纯GDI方法,如http://www.gamedev.net/topic/333453-32-bit-alpha-bitmaps-and-gdi-fun-alert/中的一些答案所概述的那样。 - Adam
好奇心驱使我尝试了一下,对我而言它起作用了。你可以发一下使用这种方法的代码吗?如果你愿意,我也可以发我的代码;-) - rodrigo
Rodrigo!非常感谢您友好、精确和出色的帮助。太棒了! - Adam
不客气!请注意,您的代码存在相当多的资源泄漏。我猜测这是因为您只复制了相关代码,但以防万一,请注意检查。 - rodrigo
谢谢,我知道这不是生产代码。我只是用最少的精力从一个更大的源代码中创建了这个独立可执行文件。顺便说一句,看到你的博客有英文版会很好。 - Adam

2

我认为,昨天我找到了一个(部分)解决方案,可能比rodrigo建议的更容易实现:您可以在传递给DrawStringStringFormat实例中指定StringFormatFlags::StringFormatFlagsBypassGDI作为格式标志,然后就万事大吉了:一切都使用具有alpha通道能力的字体渲染器进行绘制。这至少解决了alpha通道问题...


1

我一直无法找到关于这种奇怪行为的解释。然而,我发现使用默认渲染值可以在某些字体上获得更好的结果,尝试使用以下设置来使用Arial:

oGraph.SetTextRenderingHint( TextRenderingHintAntiAliasGridFit );
oGraph.SetPixelOffsetMode( PixelOffsetModeDefault );
oGraph.SetCompositingMode( CompositingModeSourceOver );
oGraph.SetCompositingQuality( CompositingQualityDefault );

0

尝试将PixelFormat32bppARGB替换为PixelFormat32bppPARGBBLENDFUNCTION结构的文档说明需要在源中使用预乘alpha。


好的,谢谢,但我已经尝试过了。不幸的是没有运气。 - Adam

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接