2V0-g: port the .fnt font pipeline over a FreeType GDI emulation

CGraphicText, CGraphicFontTexture and CGraphicDib are the 40250 bodies
verbatim; CGraphicTextInstance is verbatim except that its D3D draws become
UI render commands (one image quad per glyph, Bar for cursor/underline).

- platform/Win32Gdi.cpp: the GDI subset those files call, over vendored
  FreeType 2.13.3, following GDI's LOGFONT cell height, win metrics and gasp
  rules; system font lookup with substitute and CJK fallback faces
- Win32Crt: MultiByteToWideChar / WideCharToMultiByte (UTF-8 and 1252 by
  hand, other code pages through iconv)
- CGraphicImageTexture memory textures: glyph pages reach Godot as
  "mem:<id>@<revision>"; Metin2PythonHost.memory_texture + Canvas cache
- Util.cpp code page / font face table, SetDefaultCodePage at boot,
  DefaultFont_* block, fnt resource type, GetMaxTextureWidth/Height
- TextTag, StringCodec, Arabic, Vietnamese copied from 40250
- tests: port.text (Tahoma 12 metrics, alignment, CP949), port.app_loop
  and python_host_test.gd now check glyph quads instead of text commands

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
shenlei
2026-09-23 14:17:25 +09:00
co-authored by Claude Opus 5.5
parent 141206cb69
commit afe63bfa61
272 changed files with 137097 additions and 316 deletions
+26 -5
View File
@@ -62,11 +62,28 @@ static bool py_true(const char* expression)
return truth;
}
static bool has_text(const char* text)
// CGraphicTextInstance draws one quad per glyph from a CGraphicFontTexture page ("mem:<id>@<rev>").
static int glyph_quads()
{
int count = 0;
for (const auto& command : UIRenderCommands())
count += command.kind == UIRenderCommand::Image && command.text.rfind("mem:", 0) == 0;
return count;
}
// Some glyph page holds inked pixels (A4R4G4B4 coverage in the alpha channel).
static bool glyph_page_has_ink()
{
for (const auto& command : UIRenderCommands())
if (command.kind == UIRenderCommand::Text && command.text == text)
return true;
{
UIMemoryTexture page;
if (command.kind != UIRenderCommand::Image || command.text.rfind("mem:", 0) != 0 ||
!UIRenderMemoryTexture(command.text, &page))
continue;
for (std::uint32_t argb : page.argb)
if ((argb >> 24) != 0)
return true;
}
return false;
}
@@ -153,14 +170,18 @@ int main(int argc, char** argv)
CHECK(has_board);
// PopupDialog: open with a message, render, close with ESC through the window manager's key path.
// The message is 11 glyphs of the default font (app.SetDefaultFontName, a Tahoma cell here).
const int glyphs_before = glyph_quads();
CHECK(py("_stream.popupWindow.Open('POPUP_PROBE')\nassert _stream.popupWindow.IsShow()"));
frames(1);
CHECK(has_text("POPUP_PROBE"));
CHECK(glyph_quads() >= glyphs_before + 11);
CHECK(glyph_page_has_ink());
CHECK(py("assert _stream.popupWindow.GetChild('message').GetTextSize()[0] > 0"));
PythonBoot::UIKey(DIK_ESCAPE, true);
PythonBoot::UIKey(DIK_ESCAPE, false);
frames(1);
CHECK(py("assert not _stream.popupWindow.IsShow()"));
CHECK(!has_text("POPUP_PROBE"));
CHECK(glyph_quads() == glyphs_before);
// ... and with a click on its accept button, which runs the close event. Off-centre: the button's
// caption is a 0x0 TextLine at its exact centre and CWindow::IsIn is inclusive, so in 40250 too