How Apollo PS4 Integrates libfont and TTF Support for Text Rendering
Apollo PS4 uses a lightweight 2D text engine called libfont that renders pre-packed bitmap fonts like Adonais directly from memory, while maintaining a commented-out stub for TrueType Font (TTF) support via callback-based rasterization.
The bucanero/apollo-ps4 repository implements a custom font subsystem optimized for the PS4's limited user-mode graphics API. This system prioritizes speed and binary size by using the embedded font_adonais.h bitmap for production rendering, while preserving a documented extension point for developers who require dynamic TTF glyph generation.
The libfont Architecture: Bitmap vs. TTF Rendering Paths
The font engine in source/libfont.c exposes two distinct loading APIs, though only the bitmap path is active in production builds.
Bitmap Font Pipeline
The AddFontFromBitmapArray() function (lines 35–78 in source/libfont.c) ingests raw monochrome pixel data and converts it into RSX-compatible textures. It receives:
- A pointer to the source bitmap array (e.g.,
data_font_Adonaisfromfont_adonais.h) - A pointer to RSX texture memory (
gFontTextureBase) - Character range (first and last ASCII code)
- Glyph cell dimensions (
w×h) - Pixel format flags
The function generates two texture copies—black and white—using SDL surface operations, registers them with the RSX texture manager, and stores offsets and dimensions in the global font_datas structure.
TTF Support Stub
The AddFontFromTTF() function (lines 82–124 in source/libfont.c) is currently wrapped in a comment block and excluded from compilation. Its design expects a user-provided callback with the signature:
void ttf_callback(u8 chr, u8 *bitmap, short *w, short *h, short *y_correction);
When enabled, the callback rasterizes individual glyphs on demand into 8-bit grayscale bitmaps. The stub then packs these into the RSX A4R4G4B4 format, builds the same dual-texture (black/white) setup as the bitmap path, and records the texture offsets. This ensures that DrawString() and other rendering functions require no modifications to support TTF sources.
Loading the Adonais Bitmap Font from font_adonais.h
The include/font_adonais.h header provides a production-ready monospace font containing 95 printable ASCII characters (0x20–0x7E). The data is defined as:
static const uint8_t data_font_Adonais[124*95] = { ... };
Each glyph occupies 124 bytes, representing a 32×31 pixel cell at 1 bit per pixel (padded to 32-bit alignment). To integrate this font into Apollo:
#include "libfont.h"
#include "font_adonais.h"
/* RSX-aligned texture memory allocated during graphics initialization */
extern u8 *gFontTexture;
void SetupAdonaisFont(void)
{
/* Initialize the font subsystem */
ResetFont();
/* Load the bitmap array into texture memory */
AddFontFromBitmapArray(
gFontTexture, /* destination: RSX texture memory */
data_font_Adonais, /* source: embedded bitmap data */
0x20, /* first_char: space */
0x7E, /* last_char: tilde */
32, /* cell width */
31, /* cell height */
1, /* bits per pixel */
BIT0_FIRST_PIXEL /* bit packing order */
);
/* Activate the font for rendering */
SetCurrentFont(0);
SetFontSize(32, 31);
SetFontColor(0xffffffff, 0x00000000); /* RGBA white, transparent black */
}
Because the glyphs are pre-rasterized, this approach eliminates runtime parsing overhead and minimizes memory fragmentation on the PS4.
Rendering Text with libfont
Once a font is loaded, the API provides fine-grained control over text appearance and positioning.
Setting Font Properties
Configuration functions (lines 135–165 in source/libfont.c) modify the global rendering state:
/* Select which loaded font to use (0-based index) */
SetCurrentFont(0);
/* Set the display size - can scale the 32x31 bitmap up or down */
SetFontSize(32, 31);
/* Set colors: top color (text), bottom color (background/outline) */
SetFontColor(0xffffffff, 0x00000000);
/* Alignment: LEFT, CENTER, or RIGHT relative to draw position */
SetFontAlign(FONT_ALIGN_LEFT);
/* Z-depth for layering text over other 3D elements */
SetFontZ(0.0f);
Drawing Strings
The DrawString() function (lines 197–231 in source/libfont.c) iterates through the input string, calculates texture coordinates from the font_datas array, and submits tiny3d_DrawTexture calls for each glyph:
void RenderFrame(void)
{
/* Draw left-aligned text at (100, 200) */
DrawString(100.0f, 200.0f, "Save Manager");
/* Draw centered text */
SetFontAlign(FONT_ALIGN_CENTER);
DrawString(640.0f, 400.0f, "Press X to Continue");
/* Monospaced variant for UI lists */
DrawStringMono(50.0f, 300.0f, "Slot 1: [Active]");
}
The engine automatically handles kerning for proportional fonts (using per-glyph width tables) and supports monospaced rendering via DrawStringMono().
Enabling TTF Support (Developer Reference)
While the production build relies on font_adonais.h, developers requiring dynamic font sizes or Unicode support can activate the TTF stub.
To enable TTF rendering:
- Uncomment the block in
source/libfont.c(lines 82–124) - Link FreeType (or another rasterizer) to the project
- Implement the callback matching the expected signature:
void my_ttf_callback(u8 chr, u8 *bitmap, short *w, short *h, short *y_correction)
{
/* Use FreeType to render the glyph to grayscale */
FT_Load_Char(face, chr, FT_LOAD_RENDER);
*w = face->glyph->bitmap.width;
*h = face->glyph->bitmap.rows;
*y_correction = face->glyph->bitmap_top - *h;
memcpy(bitmap, face->glyph->bitmap.buffer, (*w) * (*h));
}
When AddFontFromTTF() is called, it invokes this callback for each character in the specified range, converts the resulting 8-bit grayscale to RSX A4R4G4B4 format, and uploads the textures exactly like the bitmap path. This ensures DrawString() works identically regardless of the font source.
Summary
- Apollo PS4 uses a custom
libfontengine located insource/libfont.cto handle all text rendering without heavy external dependencies. - Bitmap fonts are the production standard, with
font_adonais.hproviding a 95-glyph, 32×31 pixel monochrome font embedded asdata_font_Adonais. - Loading workflow: Call
ResetFont(), thenAddFontFromBitmapArray()with the RSX texture pointer and font data, followed bySetCurrentFont()andSetFontSize(). - Rendering:
DrawString()andDrawStringMono()iterate glyphs stored in the globalfont_datasarray and submittiny3d_DrawTexturecalls for each character. - TTF support exists as a commented stub (
AddFontFromTTF()at lines 82–124) that accepts a rasterization callback, allowing developers to integrate FreeType or similar libraries without modifying the drawing logic.
Frequently Asked Questions
How do I change the font color in Apollo PS4?
Call SetFontColor() with two 32-bit RGBA values before drawing. The first parameter sets the text color, and the second sets the background or outline color. For opaque white text on a transparent background, use SetFontColor(0xffffffff, 0x00000000).
Why is the TTF support commented out in the source code?
The AddFontFromTTF() function is wrapped in a comment block to keep the Apollo binary small and avoid dependencies on large libraries like FreeType. The stub serves as a reference implementation for developers who need dynamic glyph generation; enabling it requires uncommenting lines 82–124 in source/libfont.c and providing a compatible rasterization callback.
What is the resolution of the Adonais font included in font_adonais.h?
The Adonais font uses 32×31 pixel glyph cells at 1 bit per pixel. The header defines 95 printable ASCII characters (0x20 through 0x7E) stored in a static array of 11,780 bytes (124 bytes per glyph × 95 glyphs).
Can I use multiple fonts simultaneously in Apollo PS4?
Yes. Call AddFontFromBitmapArray() multiple times to load different fonts into separate indices. Use SetCurrentFont(n) where n is the 0-based index to switch between them at runtime. The global font_datas array stores up to the maximum number of fonts defined in include/libfont.h.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →