Skip to content

Conversation

@longtran2904
Copy link

@longtran2904 longtran2904 commented Aug 25, 2025

The first issue is DPI scaling. When requesting a font size:

u32 pt_size = (u32)(pt_size_unscaled*scale_factor);
...
FT_Size_RequestRec_ size = {};
size.type   = FT_SIZE_REQUEST_TYPE_NOMINAL;
size.height = (pt_size << 6);
FT_Request_Size(ft_face, &size);

4coder does not set size.horiResolution or size.vertResolution, so FreeType falls back to its default. In recent versions, this default is 96 DPI, which is what we want. However, 4coder uses an older FreeType version where the default is 72 DPI, leading to incorrect sizing calculations.

[Edit: While pulling this out into a standalone article, I noticed that this part is slightly incorrect. The default value is always 72 dpi, but scale_factor is defined relative to 96 dpi (so 1.0 equals 96 dpi). That’s why the calculation comes out wrong. The correct approach is to stop premultiplying the size with scale_factor and instead pass the actual dpi directly.]

The second issue is the codepoint lookup table. The way the code iterates through all the glyphs is incorrect. It should stop when index is zero, not when counter == glyph_count, glyph_count is typically way larger than the number of mappable glyphs anyway. This also means that the starting size for the table is too much: glyph_count * 2 is enough. Finally, the check for the zeroth glyph is also incomplete: FT_Get_First_Char could return the null codepoint, bypassing the check.

The third issue is library initialization. The code never checks whether FT_Init_FreeType succeeds, and it always calls FT_Done_FreeType unconditionally, even if initialization failed.

The fourth issue is inefficient glyph storage. Glyph data (bounds and advance) is packed in a large array covering every codepoint up to the font’s maximum. Most of these entries are unused, since many codepoints don’t have an associated glyph. I replace this with a system that only stores glyph data for codepoints that actually exist.

I also made some minor fixes, like checking for pixels greater than 128 (instead of just non-zero) in 1-bit monochrome, ignoring the unused white glyph, and resetting the arena whenever an error occurs. This commit also includes my fix for issue #35.

Edit: Forgot to mention that I also simplified the texture packing code into a single function. I could go further and inline that function too, but I prefer to keep it separate and more general, to signal that this is an additional (and optional) step, and allow other code to reuse it in the future.

The first issue is DPI scaling. When requesting a font size, 4coder does not provide horiResolution or vertResolution, causing FreeType to fall back to a default. In recent versions, this default is 96 DPI, which is what we want. However, 4coder uses an older FreeType version where the default is 72 DPI, leading to incorrect sizing calculations.

The second issue is the codepoint lookup table. The way the code iterates through all the glyphs is incorrect, the starting size for the table is incorrect, and the way the code checks for the zeroth glyph is also incorrect.

The third issue is library initialization. The code never checks whether FT_Init_FreeType succeeds, and it always calls FT_Done_FreeType unconditionally, even if initialization failed.

The fourth issue is how glyph data (bounds and advance) is stored. 4coder places it in a large array spanning from codepoint zero to the font’s maximum codepoint. Since many codepoints have no associated glyph, most of the entries go unused. My fix replaces this with a system that stores glyph data only for codepoints that actually exist.

I also made some minor fixes like checking for pixels greater than 128 (instead of just non-zero) in 1-bit monochrome, removing the unused white glyph, and resetting the arena whenever an error occurs.
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.

1 participant