Skip to content
Giovanni Bajo edited this page Jun 17, 2024 · 21 revisions

Mkfont is the tool that is used to import a font from TrueType / OpenType format and convert it into the libdragon native .font64 format. Mkfont is a sophisticated tool that performs many advanced optimizations automatically depending on the provided file. This page explains the various options and give some examples of the possible conversions.

If you want to learn how to draw text on the screen using a .font64 file crated by Mkfont, you can have a look at the documentation at the top of rdpq_text.h to get a feeling of the API. The documentation there contains also several examples.

Basic mkfont usage

In its most basic usage, just call mkfont specifying the truetype font:

$ $N64_INST/bin/mkfont Roboto-Medium.ttf

This will generate a file called Roboto-Medium.font64 in the same directory, that can be directly used with rdpq. For instance:

const int ROBOTO = 1;

rdpq_text_register_font(ROBOTO, rdpq_font_load("rom:/Roboto-Medium.font64"));

[...]

rdpq_text_printf(NULL, ROBOTO, 100, 100, "Hello, world!");

You will normally want to run this as part of your Makefile (see fontdemo's Makefile for an example), but running mkfont manually can be useful to test its features.

Tip

If you use libdragon docker, remember to use libdragon exec mkfont to run mkfont, or any other tool.

Command line options

These are the command line options supported by mkfont:

mkfont -- Convert TTF/OTF fonts into the font64 format for libdragon

Usage: ./mkfont [flags] <input files...>

Command-line flags:
   -o/--output <dir>         Specify output directory (default: .)
   -v/--verbose              Verbose output
   --no-kerning              Do not export kerning information
   --ellipsis <cp>,<reps>    Select glyph and repetitions to use for ellipsis (default: 2E,3)
   -c/--compress <level>     Compress output files (default: 1)
   -d/--debug                Dump also debug images

TTF/OTF specific flags:
   -s/--size <pt>            Point size of the font (default: whatever the font defaults to)
   -r/--range <start-stop>   Range of unicode codepoints to convert, as hex values (default: 20-7F)
                             Can be specified multiple times. Use "--range all" to extract all
                             glyphs in the font.
   --monochrome              Force monochrome output, with no aliasing (default: off)
   --outline <width>         Add outline to font, specifying its width in (fractional) pixels

It is possible to convert multiple ranges of codepoints, by specifying
--range more than one time.

--size

This is probably the most common option that you will likely need to use. It specifies the font height in pixels at which the font should be exported. Notice that, contrary to most TrueType fonts, font64 fonts are composed with bitmaps so they cannot be resized at runtime (well they could, but they'd look horrible). The bigger the size, the bigger will be the font64 file and thus the number of different textures that the font will be split upon.

For instance, the ASCII subset of Roboto at size 13 can be exported into a single 86x64 font atlas:

$ $N64_INST/bin/mkfont -v --size 13 Roboto-Medium.ttf
Converting: Roboto-Medium.ttf -> ./Roboto-Medium.font64
asc: 13 dec: -4 scalable:1 fixed:0
processing codepoint range: 0020 - 007F
aliased glyphs detected (format: 4 bpp)
created atlas 0: 86 x 64 pixels (94 glyphs)
collecting kerning information
compressed: ./Roboto-Medium.font64 (4680 -> 3459, ratio 73.9%)

But if you try to raise the size to 16, it ends up being split into two atlases:

$ $N64_INST/bin/mkfont -v --size 16 Roboto-Medium.ttf
Converting: Roboto-Medium.ttf -> ./Roboto-Medium.font64
processing codepoint range: 0020 - 007F
aliased glyphs detected (format: 4 bpp)
created atlas 0: 126 x 64 pixels (83 glyphs)
created atlas 1: 14 x 60 pixels (11 glyphs)
collecting kerning information
compressed: ./Roboto-Medium.font64 (6592 -> 4699, ratio 71.3%)

First atlas:

Second atlas:

Multiple atlases are handled just as well at runtime by minimizing texture loads and switches, but they are still more resource intensive to handle.

--size and bitmap fonts

--size is optional: if it is not specified, the font will be output to the default size decided by the font designer. In case of a modern, aliased TrueType fonts like those used on modern PCs, this size is pretty arbitrary and probably has little to do with your needs. On the other hand, especially in the retro gaming niche, there can be found many TTF fonts which are actually "bitmap" fonts, that is, they were hand-drawn into a 2D editor and then converted into the TTF format. In this case, assuming the conversion was done accurately, the default size will be the one at which the font reproduces itself correctly.

This is how a modern font like Roboto looks like in the Mac font previewer:

Screenshot 2024-06-17 alle 17 25 53

The high resolution of the glyphs show that this is a vectorial font. This is how the atlas looks like after conversion by mkfont, with a zoom:

Screenshot 2024-06-17 alle 17 27 24 Screenshot 2024-06-17 alle 17 27 57

Looking at it in the context of an emulator will give to the font a blurred look:

Screenshot 2024-06-17 alle 17 30 02

Emulators seem to somehow highlight the artifacts of aliasing at a low resolution. Things improve by turning on upscaling (eg: in Ares, turn on the "UHD Quality" option):

Screenshot 2024-06-17 alle 17 29 29

This same text looks instead perfect on a CRT: it is very sharp and there doesn't seem to be evident aliasing artifacts.

This is instead an example a bitmap font in TTF format, when displayed in the Mac preview tool:

Screenshot 2024-06-17 alle 17 36 45

It's pretty clear that this font was drawn pixel by pixel, and it is indeed a bitmap font. This is one of the atlases generated by it at its native size (that can be obtained by not specifying --size on the mkfont command line):

Screenshot 2024-06-17 alle 17 39 12

Glyphs are tightly packed but you can still tell each letter if you look closely. You can see that there is no antialiasing here, and each pixel is either fully solid or fully transparent.

Technical details on atlas generations

Internally, mkfont and rdpq_font handle 4 different types of fonts, with different atlases:

  • Aliased, no outline: in this case, atlases are generated using the I4 texture format (4 bpp). Each texel encodes up to 16 levels of transparency (coverage) that are used to reproduce the aliasing effect. As described above, the effect happens to work much better on CRTs than on emulators.
  • Aliased, outline: in this case, atlases are generated using the IA8 texture format (8 bpp). Each texel encodes up to 16 levels of transparency in the alpha channel, and up to 16 color gradients interpolating between the fill color and the outline color.
  • Monochrome, no outline: in this case, atlases are generated using a special 1 bpp packed format, since each texel requires just 1 bit to encoded. While RDP does not natively support 1 bpp, a trick is used by encoding 4 layers into a CI4 atlas and then using special palettes to isolate each layer. Fonts packed in this format are extremely tight.
  • Monochrome, outline: in this case, atlases are generated using a special 2 bpp packed format. In fact, for each texel we have 3 possible values to encode: either the texel is transparent, or it is part of the outline, or it is part of the glyph. This means that 2 bits are enough, and using a trick similar to the previous one, we can encode two layers into a CI4 atlas.

In all the above cases, fonts are encoded without specific colors, and it is possible to choose the colors at runtime.

Choosing the look on N64: --monochrome and --outline

These two options control the way the font will look like on the N64. Specifically:

  • --monochrome will force the font to be fully monochromatic (1 color without alpha). This means that there will be no aliasing performed on glyphs contours, as each pixel will be either "full" or "empty". This option is automatically selected when the provided TTF is a "bitmap" font, which is common

--range and Unicode

By default, mkfont only exports glyphs in the ASCII range. Nonetheless, rdpq_text and mkfont are fully Unicode aware: all printing functions accept UTF-8 strings, and mkfont is able to generate fonts with glyphs coming from the full Unicode range.

To export more glyphs, you must explicitly use --range, possibly multiple times, to specify range of glyphs that you want to be exported. Please refer to an Unicode range table to quickly find interesting ranges of codepoints.

For instance, if you want to also export Hiragana and Katakana glyphs to be able to write Japanese text, use:

$ $N64_INST/bin/mkfont -v --size 13 --range 20-7F --range 3000—30FF arial-unicode-ms.ttf
Converting: arial-unicode-ms.ttf -> ./arial-unicode-ms.font64
processing codepoint range: 0020 - 007F
monochrome glyphs detected (format: 1bpp)
created atlas 0: 64 x 20 pixels (94 glyphs)
processing codepoint range: 3000 - 30FF
created atlas 1: 64 x 64 pixels (151 glyphs)
created atlas 2: 112 x 21 pixels (89 glyphs)
compressed: ./arial-unicode-ms.font64 (10464 -> 6385, ratio 61.0%)

In the above example, mkfont generates one atlas for the ASCII range, and two of them for the Katakana range. mkfont will always generate disjoint atlases for different ranges. This is because normally characters from different ranges are less likely to be used in the same text (eg: how many times the same text will mix Japanese and Greek glyphs?), with the obvious exceptions of ASCII characters.

After exporting, using Unicode glyphs is as easy as referencing them in your source code:

rdpq_text_printf(NULL, FONT_ARIAL_UNICODE, 100, 100, "いつ にほんに いきますか。");

The string must be written in UTF-8 in the source code, which should be the default of any modern programming editor.

Screenshot 2024-06-17 alle 12 31 08

--no-kerning

By default, mkfont exports kerning information from the TTF font, if provided. Kerning allows font designers to specify spacing adjustments between specific couple of glyphs. The screenshots show a typical kerning adjustment, where some spacing is remove between the letters T and a, as the a glyph is small enough.

Screenshot 2024-06-16 alle 16 24 55 Screenshot 2024-06-16 alle 16 24 29

Left image is without kerning adjustments, right image is with kerning adjustment.

Kerning does increase .font64 file size and brings some very tiny runtime overhead. For instance, the Pacifico font used in these screenshots is 10568 bytes (with default libdragon compression), and its size can be reduced to 9821 bytes by disabling kerning.

Normally, it is suggested to leave kerning on, unless the performance must be kept to the bare minimum (though in that case, it might be better to choose a font that doesn't require kerning rather than ignoring the kerning that the designer thought it was needed).

--ellipsis

This option allows to configure the glyph to use as ellipsis when drawing in the WRAP_ELLIPSES word wrap mode. rdpq_text allows to specify several word wrap modes:

typedef enum {
    WRAP_NONE = 0,         ///< Truncate the text (if any)
    WRAP_ELLIPSES = 1,     ///< Truncate the text adding ellipsis (if any)
    WRAP_CHAR = 2,         ///< Wrap at character boundaries 
    WRAP_WORD = 3,         ///< Wrap at word boundaries 
} rdpq_textwrap_t;

which can be specified as text parameters:

typedef struct rdpq_textparms_s {
    int16_t width;           ///< Maximum horizontal width of the paragraph, in pixels (0 if unbounded)
    int16_t height;          ///< Maximum vertical height of the paragraph, in pixels (0 if unbounded)
    [...]
    rdpq_textwrap_t wrap;    ///< Wrap mode

When WRAP_ELLIPSES is specified, the text is truncated to the specified width, and the ellipsis character is inserted to show elision:

Screenshot 2024-06-16 alle 22 45 54

By default, three ASCII full stops (U+2E .) are inserted as ellipses, but --ellipses allows to configure this. It is possible in fact to specify a custom codepoint to use for ellipses and the number of repetitions. For instance, the use the more compact Unicode ellipsis character (U+2026 ), the syntax to use is --ellipses 2026,1.

Clone this wiki locally