Это мои друзья, это будет долго ...
Я получаю довольно странное поведение, когда пытаюсь сделать текст в многослойном окне.
Странная вещьзаключается в том, что для некоторых комбинаций 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!
При использовании многослойных окон и полной непрозрачности шрифты рисуют прозрачную «дыру» на фоне.Однако, если я добавлю небольшую прозрачность к цвету текста (альфа-канал = 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, 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
[ПРАВИТЬ]Задача решена!Код ниже в соответствии с Родрого отличные предложения.Слава и огромное количество спасибо ему.Я очень благодарен.
Все изменения помечены как // # 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