Embedding Fonts (BDF)
stdgba embeds bitmap fonts at compile time from BDF files through gba::embed::bdf in <gba/embed>.
BDF format reference: Glyph Bitmap Distribution Format (Wikipedia).
This gives you a typed font object with:
- per-glyph metrics and offsets,
- packed 1bpp glyph bitmap data,
- helpers for BIOS
BitUnPackparameters, - lookup with fallback to
DEFAULT_CHAR.
Quick start
#include <array>
#include <gba/embed>
static constexpr auto font = gba::embed::bdf([] {
return std::to_array<unsigned char>({
#embed "9x18.bdf"
});
});
static_assert(font.glyph_count > 0);
The returned type is gba::embed::bdf_font_result<GlyphCount, BitmapBytes>.
Demo
The demo below embeds multiple BDF files and renders them in one text layer.
Demo fonts used:
6x13B.bdfHaxorMedium-12.bdf
Font source: IT-Studio-Rech/bdf-fonts.
The demo applies with_shadow<1, 1> to both embedded fonts and uses the
two_plane_three_color profile so the shadow pass is visible.
#include <gba/bios>
#include <gba/embed>
#include <gba/interrupt>
#include <gba/text>
#include <array>
int main() {
using namespace gba::literals;
static constexpr auto base_font_ui = gba::embed::bdf([] {
return std::to_array<unsigned char>({
#embed "6x13B.bdf"
});
});
static constexpr auto base_font_haxor = gba::embed::bdf([] {
return std::to_array<unsigned char>({
#embed "HaxorMedium-12.bdf"
});
});
static constexpr auto font_ui = gba::text::with_shadow<1, 1>(base_font_ui);
static constexpr auto font_haxor = gba::text::with_shadow<1, 1>(base_font_haxor);
gba::irq_handler = {};
gba::reg_dispstat = {.enable_irq_vblank = true};
gba::reg_ie = {.vblank = true};
gba::reg_ime = true;
gba::reg_dispcnt = {.video_mode = 0, .enable_bg0 = true};
gba::reg_bgcnt[0] = {.screenblock = 31};
constexpr auto config = gba::text::bitplane_config{
.profile = gba::text::bitplane_profile::two_plane_three_color,
.palbank_0 = 1,
.palbank_1 = 2,
.start_index = 1,
};
constexpr auto theme = gba::text::bitplane_theme{
.background = "#1A2238"_clr,
.foreground = "#F6F7FB"_clr,
.shadow = "#0A1020"_clr,
};
gba::text::set_theme(config, theme);
gba::pal_bg_mem[0] = theme.background;
gba::text::linear_tile_allocator alloc{.next_tile = 1, .end_tile = 512};
using layer_type = gba::text::bg4bpp_text_layer<240, 160>;
static layer_type::cell_state_map cell_state{};
layer_type layer{31, config, alloc, cell_state};
// Stream metrics for layout
gba::text::stream_metrics title_metrics{
.letter_spacing_px = 0,
.line_spacing_px = 0,
.tab_width_px = 32,
.wrap_width_px = 224,
};
gba::text::stream_metrics body_metrics{
.letter_spacing_px = 1,
.line_spacing_px = 1,
.tab_width_px = 32,
.wrap_width_px = 224,
};
layer.draw_stream(font_haxor, "Embedded BDF fonts", 4, 8, title_metrics);
layer.draw_stream(font_haxor, "HaxorMedium-12: ABC abc 0123", 4, 34, body_metrics);
layer.draw_stream(font_ui, "6x13B: GBA text layer sample", 4, 64, body_metrics);
layer.draw_stream(font_ui, "glyph_or_default + BitUnPack-ready rows", 4, 84, body_metrics);
layer.flush_cache();
while (true) {
gba::VBlankIntrWait();
}
}

What embed::bdf(...) parses
The parser expects standard text BDF structure and reads these fields:
- font-level:
FONTBOUNDINGBOXCHARSFONT_ASCENTandFONT_DESCENT(fromSTARTPROPERTIESblock)DEFAULT_CHAR(optional, fromSTARTPROPERTIES)
- per-glyph:
STARTCHAR/ENDCHARENCODINGDWIDTHBBXBITMAP
It validates glyph counts and bitmap row sizes at compile time.
BDF to GBA bitmap packing
Each BITMAP row is packed to 1bpp bytes in a BIOS-friendly way:
- leftmost source pixel is written to bit 0 (LSB),
- rows are stored in row-major order,
- byte width is
(glyph_width + 7) / 8.
This layout is designed so BitUnPack can expand glyph rows directly.
Using glyph metadata
const auto& g = font.glyph_or_default(static_cast<unsigned int>('A'));
auto width_px = g.width;
auto height_px = g.height;
auto advance_px = g.dwidth;
Useful members on glyph:
encodingdwidthwidth,heightx_offset,y_offsetbitmap_offsetbitmap_byte_widthbitmap_bytes()
Accessing bitmap data and BitUnPack headers
#include <gba/bios>
const auto& g = font.glyph_or_default(static_cast<unsigned int>('A'));
const unsigned char* src = font.bitmap_data(g);
auto unpack = g.bitunpack_header(
/*dst_bpp=*/4,
/*dst_ofs=*/1,
/*offset_zero=*/false
);
// Example destination buffer for expanded glyph data
unsigned int expanded[128]{};
gba::BitUnPack(src, expanded, unpack);
You can also fetch by encoding directly:
const unsigned char* src = font.bitmap_data(static_cast<unsigned int>('A'));
auto unpack = font.bitunpack_header(static_cast<unsigned int>('A'));
Fallback behaviour
glyph_or_default(encoding) resolves in this order:
- exact glyph encoding,
DEFAULT_CHAR(if present and found),- glyph index
0.
This makes rendering robust when text includes characters not present in your BDF.
Font variants for text rendering
After embedding, you can generate compile-time variants for the text renderer:
#include <gba/text>
static constexpr auto font_shadow = gba::text::with_shadow<1, 1>(font);
static constexpr auto font_outline = gba::text::with_outline<1>(font);
These variants keep the same font-style API but add pre-baked decoration masks.