Отрисовка шрифтов GDI+, особенно в многослойных окнах


Это, друзья мои, будет долгим...

Я получаю довольно странное поведение, когда пытаюсь отрисовать текст в многослойном окне.

Странная вещь заключается в том, что для некоторых комбинаций font/font-style/font-size GDI+ изменяет метод рендеринга. Для Тахома-жирных шрифтов размеры между 8.49 и 16.49 (пиксель-единицы) включительно "fail". Для других шрифтов и стилей я получаю "fails" в разных размерах.

вывод из кода ниже

Для ясности я предоставил полный исполняемый пример ниже.Два ключевых параметра для игры находятся в строке 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!

При использовании многослойных окон и полной непрозрачности шрифты рисуют прозрачную "дыру" на заднем плане. Однако, если я добавлю небольшую прозрачность к цвету текста (альфа-канал = 254), шрифты станут непрозрачными. Или если я использую обычные (неслоистые) окна, шрифты становятся непрозрачными. что здесь происходит??

Но даже без многослойных / прозрачных проблем это понятно, что здесь происходит что-то странное. Шрифты размером 8.49-16.48 отрисовываются идеально, другие шрифты имеют небольшое размытое качество, особенно маленькие. Таким образом, похоже, что система использует другой подход к визуализации этих средних размеров. Может ли кто-нибудь пролить свет на это, как я могу визуализировать, например, шрифты размером 8.0 пикселей без размытости выше? я перепробовал все виды настроек для SetTextRenderingHint() и SetTextContrast(), но ни один не выглядел четким для шрифтов размер 8. Я пробовал только Tahoma & Arial...


Побочный вопрос 1: Я хотел использовать чистый GDI+ для закадрового рисования, но я не мог заставить его работать, просто создавая Bitmap & Graphics объекты. Мне все еще приходилось использовать старые вещи GDI, чтобы создать DC и выбрать HBitmap в него. Как я могу сделать все это в GDI+?

Побочный вопрос 2 (только для гиков): Я также попытался нарисовать шрифты в старом добром GDI, но там я получил некоторые еще более странные эффекты: (1) В a послойное окно текст стал прозрачным, но в виде аддитивного способа. (Таким образом, красный текст выглядел бы прекрасно, если бы окно позади было темным, но если окно позади него было белым, текст полностью исчез!) Более того, если я заполнил свое собственное окно полупрозрачным квадратом, то это ведет себя так, как и ожидалось. (Красный квадрат становился темно-красным, если окно за ним было черным, а квадрат становился светло-красным над Белым окном). И я могу наблюдать оба этих поведения одновременно в одно многослойное окно. И еще (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

[EDIT] проблема решена! Код ниже в соответствии сRodrogos отличные предложения. Слава и огромное количество благодарностей ему. Я вам очень благодарен.

Все правки помечены //#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 8

4 ответа:

Я думаю, что проблема в том, что GDI (без +) не очень хорошо поддерживает Альфа-прозрачность. В лучшем случае, он сохраняет альфа-канал нетронутым.

При построении графического объекта с использованием HDC с выбранным растровым изображением, имеющим альфа-канал... ну, все плохо перемешивается. Я предполагаю, что растеризованный шрифт GDI + решает, какой метод использовать для визуализации на основе большого количества параметров; затем, если этот метод будет поддерживаться в GDI, он будет использоваться (и альфа-канал игнорируется); если метод рендеринга не поддерживается в GDI, он будет откатываться к пиксельному рендерингу или аналогичному, и альфа-канал будет использоваться правильно.

Таким образом, чтобы получить правильные результаты, вы не должны использовать HDC для изменения Альфа-канала. Попробуйте внести следующие изменения:
  • Используйте растровое изображение для создания графического объекта, а не HDC:

    Graphics oGraph( &oDaBigOne );
    
  • Выберите растровое изображение в HDC hOff только после рендеринга. Конечно, это лучше, чем ... графический объект уничтожается, ограничивая его область видимости с помощью { ... }.

[EDIT]

После нового кода решение будет простым: вы должны не только переместить вызов SelectObject () после чертежа, но и GetBitmap (). То есть эти две функции должны быть непосредственно перед вызовом: UpdateLayeredWindow ():

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

Я думаю, вчера я нашел (частичное) решение этой проблемы, которое может быть более простым в реализации, чем предложенное Родриго: вы можете просто указать StringFormatFlags::StringFormatFlagsBypassGDI в качестве флага формата в StringFormat-экземпляре, который вы передаете DrawString, и вуаля: все рисуется с помощью альфа-способного шрифта-рендера. По крайней мере, это устраняет проблему Альфа-канала...

Некоторое время я не мог найти объяснения этому странному поведению. Однако я обнаружил, что использование значений рендеринга по умолчанию дает гораздо лучшие результаты с некоторыми шрифтами, попробуйте Arial со следующими настройками:

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

Попробуйте заменить PixelFormat32bppARGB на PixelFormat32bppPARGB. В документации к структуре BLENDFUNCTION говорится, что для нее требуется предварительно умноженная Альфа в исходном коде.