The Glyph_Cache module is designed to greatly simplify text rendering, both in games and in regular applications. It is intended to free you from thinking about rasterizing glyphs from any font, and any size or style. As long as they are supported by FreeType 2 (FT2).
No more pre-baking unicode ranges to Bitmap fonts, and then realizing you need a 32px version for titles too so you now have two fonts. But then you realize that you have some users that use non-latin alphabets. So, what do you do, you have to go extend the baked-in unicode ranges. Obviously then you go release an update to the game, but it turns out it’s now huge because the font atlas for the 32px version of the font is so big it’s pulling you towards the monitor. You get the point.
Anyways, using Glyph_Cache you will no longer have that problem. When you request a glyph, you will get some metrics back alongside coordinates and size of the texture region where the glyph was rasterized. You don’t have to use the glyph metrics however — in fact I suggest you not to — since this module is designed to integrate well with Harfbuzz (here you can find JAI bindings to Harfbuzz). Once all the glyphs necessary for the current frame are requested, you can query the pixel buffer and use it to render.
Use this module if you need to render text in interactive applications. Especially in instances where you will need a big and/or unpredictable range of glyphs, potentially across multiple fonts.
Note:
There are some restrictions based on what FT2 is capable of rasterizing as of January 2025. Which are: OpenType-SVG and COLR/CPAL fonts, among probably many more I don’t even know about. In an ideal future I will be adding support for these using an external svg renderer plugged into FreeType2. But I currently have no need for it, so we’ll see.
How to use:
Glyph_Cache.init_font_cache(width = 512, height = 512);
defer Glyph_Cache.deinit_font_cache();
init_font_cache()
needs to be called once, before you start requesting glyphs. deinit_font_cache()
frees the buffer for the cache bitmap and other internal resources.
Once it’s initialized you can add fonts in the following ways, based on if you want to load it from a file or from memory.
success_loading_from_file: bool =
Glyph_Cache.add_font_from_file("Karla-Regular.ttf");
buffer : []u8;
success_loading_from_memory : bool =
Glyph_Cache.add_font_from_memory(buffer);
After the font faces have been loaded you can query which one, among the currently loaded ones, is the best fit for the style you want. Note that this might not be the best way of doing this so any suggestions are very welcome. Feel free to submit pull requests.
font_face := Glyph_Cache.find_best_face_for_style(.{
family = "Karla Regular",
style = .NORMAL,
weight = .NORMAL,
});
You can then bind a face and request glyphs either by codepoint or by index.
Glyph_Cache.bind_face(font_face, pixels_per_em);
glyph, found := Glyph_Cache.get_glyph_for_codepoint(codepoint);
glyph_index := 1;
glyph, found := Glyph_Cache.get_glyph(glyph_index);
These are the useful members of the Cached_Glyph
struct:
x, y: int; // pixels
w, h: int; // pixels
bearing_x, bearing_y : int; // pixels (right handed)
advance_x, advance_y : int; // pixels (right handed)
Note that all members of this struct have already been scaled so that you only need to simply use the values without major headaches. (Except the handedness.)
It is crucial that you update the texture before you render your glyphs, you will have to draw text in two passes (see the examples). Simp does the same thing, you prepare the text, then you render the prepared glyphs.
dirty, bitmap := Glyph_Cache.peek_cache_bitmap(evict_after = 8);
if dirty
{
// ... upload bitmap to GPU
}
Here evict_after=8
means that all glyphs that haven’t been used in the last 7 frames will be evicted from the cache.