Skip to content

TTF support#2232

Open
Gedo6922 wants to merge 13 commits into
Phobos-developers:developfrom
Gedo6922:my-feature
Open

TTF support#2232
Gedo6922 wants to merge 13 commits into
Phobos-developers:developfrom
Gedo6922:my-feature

Conversation

@Gedo6922

@Gedo6922 Gedo6922 commented May 30, 2026

Copy link
Copy Markdown

TTF Support

Summary

Replaces the legacy .FNT bitmap font rendering with modern TrueType/OpenType (.ttf/.otf) font support using Windows GDI. All game text is rendered through DrawTextW with proper alignment, word-wrapping, and bidirectional text support handled automatically by the operating system.
Continuation of TTF work of which the previous owner disappeared from existence in unknown circumstances
The old pr: #2178

Changes

New Files:

  • src/TextRenderer/TextRenderer.h — Header for the TTF rendering namespace
  • src/TextRenderer/TextRenderer.cpp — GDI-based font loading and text rendering engine
  • src/TextRenderer/Hooks.cpp — Four engine hooks intercepting all text drawing paths

Modified Files:

  • Phobos.vcxproj — Added new source files and header to project

Engine Hooks:

Hook Address Function Purpose
BitFont_GetTextDimension 0x433CF0 Text measurement Returns TTF text dimensions to the game
BitText_DrawText 0x434CD0 Main text rendering All UI text, tooltips, menu buttons
BitText_Print 0x434B90 Single-line text CSF messages, game notifications
BitFont_Blit 0x434120 Character rendering Chat input, typewriter effects

Key Features:

  • Uses Windows GDI DrawTextW — no external library dependencies
  • Font face and size configurable via UIMD.INI
  • Automatic RTL and Arabic-Indic numeral handling via GDI

INI Configuration:

[EnableTTF]
Enabled=true            ; Enable TTF rendering 
FontName=arial.ttf      ; Font file in game directory (default: arial.ttf)
FontSize=14             ; Font size in pixels (default: 14)
AntiAlias=true          ; ClearType smoothing 

Testing Instructions

Setup:

-Place a .ttf font file in the game directory alongside gamemd.exe
-Add the [EnableTTF] section to UIMD.INI with desired settings
-Launch the game

Known Limitations for Testers:

-CSF Message Typewriter Animation:
Symptom: Game messages like "Tech Building Captured" appear as a whole sentence vertically instead of character-by-character horizontally during the reveal animation.

-Continuous msgs ghosting in fog of war and lower bar.

-fps drop when a lot of text displayed.

Gedo6922 and others added 6 commits May 28, 2026 20:38
Integrates FreeType for font rasterization and HarfBuzz for complex text shaping, including support for right-to-left (RTL) languages like Arabic.

This new system overrides the legacy BitFont rendering for certain text elements, enabling the use of TrueType/OpenType fonts and configurable sizing via `UIMD.INI`.

External dependencies (FreeType, HarfBuzz, Fribidi, etc.) are managed through vcpkg. Project settings (`Phobos.props`) are updated to include vcpkg paths and switch to the Cdecl calling convention.
Replaces the FreeType and HarfBuzz-based text renderer with a GDI-based implementation. This change simplifies external dependencies by removing the `vcpkg` submodule and all direct references to its installed libraries in `Phobos.props`. The new system leverages Windows' native GDI for font rasterization and text layout, while retaining support for configurable fonts via `UIMD.INI`. The `MessageListClass_Draw_TTF` hook, which previously used the FreeType renderer, is also removed.
No hasArabic — GDI handles RTL automatically

No DT_RTLREADING — not needed, Windows detects text direction

No font caching — single font, loaded once

No GDIFont struct — just an HFONT

~50 lines for the core rendering logic

Color conversion simplified: (c & 0x1F) * 8 instead of * 255 / 31
@G-LimeJuice

Copy link
Copy Markdown

Stop rewrite my code!

@Metadorius

Copy link
Copy Markdown
Member

Stop rewrite my code!

That's not how it works, broski. In order to contribute your changes you've made a derivative of Phobos' GPL licensed code, modified it and published it. Your contribution is under GPL license and was distributed as a pull request. You can't just tell people to stop modifying it.

The only thing I agree with is that you should be added to credits list for it (which you should've done in the first place). @Gedo6922 please add him and yourself to CREDITS.md.

@Gedo6922

Gedo6922 commented May 31, 2026

Copy link
Copy Markdown
Author

Stop rewrite my code!

That's not how it works, broski. In order to contribute your changes you've made a derivative of Phobos' GPL licensed code, modified it and published it. Your contribution is under GPL license and was distributed as a pull request. You can't just tell people to stop modifying it.

The only thing I agree with is that you should be added to credits list for it (which you should've done in the first place). @Gedo6922 please add him and yourself to CREDITS.md.

sure thing I already mentioned that it's Continuation of his work and will be added to the credit anyway

@Gedo6922 Gedo6922 closed this May 31, 2026
@Gedo6922 Gedo6922 reopened this May 31, 2026
-Adjusted pause menu
-Useless changes removed
-Known issues:
     * Text Box for deployed MCV is smaller than the text "Power = XX Drain =XX"
     * Multiplayer and game msg chat yet to support ttf
@G-LimeJuice

Copy link
Copy Markdown

Stop rewrite my code!

That's not how it works, broski. In order to contribute your changes you've made a derivative of Phobos' GPL licensed code, modified it and published it. Your contribution is under GPL license and was distributed as a pull request. You can't just tell people to stop modifying it.

The only thing I agree with is that you should be added to credits list for it (which you should've done in the first place). @Gedo6922 please add him and yourself to CREDITS.md.

Excuse me???
I have been researching BitFont and BitText to fix Arabic text support.
I wrote the RTL related code myself but i used somthing AI DeepSeek to modify it so there issue with ordering of RTL like symbols
and then replace parts of it with harfbuzz, freetype, and fribidi to fixed it.
The code was completed about 5 months ago and i developed it without any help.

Kerb, you ruined my project that adds Arabic RTL support to the CnCNet client.
I meant you rejected my RTL implementation for the client and wanted to use HarfBuzz instead of my own code.

I also worked on TTF font rendering in-game and either through HarfBuzz or my own implementation
but it seems you want to redo stuff HarfBuzz.

Why did you change your mind about my project? Why did you turn against it?

Why are you unwilling to let me improve the code?
If there is a problem with my implementation, can you explain what it is?
I would rather discuss and improve it than replace it completely but Nothing!

@Metadorius

Copy link
Copy Markdown
Member

@G-LimeJuice We are not obliged to accept your code. We are not obliged to accept your code without edits as well. None of this is implied, written in any policies or licenses.

I haven't "ruined" anything, it's you who ragequitted upon receiving criticism from the folks. Our only goal is for the code to be optimal, producing correct results AND maintainable. Throwing a tantrum in response is childish.

Why are you unwilling to let me improve the code?
If there is a problem with my implementation, can you explain what it is?
I would rather discuss and improve it than replace it completely but Nothing!

This is bullshit, it's you who quit without any explanation and closed your PR(s) upon receiving criticism, and now you're saying this. Maybe don't lie?

If you don't want to understand and accept how civilized software development is done, then don't make a headache for everyone else. You are free to collaborate with us, but this isn't collaboration.

@github-actions

github-actions Bot commented Jun 1, 2026

Copy link
Copy Markdown

Nightly build for this pull request:

This comment is automatic and is meant to allow guests to get latest nightly builds for this pull request without registering. It is updated on every successful build.

- Optimize `DrawText` by reusing the DIB bitmap for reduced allocation overhead.
- Extend GDI rendering to `BitText_Print` and `BitFont_Blit` via new hooks.
- Improve text dimension calculation and single-character drawing logic.
- Remove `vcpkg` from `.gitignore` as it's no longer a dependency for the text renderer.
- Refactor internal implementation for improved clarity, including variable naming and detailed comments.
- Correct font creation by switching from `CreateFontW` to `CreateFontA` for reliable font name parsing from `UIMD.INI`.
- Improve drawing process with explicit DIB background copying and 16-bit word alignment for stability.
- Refine surface locking coordinates and drawing boundary calculations.
@coderabbitai

coderabbitai Bot commented Jun 3, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

This PR introduces a new TextRenderer module that provides Win32 GDI-based TrueType font rendering for the Phobos mod. The implementation includes lazy font initialization from configuration, text drawing with DIB backbuffer composition, text measurement, and four binary hooks that integrate the renderer into the game engine's existing font and text operations.

Changes

TextRenderer Module & Engine Integration

Layer / File(s) Summary
TextRenderer public API
src/TextRenderer/TextRenderer.h
Declares three namespace functions: IsInitialized(), DrawText() to render text with alignment and bounding box, and GetTextDimension() to measure text dimensions. Includes forward declarations for BitFont and Surface to minimize dependencies.
GDI state and lazy font loading
src/TextRenderer/TextRenderer.cpp
Global cached Win32 GDI resources (font, device context, RGB565 DIB backbuffer); IsInitialized() reports readiness; LoadFontOnce() lazily loads TrueType configuration from GameStrings::UIMD_INI and creates font/DC handles once.
Text rendering and measurement
src/TextRenderer/TextRenderer.cpp
DrawText() locks the target surface, manages/reallocates the DIB backbuffer, composites the destination region, applies text color and layout flags based on alignment, draws via GDI DrawTextW, and copies pixels back. GetTextDimension() measures strings using DrawTextW with DT_CALCRECT and word-break layout.
Binary hooks for game engine integration
src/TextRenderer/Hooks.cpp
Four hooks patch game addresses: BitFont_GetTextDimension (0x433CF0) measures text and redirects to 0x433EA2; BitText_DrawText (0x434CD0) draws and returns 0x435310; BitText_Print (0x434B90) draws with fixed width/alignment and returns 0x434BDE; BitFont_Blit (0x434120) draws single characters, measures width, and returns adjusted X coordinate via 0x434155.
Visual Studio project configuration
Phobos.vcxproj
Adds src\TextRenderer\TextRenderer.cpp, src\TextRenderer\TextRenderer.h, and src\TextRenderer\Hooks.cpp to the build item lists.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐰 A rabbit with brush paints fonts so fine,
GDI magic makes the text align,
Hooks rewire the game's old way,
TrueType beauty shines today! 🎨✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'TTF support' accurately summarizes the main change—adding TrueType font rendering capability via a new TextRenderer module with GDI-based implementation and hooks.
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The pull request description is comprehensive and well-structured, including summary, changes, features, configuration, testing instructions, and known limitations.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@Phobos.vcxproj`:
- Line 33: Move the TextRenderer.h entry out of the compiled-files ItemGroup and
into the header-files ItemGroup in Phobos.vcxproj: locate the line `<ClInclude
Include="src\TextRenderer\TextRenderer.h" />` currently placed with
`<ClCompile>` entries and cut/paste it under the `<ItemGroup>` that contains
other `<ClInclude>` entries so header files are grouped with header ItemGroup,
preserving XML ordering and project formatting.

In `@src/TextRenderer/Hooks.cpp`:
- Around line 46-53: In BitText_Print (Hooks.cpp) the caller's width (W,
populated by GET_STACK) is ignored and a hardcoded 2000 is passed to
TextRenderer::DrawText; update the TextRenderer::DrawText invocation to pass W
instead of 2000 so wrapping/clipping honors the original narrow print region
(ensure the argument order remains pFont, pSurface, pWideString, X, Y, W, H, 0).
- Around line 49-50: The guard using TextRenderer::IsInitialized() must not
short-circuit lazy initialization; instead either make IsInitialized() perform
the lazy init (call LoadFontOnce()) or remove the early return and call DrawText
(or GetTextDimension where appropriate) directly so the renderer initializes on
first use; update the BitText_Print and BitFont_Blit hook sites to invoke
DrawText/GetTextDimension (or call LoadFontOnce() before checking
IsInitialized()) so the TTF renderer switches on the first call rather than
being permanently bypassed.
- Around line 68-74: The code mutates the shared font color (pFont->Color) when
nColor != -1 and never restores it, which taints later draws; fix by saving the
original color from pFont before changing it, set pFont->Color to the new color
only for the duration of the draw (TextRenderer::DrawText / any subsequent
BitFont_Blit calls), and always restore the saved original color after the draw
completes (including on all execution paths). Locate the change around
pFont->Color, the DrawText call in TextRenderer::DrawText and any BitFont_Blit
usage, and ensure restoration happens even if GetTextDimension or DrawText
returns early.

In `@src/TextRenderer/TextRenderer.cpp`:
- Around line 67-77: The code currently computes drawingWidth/drawingHeight from
raw posX/posY but only clamps the lock origin (lockX/lockY), causing negative
posX/posY to leave unclipped left/top content and oversized temp bitmaps; before
creating the temporary HBITMAP and before calling DrawTextW, adjust the visible
box by subtracting the clamped offset (clampedX = max(0,posX), clampedY =
max(0,posY)), reduce drawingWidth/drawingHeight by (clampedX - posX) and
(clampedY - posY) respectively so the temp bitmap only covers visible pixels,
and shift the GDI RECT/DrawTextW origin by (posX - clampedX, posY - clampedY)
(i.e. offset the drawing origin into the temp bitmap) so DrawTextW writes into
the correct location while Lock still uses lockX/lockY.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b55e1deb-ae4d-45e8-a7ef-da25733e467a

📥 Commits

Reviewing files that changed from the base of the PR and between fc79aa7 and f0b78a8.

📒 Files selected for processing (4)
  • Phobos.vcxproj
  • src/TextRenderer/Hooks.cpp
  • src/TextRenderer/TextRenderer.cpp
  • src/TextRenderer/TextRenderer.h

Comment thread Phobos.vcxproj Outdated
Comment thread src/TextRenderer/Hooks.cpp Outdated
Comment thread src/TextRenderer/Hooks.cpp Outdated
Comment thread src/TextRenderer/Hooks.cpp Outdated
Comment thread src/TextRenderer/TextRenderer.cpp Outdated

@TaranDahl TaranDahl left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see any issues, but I do not understand the font-related things.

@SadPencil

Copy link
Copy Markdown

@Gedo6922 Please explicit address each AI review comment. Meanwhile, please describe how to use TTF font in this PR and what known limitation a tester needs to know

@Gedo6922

Gedo6922 commented Jun 4, 2026

Copy link
Copy Markdown
Author

update the TextRenderer::DrawText invocation to pass W
instead of 2000 so wrapping/clipping honors the original narrow print region

It has to remain 2k or wrapping and clipping happens which we dont need for ingame chat and msgs

Gedo6922 added 2 commits June 4, 2026 20:25
- Add `AntiAlias` setting to enable/disable ClearType font smoothing.
- Consolidate all TrueType font configuration under the `[EnableTTF]` INI section.
- Ensure `BitFont_Blit` preserves the font's original color after drawing.
- Improve drawing boundaries and coordinate clamping for more accurate text rendering.
- Streamline code by removing redundant `IsInitialized` checks and excessive comments.
The original game code sometimes supplies a narrow width to `BitText::Print`, which is intended for single-line text. This causes GDI to word-wrap text, such as CSF messages, leading to incorrect display. This change overrides the width with a large fixed value to ensure single-line rendering as intended.
@TaranDahl TaranDahl added the ⚙️T2 T2 maintainer review is sufficient label Jun 5, 2026
- Make `BitFont_Blit` hook measure-only, delegating actual character drawing
  to the main `DrawText` function for consistency and simplified logic.
- Enhance `TextRenderer` robustness by introducing a `MAX_DRAWING_WIDTH`
  safety cap and ensuring all GDI drawing commands are flushed.
- Improve code readability and maintainability with extensive comments

@SadPencil SadPencil left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, we must find a way to test the performance, especially check whether there exist memory leaks or not

- Introduce `MAX_TEXT_WIDTH` for consistent width capping in `DrawText` and
  forcing single-line output for `BitText::Print` callers.
- Add comprehensive documentation to `Hooks.cpp` and `TextRenderer.cpp/.h`
  explaining component roles and drawing logic.
- Explicitly define `BitFont::Blit` as a measure-only hook, streamlining
  character width measurement for cursor positioning.
- Refactor internal variable names and remove obsolete includes for
  better maintainability.
@Gedo6922

Gedo6922 commented Jun 6, 2026

Copy link
Copy Markdown
Author

Also, we must find a way to test the performance, especially check whether there exist memory leaks or not

did more tests with debugger by tracking the GDI object count and DIB allocations.
[Phobos] [TTF] Diag: Draws=791433, DIB creates=775768, DIB deletes=775767, GDI handles=3, Surface locks=0
these r the results of one of the many games we done which didnt have any memory leaks or sudden crashes
I can upload the logs for all players I can upload the code with the debugger if u wanna check em out

but to note the issues I already listed

1- CSF Message Typewriter Animation vertical instead of horizontal
2- Continuous msgs ghosting in fog of war and lower bar.
3- fps drop when a lot of text displayed. can be recreated by just spamming T or any selection command.

1st idk about but it has 0 impact on performance I think
2nd imma investigate more since I didnt have this problem before so I need to do some backtracking
3rd is most cruical which I need help with the most

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚙️T2 T2 maintainer review is sufficient

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants