Fix unicode support for win9x
This commit is contained in:
parent
528356e9e4
commit
dd4aa6b163
1 changed files with 128 additions and 26 deletions
|
|
@ -1,6 +1,15 @@
|
|||
// gdi_font.cpp — TrueType font rendering via GDI, blitted into Allegro
|
||||
// BITMAPs. This file includes Allegro AND Windows headers; winalleg.h
|
||||
// resolves the BITMAP typedef conflict.
|
||||
//
|
||||
// Unicode strategy (MSLU / "unicows" pattern):
|
||||
// All text input is UTF-8. We always convert to UTF-16 internally.
|
||||
// At runtime we detect whether the OS supports W (wide) Win32 APIs:
|
||||
// - NT-based (2000+): call W functions directly → full Unicode.
|
||||
// - Win9x (95/98/Me): convert UTF-16 → current codepage via
|
||||
// WideCharToMultiByte, then call A functions → best-effort rendering.
|
||||
// This is the same approach Microsoft's unicows.dll takes, implemented
|
||||
// inline for the small set of GDI functions we actually use.
|
||||
|
||||
#include <allegro.h>
|
||||
#ifdef ALLEGRO_WINDOWS
|
||||
|
|
@ -19,8 +28,18 @@ namespace
|
|||
HFONT g_hfont = NULL;
|
||||
HDC g_measure_dc = NULL; // off-screen DC kept for measuring only
|
||||
int g_font_height = 0;
|
||||
bool g_wide_ok = false; // true on NT-based systems (W APIs work)
|
||||
|
||||
// Convert a UTF-8 string to UTF-16 for wide Win32 APIs.
|
||||
// Detect whether the OS supports W (wide/Unicode) Win32 APIs.
|
||||
void detect_unicode_support()
|
||||
{
|
||||
OSVERSIONINFOA ovi;
|
||||
ovi.dwOSVersionInfoSize = sizeof(ovi);
|
||||
GetVersionExA(&ovi);
|
||||
g_wide_ok = (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT);
|
||||
}
|
||||
|
||||
// Convert UTF-8 to UTF-16. Works on all Win32 platforms.
|
||||
std::wstring utf8_to_wide(const char *s, int len = -1)
|
||||
{
|
||||
if (!s || (len == 0) || (len == -1 && !*s))
|
||||
|
|
@ -30,27 +49,59 @@ namespace
|
|||
return std::wstring();
|
||||
std::wstring w(wlen, L'\0');
|
||||
MultiByteToWideChar(CP_UTF8, 0, s, len, &w[0], wlen);
|
||||
// If len was -1, MultiByteToWideChar includes the null terminator in wlen.
|
||||
if (len == -1 && !w.empty() && w.back() == L'\0')
|
||||
w.pop_back();
|
||||
return w;
|
||||
}
|
||||
|
||||
// Convert UTF-16 to the current ANSI codepage (for Win9x A-function fallback).
|
||||
std::string wide_to_ansi(const std::wstring &w)
|
||||
{
|
||||
if (w.empty())
|
||||
return std::string();
|
||||
int alen = WideCharToMultiByte(CP_ACP, 0, w.c_str(), (int)w.size(),
|
||||
NULL, 0, NULL, NULL);
|
||||
if (alen <= 0)
|
||||
return std::string();
|
||||
std::string a(alen, '\0');
|
||||
WideCharToMultiByte(CP_ACP, 0, w.c_str(), (int)w.size(),
|
||||
&a[0], alen, NULL, NULL);
|
||||
return a;
|
||||
}
|
||||
|
||||
// Try to create the font with the given face name.
|
||||
HFONT try_create(const char *face, int pt)
|
||||
{
|
||||
std::wstring wface = utf8_to_wide(face);
|
||||
return CreateFontW(
|
||||
-pt, // negative = point size (not cell height)
|
||||
0, 0, 0,
|
||||
FW_NORMAL, // weight
|
||||
FALSE, FALSE, FALSE, // italic, underline, strikeout
|
||||
DEFAULT_CHARSET,
|
||||
OUT_TT_PRECIS, // prefer TrueType
|
||||
CLIP_DEFAULT_PRECIS,
|
||||
CLEARTYPE_QUALITY,
|
||||
DEFAULT_PITCH | FF_SWISS,
|
||||
wface.c_str());
|
||||
// Win9x doesn't have ClearType.
|
||||
DWORD quality = g_wide_ok ? CLEARTYPE_QUALITY : ANTIALIASED_QUALITY;
|
||||
|
||||
if (g_wide_ok)
|
||||
{
|
||||
std::wstring wface = utf8_to_wide(face);
|
||||
return CreateFontW(
|
||||
-pt, 0, 0, 0,
|
||||
FW_NORMAL,
|
||||
FALSE, FALSE, FALSE,
|
||||
DEFAULT_CHARSET,
|
||||
OUT_TT_PRECIS,
|
||||
CLIP_DEFAULT_PRECIS,
|
||||
quality,
|
||||
DEFAULT_PITCH | FF_SWISS,
|
||||
wface.c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
return CreateFontA(
|
||||
-pt, 0, 0, 0,
|
||||
FW_NORMAL,
|
||||
FALSE, FALSE, FALSE,
|
||||
DEFAULT_CHARSET,
|
||||
OUT_TT_PRECIS,
|
||||
CLIP_DEFAULT_PRECIS,
|
||||
quality,
|
||||
DEFAULT_PITCH | FF_SWISS,
|
||||
face);
|
||||
}
|
||||
}
|
||||
|
||||
// Verify the returned font actually matches the requested face name
|
||||
|
|
@ -59,17 +110,60 @@ namespace
|
|||
{
|
||||
HDC dc = GetDC(NULL);
|
||||
HFONT old = (HFONT)SelectObject(dc, hf);
|
||||
wchar_t actual[LF_FACESIZE] = {};
|
||||
GetTextFaceW(dc, LF_FACESIZE, actual);
|
||||
|
||||
bool match = false;
|
||||
if (g_wide_ok)
|
||||
{
|
||||
wchar_t actual[LF_FACESIZE] = {};
|
||||
GetTextFaceW(dc, LF_FACESIZE, actual);
|
||||
std::wstring wexpected = utf8_to_wide(expected);
|
||||
match = (_wcsicmp(actual, wexpected.c_str()) == 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
char actual[LF_FACESIZE] = {};
|
||||
GetTextFaceA(dc, LF_FACESIZE, actual);
|
||||
match = (_stricmp(actual, expected) == 0);
|
||||
}
|
||||
|
||||
SelectObject(dc, old);
|
||||
ReleaseDC(NULL, dc);
|
||||
std::wstring wexpected = utf8_to_wide(expected);
|
||||
return _wcsicmp(actual, wexpected.c_str()) == 0;
|
||||
return match;
|
||||
}
|
||||
|
||||
// Measure text width using the appropriate API.
|
||||
void measure_text(HDC dc, const std::wstring &wtext, SIZE *sz)
|
||||
{
|
||||
if (g_wide_ok)
|
||||
{
|
||||
GetTextExtentPoint32W(dc, wtext.c_str(), (int)wtext.size(), sz);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string ansi = wide_to_ansi(wtext);
|
||||
GetTextExtentPoint32A(dc, ansi.c_str(), (int)ansi.size(), sz);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw text into a DC using the appropriate API.
|
||||
void draw_to_dc(HDC dc, const std::wstring &wtext, RECT *rc, UINT fmt)
|
||||
{
|
||||
if (g_wide_ok)
|
||||
{
|
||||
DrawTextW(dc, wtext.c_str(), (int)wtext.size(), rc, fmt);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string ansi = wide_to_ansi(wtext);
|
||||
DrawTextA(dc, ansi.c_str(), (int)ansi.size(), rc, fmt);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void gdi_font_init(int point_size)
|
||||
{
|
||||
detect_unicode_support();
|
||||
|
||||
// Try Inter first, then Segoe UI, then Arial.
|
||||
static const char *faces[] = {"Inter", "Segoe UI", "Arial"};
|
||||
for (int i = 0; i < 3; ++i)
|
||||
|
|
@ -92,9 +186,18 @@ void gdi_font_init(int point_size)
|
|||
g_measure_dc = CreateCompatibleDC(NULL);
|
||||
SelectObject(g_measure_dc, g_hfont);
|
||||
|
||||
TEXTMETRICW tm;
|
||||
GetTextMetricsW(g_measure_dc, &tm);
|
||||
g_font_height = tm.tmHeight;
|
||||
if (g_wide_ok)
|
||||
{
|
||||
TEXTMETRICW tm;
|
||||
GetTextMetricsW(g_measure_dc, &tm);
|
||||
g_font_height = tm.tmHeight;
|
||||
}
|
||||
else
|
||||
{
|
||||
TEXTMETRICA tm;
|
||||
GetTextMetricsA(g_measure_dc, &tm);
|
||||
g_font_height = tm.tmHeight;
|
||||
}
|
||||
}
|
||||
|
||||
void gdi_font_shutdown()
|
||||
|
|
@ -122,7 +225,7 @@ int gdi_font_text_width(const char *text)
|
|||
return 0;
|
||||
std::wstring w = utf8_to_wide(text);
|
||||
SIZE sz;
|
||||
GetTextExtentPoint32W(g_measure_dc, w.c_str(), (int)w.size(), &sz);
|
||||
measure_text(g_measure_dc, w, &sz);
|
||||
return sz.cx;
|
||||
}
|
||||
|
||||
|
|
@ -132,7 +235,7 @@ int gdi_font_text_width(const char *text)
|
|||
// Strategy:
|
||||
// 1. Create a small DIB section just big enough for the text.
|
||||
// 2. Fill it with black (so GDI anti-aliasing blends against black).
|
||||
// 3. Use DrawTextA to render white text onto the DIB.
|
||||
// 3. Render white text onto the DIB.
|
||||
// 4. Read pixels back — the white channel gives us coverage (alpha).
|
||||
// 5. Blend each pixel into the Allegro BITMAP at the requested color.
|
||||
// -----------------------------------------------------------------------
|
||||
|
|
@ -143,10 +246,9 @@ static void render(BITMAP *dst, int x, int y, int allegro_color, const char *tex
|
|||
return;
|
||||
|
||||
std::wstring wtext = utf8_to_wide(text);
|
||||
int wlen = (int)wtext.size();
|
||||
|
||||
SIZE sz;
|
||||
GetTextExtentPoint32W(g_measure_dc, wtext.c_str(), wlen, &sz);
|
||||
measure_text(g_measure_dc, wtext, &sz);
|
||||
int tw = sz.cx;
|
||||
int th = sz.cy;
|
||||
if (tw <= 0 || th <= 0)
|
||||
|
|
@ -181,7 +283,7 @@ static void render(BITMAP *dst, int x, int y, int allegro_color, const char *tex
|
|||
SetTextColor(dc, (COLORREF)0x00FFFFFF); // white
|
||||
SetBkMode(dc, TRANSPARENT);
|
||||
RECT rc = {0, 0, tw, th};
|
||||
DrawTextW(dc, wtext.c_str(), wlen, &rc, DT_LEFT | DT_TOP | DT_NOPREFIX | DT_SINGLELINE);
|
||||
draw_to_dc(dc, wtext, &rc, DT_LEFT | DT_TOP | DT_NOPREFIX | DT_SINGLELINE);
|
||||
|
||||
GdiFlush();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue