Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Truetype support #202

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open

Conversation

sonilyan
Copy link
Contributor

@sonilyan sonilyan commented Nov 11, 2022

Hello, I added truetype font support for Fallout 2, which makes it easy to support multiple languages.
Support to adjust the font size and replace different ttf fonts.

Use iconv to convert the encoding, and then read the glyphs through the freetype library.

Windows, android, ios, macos have been tested, but linux platform has not been tried.

example

@sonilyan
Copy link
Contributor Author

Create a fonts folder in the root directory, then add the corresponding folders according to the language, such as chs, english, and then create an ini file with the following content:

[font0]
maxHeight=10
maxWidth=10
lineSpacing=0
wordSpacing=4
letterSpacing=1
heightOffset=0
fileName=r_fallouty.ttf
warpMode=0
encoding=GBK

[font1]
maxHeight=12
maxWidth=12
lineSpacing=0
wordSpacing=4
letterSpacing=1
heightOffset=0
fileName=r_fallouty.ttf
warpMode=0
encoding=GBK

[font2]...etc

The encoding value refers to the support for encoding in libiconv
https://www.gnu.org/software/libiconv/

Then put the corresponding ttf font file in the directory.

If there is an error in this process, the font is displayed in the old way.

@JanSimek
Copy link
Contributor

Tested on Arch linux and it works great. Amazing job!

Copy link

@klei1984 klei1984 left a comment

Choose a reason for hiding this comment

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

Added a few recommendations on how could the implementation be improved.

{
if (input[0] == '\x95') {
size_t output_size = 1024;
iconv_t cd = iconv_open("UCS-4-INTERNAL", current->encoding);

Choose a reason for hiding this comment

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

UCS-4-INTERNAL is not available on various systems, e.g. Ubuntu 20.04. UCS-4-INTERNAL could either be UCS-4LE or UCS-4BE. Is it guaranteed that UCS-4BE encoded unicode charcodes are understood by FT_Get_Char_Index()?

char* tmp = (char*)(output + 1);
const char* tmp2 = (const char *)(input + 1);
charInPutLen -= 1;
iconv(cd, &tmp2, &charInPutLen, &tmp, &output_size);

Choose a reason for hiding this comment

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

What happens if cd is -1 (iconv_open failed).


static int LtoU(const char* input, size_t charInPutLen)
{
if (input[0] == '\x95') {

Choose a reason for hiding this comment

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

Opening and closing iconv cd at each function call is slow. It would be much better if the iconv object would be opened at the time when the font is loaded and closed when the font manager deletes the font. The cd could be stored in the font specific descriptor.

iconv can reset the conversion state by passing it nul parameters so there is really no need to open and close the converter at each call.

You can also create a dedicated LtoU and another UtoL instance.

current->map[unicode].rows = knobHeight;
current->map[unicode].buffer = knobDump;
} else {
FT_Load_Glyph(current->face, FT_Get_Char_Index(current->face, unicode), FT_LOAD_DEFAULT | FT_LOAD_NO_BITMAP);

Choose a reason for hiding this comment

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

What happens if FT_Get_Char_Index returns undefined character code. What happens if the face does not load the glyph and FT_Load_Glyph returns an error in which case it might be nondeterministic what would be rendered by FT_Render_Glyph?

// 0x441CEC
void FtFontsExit()
{
for (int font = 0; font < FT_FONT_MAX; font++) {

Choose a reason for hiding this comment

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

If I am not mistaken, you never called FT_Done_Face. Resources of all loaded fonts are possibly leaked. Ah ok, I just realized the todo below.

// 0x441D20
static int FtFontLoad(int font_index)
{
char string[56];

Choose a reason for hiding this comment

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

In proper ttf fonts the horizontal bearings and the advance are all encoded in the glyph metrics. These manually set up, resolution specific settings from the ini files can all be calculated for the requested em size on the fly. Both maxWidth and maxHeight could be iteratively found for a particular em size without rendering using FT_Get_First_Char and FT_Get_Next_Char in a while loop at font load time.

ptr += 10000;
}

if (fileRead(ptr, 1, readleft, stream) != readleft) {

Choose a reason for hiding this comment

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

If this fails, what happens with desc->filebuffer ? Is it cleaned up by the caller?

}

FT_Init_FreeType(&(desc->library));
FT_New_Memory_Face(desc->library, desc->filebuffer, fileSize, 0, &desc->face);

Choose a reason for hiding this comment

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

What happens if these APIs return non 0, any error?

}

unsigned char* palette = _getColorBlendTable(color & 0xFF);
int count = LtoU((char*)string, strlen(string));

Choose a reason for hiding this comment

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

If there is outline (FONT_SHADOW) than LtoU is prcessing entire strings twice.

GNW based GUI implementations often center align vertically or horizontally in which case the current implementation will call LtoU or UtoL for the exact same string several times.

It would make sense to cache the generated strings and free them up when they become stale in the cache. This could increase overall performance a lot.

FtFontDrawImpl(buf + pitch + 1, string, length, pitch, (color & ~0xFF) | _colorTable[0]);
}

unsigned char* palette = _getColorBlendTable(color & 0xFF);

Choose a reason for hiding this comment

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

Building these blend tables are expensive. The GNW implementation I seen uses a reference counted lookup table and free up a particular blend table when the ref count reaches 0. In this context a blend table is built for (color & ~0xFF) | _colorTable[0]) and another for color & 0xFF. As there are but a few text colors, like green, golden, etc. it would make sense to simply generate these blend tables for the most common text colors in the init phase and add reference counters to them. On exiting the font manager, _freeColorBlendTable could be called as many times as the text manager reference count value.

@klei1984
Copy link

  • The iconv library uses LGPL-2.1 license. Statically linking it requires fallout2-ce to provide instructions on how to relink another version of the library to the application. For the LGPL requirements to be fulfilled it is recommended to add the modified iconv library version (source code) to the fallout2-ce project before this pull request.

  • The freetype library uses its own FTL license. It is recommended to add the following disclaimer to the fallout2-ce readme:

    Portions of this software are copyright © <year> The FreeType Project (www.freetype.org). All rights reserved.
    
  • The licenses for the 5 ttf fonts needs to be confirmed and their copyright holders mentioned in a manifest or similar.

@sonilyan
Copy link
Contributor Author

@klei1984
Thank you for your feedback. This part of the work only shows a framework and possibilities. I hope that if @alexbatalov decides to adopt this approach, he can open a branch and then do further work on it. I’m very busy at the moment. I hope that if this part of the code can enter the main branch, then more people can help improve this part of the code.

@y0lo
Copy link
Contributor

y0lo commented Jun 2, 2023

Nice to see this.
I tried different approach. I did simple python script and used FreeType to covert ttf to aaf.
I even tried to resize original glyphs in runtime on load.

However I disappointed in this approach the size of glyphs is too small. Any custom font looks awful. And many menus elements doesn't support custom size of glyphs. For exmple in dialogs you cannot scroll your answers list.

@klei1984
Copy link

klei1984 commented Jun 2, 2023

I integrated the given solution into GNW in the M.A.X. Port project trying to resolve all code review findings that are noted here.

M.A.X. had a C++ wrapper on top of GNW for its basic UI elements like windows, edit controls, buttons, text boxes. The C++ layer manages various resources, including audio samples, but it does not handle automatic label formatting and resizing. Truncation of non fitting text is supported.

The implementation of the TTF GNW font manager in M.A.X. Port can be found here.

Please note that M.A.X. does not use monochromatic AAF fonts, so GNW Color's blending tables are not supported in the given implementation. Based on the review comments it can be integrated with relative ease.

@lpgneg19
Copy link

lpgneg19 commented Dec 6, 2023

Will this project still be updated?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants