GDI +字体渲染(和分层窗口) [英] GDI+ font rendering (and layered windows)

查看:233
本文介绍了GDI +字体渲染(和分层窗口)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的朋友们,这将是一个漫长的......

当我尝试在分层窗口中使用GDI +渲染文本时,我得到一些非常奇怪的行为.

奇怪的是,对于字体/字体样式/字体大小的某些组合,GDI +更改了呈现方法.对于Tahoma-Bold字体,字体大小在8.49到16.49(像素单位)之间(包括失败").对于其他字体和样式,我会得到不同大小的失败".

下面代码的输出
http://i.imgur.com/lFvX4.png [

This my friends, is a going to be long one...

I am getting some quite bizarre behaviour when I try to render text with GDI+ in a layered window.

The strange thing is that for some combinations of font/font-style/font-size, GDI+ changes the rendering method. For Tahoma-Bold fonts sizes between 8.49 and 16.49 (Pixel-Units) inclusive "fail". For other fonts and styles I get "fails" at different sizes.

output from code below
http://i.imgur.com/lFvX4.png[^]

For clarity I have provided a complete executable example further down. The two key parameters to play with are on line 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-channel = 254)添加一点透明度,字体就会变得不透明!或者如果我使用常规(非分层)窗口,字体也会变得不透明. 这是怎么回事?

但是,即使没有分层/透明性问题,也很明显这里发生了一些奇怪的事情.字体大小为8.49-16.48的字体可完美呈现像素,其他字体则具有轻微的模糊质量,尤其是较小的字体.因此,似乎系统采用了不同的方法来呈现这些中等大小.有人可以对此有所启发吗?我如何渲染例如8.0像素大小的字体而没有上面的模糊性?我尝试了SetTextRenderingHint()SetTextContrast()的各种设置,但是看起来都不清晰字体大小为8的字体.我尝试过Tahoma&仅限Arial ...

侧面问题1:我想为屏幕外的绘图使用纯GDI +,但仅创建Bitmap&图形对象.我仍然必须使用旧的GDI东西来创建DC并选择HBitmap.如何在GDI +中完成所有操作?

侧面问题2(仅适用于极客):我也尝试在陈旧的GDI中绘制字体,但在那里我得到了一些更奇怪的效果:(A)窗口使文本变得透明,但以加法方式. (因此,如果后面的窗口很暗,但如果后面的窗口是白色,则红色文本看起来会很好,但文本会完全消失!)此外,如果我在自己的窗口中填充了半透明的正方形,则其行为将达到预期的效果. (如果红色方格后面的窗口是黑色的,则红色方格会变成深红色,而白色方格上的红色方格会变成浅红色).而且我可以在一个分层的窗口中同时观察到这两种行为.而(B)作为高度不希望的奖励,则绘制的文本丢失了其命中测试并变得无法点击?有什么解释吗?

如果您已读完本文,则感谢您的耐心配合,并感谢您的任何回答!


When using layered windows and full opacity the fonts draw a transparent "hole" in the background. However if I add a slight transparency to the text colour (alpha-channel = 254) the fonts become opaque!! Or if I use regular (non-layered) windows the fonts render opaque. What is going on here??

But even without the layered/transparency problems it is clear that something strange is happening here. The fonts size 8.49 - 16.48 get rendered pixel perfect, the other fonts have slight blurry quality, especially the small ones. So it seems that the system takes a different approach to rendering these medium sizes. Can somebody shed some light on this, how can I possibly render for example fonts size 8.0 pixels without the blurriness above? I have tried all sorts of settings for SetTextRenderingHint() and SetTextContrast() but none looked crisp for fonts of size 8. I have tried Tahoma & Arial only...

Side question 1: I wanted to use pure GDI+ for the off-screen drawing but I could not get it work by simply creating Bitmap & Graphics objects. I still had to use old GDI stuff to create a DC and to select the HBitmap into it. How can I do it all in GDI+?

Side question 2 (Geeks only): I also tried to draw the fonts in good-old GDI but there I got some even more bizarre effects: (A) In a layered window the text became transparent but in an additive way. (So a red text would look fine if the window behind was dark but if the window behind it was whit the text disappeared completely!) Furthermore if I filled my own window with a semi-transparent square, then that behave as expected. (A red square would turn dark red if the window behind it was black, and the square would turn light red over a white window). And I can observe both these behaviours simultaneously in one layered window. And (B) as a highly undesired bonus the drawn text lost it''s hit-test and became un-clickable? Any explanations out there?

And if you have read this far, thanks for enduring, and thanks for any answers!

// 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

推荐答案

除了仅在某些颜色值中设置Alpha值外,Alpha混合还有更多功能.您描述的无法解释的行为可能是由于使用了预期之外的其他混合策略(图形设备的默认设置)导致的.

我在GDI +中通常没有太多关系,这在DirectX中是我更经常需要的.因此,我不能保证这会有所帮助,但是MSDN中的此页面可能是一个很好的起点:

http://msdn.microsoft.com/en-us/library/ms533803 (v = vs.85).aspx [
There is a little more to alpha blending than just setting the alpha values in some color values. The unexplained behavior you described may be the result of using another blending strategy (the default of the graphics device) than you expect.

I don''t usually have much to do with this in GDI+, this is something I more often need in DirectX. Therefore I can''t guarantee that this will help, but this page in the MSDN may be a good starting point:

http://msdn.microsoft.com/en-us/library/ms533803(v=vs.85).aspx[^]

I quickly skimmed through the text and found thet the Graphics::SetCompositingMode() and Graphics::SetCompositingQuality() Methods may be what you are looking for.

Edit^2: I took another look at your code and saw that you already use SetCompositingMode(). You use CompositingMode.SourceOver, which may be the problem. Have you tried CompositingMode.SourceCopy? This way the alpha value would be determined by the drawn color alone and not by the destination color.


这篇关于GDI +字体渲染(和分层窗口)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆