Esto mis amigos, es una va a ser larga ...GDI + de representación de fuentes, especialmente en ventanas superpuestas
estoy recibiendo un comportamiento bastante extraño cuando trato de representar el texto en una ventana de capas.
Lo extraño es que para algunas combinaciones de fuente/fuente-estilo/tamaño de fuente, GDI + cambia el método de representación. Para tamaños de fuente Tahoma-Bold entre 8.49 y 16.49 (Unidades de píxeles) inclusive "falla". Para otras fuentes y estilos me sale "falla" en diferentes tamaños.
Para mayor claridad que he proporcionado un ejemplo ejecutable completa más abajo. Los dos parámetros clave para jugar están en la línea 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!
Al utilizar ventanas superpuestas y opacidad completa las fuentes dibujar una transparente "agujero" en el fondo. Sin embargo, si agrego una ligera transparencia al color del texto (canal alfa = 254) las fuentes se vuelven opacas. O si uso ventanas regulares (sin capas) las fuentes se vuelven opacas. ¿Qué está pasando aquí?
Pero incluso sin los problemas de capas/transparencia, está claro que algo extraño está sucediendo aquí. Las fuentes tamaño 8.49 - 16.48 se convierten en píxeles perfectos, las otras fuentes tienen una ligera calidad borrosa, especialmente las pequeñas. Por lo tanto, parece que el sistema adopta un enfoque diferente para representar estos tamaños medios. ¿Alguien puede arrojar algo de luz sobre esto, cómo puedo renderizar, por ejemplo, fuentes de tamaño 8.0 píxeles sin la imagen borrosa arriba? que han intentado todo tipo de ajustes para SetTextRenderingHint()
y SetTextContrast()
pero ninguno parecía nítido para las fuentes de tamaño 8. He intentado Tahoma & Arial única ...
pregunta Lado 1: que quería utilizar GDI puro para el dibujo fuera de la pantalla pero no pude hacer que funcione simplemente creando Bitmap
& Graphics
objetos. Todavía tenía que usar material antiguo de GDI para crear un DC y seleccionar HBitmap en él. ¿Cómo puedo hacer todo en GDI +?
pregunta Lado 2 (sólo Geeks): También trataron de sacar las fuentes de buena de edad GDI pero no me dieron algunos efectos aún más extrañas: (1) En una ventana por capas el texto se volvió transparente pero en un aditivo manera. (Así que un texto rojo se vería bien si la ventana detrás estuviera oscura, pero si la ventana detrás de ella estaba con el texto desaparecido por completo!) Además, si llené mi propia ventana con un cuadrado semitransparente, entonces eso se comportaría como se esperaba. (Un cuadrado rojo se volvería rojo oscuro si la ventana detrás de él fuera negra, y el cuadrado se volvería de color rojo claro sobre una ventana blanca). Y puedo observar estos dos comportamientos simultáneamente en una ventana en capas. Y (2) como una bonificación altamente indeseable, el texto dibujado perdió su prueba de aciertos y se hizo inactivo? ¿Alguna explicación por ahí?
Y si ha leído hasta aquí, gracias por perdurar, y gracias por cualquier respuesta!
// 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] Problema resuelto! Código de acuerdo a Rodrogo´s excelentes sugerencias. Felicitaciones y una inmensa cantidad de gracias a él. Estoy realmente agradecido
Todas las ediciones están marcados con // # 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
THX! Suena interesante. Me temo que no puedo probar esto esta semana, pero lo haré más tarde. Y publicar comentarios. Aclamaciones. – Adam
Rodrigo, en realidad encontré un poco de tiempo para probar esto. Lo siento, no hay cigarro ...Si no tengo DC seleccionado, las operaciones de GDI + mostrarán: nada. (De hecho, me di cuenta, había intentado esto, hace mucho tiempo, como está implícito en la "pregunta lateral n. ° 1" anterior). Así que todavía tengo el problema de que la representación de GDI + va mal para algunos tamaños de fuente de Tahoma. Lo que queda por probar es un enfoque puro de GDI como se describe en algunas respuestas en: http://www.gamedev.net/topic/333453-32-bit-alpha-bitmaps-and-gdi-fun-alert/ – Adam
Curious, pero lo intenté y funcionó para mí. ¿Podría publicar el código con este método? O si lo prefieres, podría publicar el mío ;-) – rodrigo