Jazz2::UI::Font class

Bitmap font renderer.

The renderer loads a bitmap font from a custom file format with full Unicode support and renders it to a canvas. It can also measure the size of a string without rendering it.

Text formatting

The renderer supports inline text formatting using the "\f[…]" notation. Unknown attributes are ignored. Following attributes are supported:

  • "\f[c:#RRGGBB]" — Sets the font color that can be specified in a hex value in RGB format
    • The game usually renders text using a colorization shader that uses a different color space to be able to change the text color more precisely, so the specified color must be adjusted accordingly
  • "\f[/c]" — Resets the font color
  • "\f[w:XX]" — Sets the character spacing as a percentage, recommended range is 80% to 120%
  • "\f[/w]" — Resets the character spacing

Public static functions

static auto StripFormatting(StringView text) -> String
Strips formatting from the specified text.

Constructors, destructors, conversion operators

Font(const std::unique_ptr<Death::IO::Stream>& s, StringView path, const std::uint32_t* palette)
Creates a new instance by loading a bitmap font from the specified path.

Public functions

auto GetSizeInPixels() const -> std::int32_t
Returns font size in pixels.
auto GetAscentInPixels() const -> std::int32_t
Returns font ascent in pixels.
auto MeasureChar(char32_t c) const -> Vector2f
Returns size of a single character.
auto MeasureString(StringView text, float scale = 1.0f, float charSpacing = 1.0f, float lineSpacing = 1.0f) -> Vector2f
Returns size of a string.
auto MeasureStringEx(StringView text, float scale, float charSpacing, float maxWidth, std::int32_t* charFit, float* charFitWidths) -> Vector2f
Returns size of a string and its cumulative widths.
void DrawString(Canvas* canvas, StringView text, std::int32_t& charOffset, float x, float y, std::uint16_t z, Alignment align, Colorf color, float scale = 1.0f, float angleOffset = 0.0f, float varianceX = 4.0f, float varianceY = 4.0f, float speed = 0.4f, float charSpacing = 1.0f, float lineSpacing = 1.0f)
Draws a string.
auto IsPaletteIndexed() const -> bool
Whether the atlas holds palette indices instead of baked colors.

Constants

static Colorf DefaultColor constexpr
Default (yellow) font color.
static Colorf TransparentDefaultColor constexpr
Default (yellow) font color with 60% transparency.
static Colorf RandomColor constexpr
Random (rainbow) font color.
static Colorf TransparentRandomColor constexpr
Random (rainbow) font color with 60% transparency.
static bool ShadowsEnabled constexpr
Whether text is drawn with a shadow under it.

Function documentation

Jazz2::UI::Font::Font(const std::unique_ptr<Death::IO::Stream>& s, StringView path, const std::uint32_t* palette)

Creates a new instance by loading a bitmap font from the specified path.

Parameters
s Stream the bitmap font is read from, which can also be a file inside a .pak
path Path the stream was opened from, used in messages and as the texture name
palette Palette used to colorize the font

bool Jazz2::UI::Font::IsPaletteIndexed() const

Whether the atlas holds palette indices instead of baked colors.

A backend that samples a paletted texture keeps the atlas as the indices it was authored with and resolves the colors from the live palette texture at draw time; everywhere else the palette is baked into the atlas when the font is loaded. Only a baked atlas goes stale when the palette changes, which is what ContentResolver decides from - reloading an indexed font would cost a whole atlas and change nothing about how it draws.

Variable documentation

static bool Jazz2::UI::Font::ShadowsEnabled constexpr

Whether text is drawn with a shadow under it.

A shadowed string goes out twice - a faint black copy offset below it first, then the text itself - so the shadow costs as much as the text does. The Nintendo 64 leaves it out: every glyph there is a render command of its own and a textured rectangle with its own TMEM upload, and the shadows were close to half of a text-heavy menu frame (measured in ares: 118 to 74 ms on the first-run screen, 38 to 28 ms on the main menu). Every shadow draw is guarded by this, so where it is off none of that work is done at all.