pub struct FontAtlas { /* private fields */ }Expand description
A BitmapFont installed in VRAM and ready to draw from.
Hold on to the returned handle for the lifetime you want glyphs
to stay drawable. Uploading costs ~1 GP0 0xA0 transfer of
glyph_count × glyph_w × glyph_h / 4 bytes (plus 2 halfwords
for the CLUT); draw calls cost 4 GP0 words per glyph.
Implementations§
Source§impl FontAtlas
impl FontAtlas
Sourcepub fn upload(font: &'static BitmapFont, tpage: TexturePage, clut: Clut) -> Self
pub fn upload(font: &'static BitmapFont, tpage: TexturePage, clut: Clut) -> Self
Upload font as a 4bpp CLUT texture at tpage, with a
2-entry CLUT (transparent, white) at clut.
The caller picks the tpage / clut locations – typically
TexturePage::new(768, 0, TextureDepth::Bit4) for the standard
off-display region, and a Clut::new(768, 480) for the
CLUT row. Both must live inside VRAM and not overlap the
active framebuffer.
Atlas layout picks glyphs_per_row so the texture’s pixel
width stays within MAX_ATLAS_W_TEXELS. For 8-wide fonts
that’s 32 glyphs per row; 16-wide fonts get 16 per row.
Sourcepub fn text_width(&self, text: &str) -> u16
pub fn text_width(&self, text: &str) -> u16
Pixel width of text when rendered with this atlas.
Sourcepub fn glyph_advance(&self, ch: char) -> u8
pub fn glyph_advance(&self, ch: char) -> u8
Pixel advance of one character, the per-glyph term
FontAtlas::text_width sums.
Exposed so a caller scanning a string can carry a running width
instead of re-measuring every prefix: word wrap is quadratic
otherwise, which on R3000 is the most expensive thing a text layout
does. text_width(s) stays exactly
s.chars().map(glyph_advance).fold(0, u16::saturating_add).
Sourcepub fn text_width_with_spacing(&self, text: &str, letter_spacing: i8) -> u16
pub fn text_width_with_spacing(&self, text: &str, letter_spacing: i8) -> u16
Pixel width of text using this atlas’s font metrics plus signed
spacing inserted between adjacent characters.
Sourcepub fn line_height(&self) -> u16
pub fn line_height(&self) -> u16
Pixel height of a single text line. Usually == glyph_h.
Sourcepub fn draw_text(&self, x: i16, y: i16, text: &str, tint: (u8, u8, u8))
pub fn draw_text(&self, x: i16, y: i16, text: &str, tint: (u8, u8, u8))
Draw text at screen-space (x, y) with the given tint.
(0x80, 0x80, 0x80) = unmodulated white; any other value
recolours every glyph via the PSX per-texel multiplier
(output = texel * tint / 128).
Fast path – uses textured rectangles (GP0 0x64, 4 words
per glyph). For any transform or per-vertex colour needs,
reach for Self::draw_text_scaled, Self::draw_text_rotated,
Self::draw_text_affine, or Self::draw_text_gradient.
Characters outside the font’s codepoint range are skipped –
they still advance the cursor so the rest of the string
lines up as the caller intended. Iteration is per-char,
so any &str is valid input; a Latin-1 atlas with
first_char = 0xA0 picks up é, ü, etc. automatically.
Sets the GP0(0xE1) draw-mode tpage to our atlas once at the start of the call – if the caller was rendering with a different tpage, they’ll need to re-apply theirs after draw_text returns.
§Example
atlas.draw_text(8, 8, "SCORE 0042", (0x80, 0x80, 0x80));Sourcepub fn emit_text_packets(
&self,
x: i16,
y: i16,
text: &str,
tint: (u8, u8, u8),
emit: impl FnMut(&[u32]) -> bool,
) -> bool
pub fn emit_text_packets( &self, x: i16, y: i16, text: &str, tint: (u8, u8, u8), emit: impl FnMut(&[u32]) -> bool, ) -> bool
Emit ordered GP0 packets for deferred, axis-aligned text drawing.
The sink receives one two-word draw-mode/texture-window packet followed by one four-word textured rectangle per supported glyph. Atlas padding, packed-page origin, CLUT and character advances are owned by this atlas, just as they are for immediate drawing. No GPU access occurs here.
The sink must copy each borrowed packet before returning. Returning
false stops emission immediately, allowing a fixed packet arena to
reject a full run without writing beyond its capacity. Earlier packets
remain accepted; the return value says whether the entire run fit.
Sourcepub fn draw_text_with_spacing(
&self,
x: i16,
y: i16,
text: &str,
letter_spacing: i8,
tint: (u8, u8, u8),
)
pub fn draw_text_with_spacing( &self, x: i16, y: i16, text: &str, letter_spacing: i8, tint: (u8, u8, u8), )
Draw text with signed spacing inserted between adjacent characters.
letter_spacing is measured in final screen pixels and is not applied
after the last character.
Sourcepub fn draw_text_blended(
&self,
x: i16,
y: i16,
text: &str,
tint: (u8, u8, u8),
blend: TextBlend,
)
pub fn draw_text_blended( &self, x: i16, y: i16, text: &str, tint: (u8, u8, u8), blend: TextBlend, )
Draw text with the GPU’s semi-transparency enabled, blending
each glyph against the framebuffer with blend’s equation.
Fast path – same GP0 0x64 textured rectangle and same 4
words per glyph as Self::draw_text, with the primitive’s
semi-transparency bit set and the draw mode’s ABR field carrying
the equation. The only extra cost is the one draw-mode word at
the start and the one that restores the opaque mode at the end.
Why this exists: the opaque path can only recolour a glyph, and
a tint ramped toward black draws a black glyph, not an absent
one. TextBlend::Add plus a ramped tint is the PS1’s real
fade – at tint zero the glyph contributes nothing and vanishes.
Restores the atlas’s opaque draw mode before returning, so a
blended run cannot leave a stray blend equation on the GPU for
whatever draws next. As with Self::draw_text, the tpage slot
is left pointing at this atlas.
Sourcepub fn draw_text_blended_with_spacing(
&self,
x: i16,
y: i16,
text: &str,
letter_spacing: i8,
tint: (u8, u8, u8),
blend: TextBlend,
)
pub fn draw_text_blended_with_spacing( &self, x: i16, y: i16, text: &str, letter_spacing: i8, tint: (u8, u8, u8), blend: TextBlend, )
Self::draw_text_blended with signed inter-character spacing,
measured in screen pixels and not applied after the last glyph.
Sourcepub fn draw_backdrop_blended(
&self,
x: i16,
y: i16,
w: u16,
h: u16,
tint: (u8, u8, u8),
blend: TextBlend,
)
pub fn draw_backdrop_blended( &self, x: i16, y: i16, w: u16, h: u16, tint: (u8, u8, u8), blend: TextBlend, )
Draw a flat semi-transparent rectangle in blend’s equation –
a backing plate for a run of text drawn on top of it.
This is a method on the atlas rather than a free function
because the blend equation is carried by the GP0(E1h) draw-mode
word, which also names a texture page. Setting one means setting
the other, so whatever changes the equation has to know which
page the glyphs that follow will want back – and the atlas is
what knows that. It restores the atlas’s opaque draw mode on the
way out, exactly as Self::draw_text_blended does.
TextBlend::Subtract is the equation to reach for behind
text: it darkens whatever is under it and, because it scales
with the tint, it fades to nothing along with the text it seats.
TextBlend::Average cannot fade – at tint zero it still
halves the background, so a plate drawn with it outlives the
glyphs and is left behind as a grey box.
Sourcepub fn draw_text_scaled(
&self,
x: i16,
y: i16,
text: &str,
scale_x: u8,
scale_y: u8,
tint: (u8, u8, u8),
)
pub fn draw_text_scaled( &self, x: i16, y: i16, text: &str, scale_x: u8, scale_y: u8, tint: (u8, u8, u8), )
Draw text at screen-space (x, y) scaled by (scale_x, scale_y). scale=(1, 1) matches Self::draw_text’s
output, but via the quad path instead of the rect path –
so prefer draw_text for native-size.
Quad path – uses textured quads (GP0 0x2C, 9 words per
glyph). PSX samples textures with nearest-neighbour, so
integer scales (2×, 3×, 4×) produce crisp pixel-doubled
output. Use Self::draw_text_scaled_q8 for fractional
screen-space sizes.
§Example
atlas.draw_text_scaled(80, 100, "GAME OVER", 3, 3, (220, 40, 40));Sourcepub fn draw_text_scaled_q8(
&self,
x: i16,
y: i16,
text: &str,
scale_x_q8: u16,
scale_y_q8: u16,
tint: (u8, u8, u8),
)
pub fn draw_text_scaled_q8( &self, x: i16, y: i16, text: &str, scale_x_q8: u16, scale_y_q8: u16, tint: (u8, u8, u8), )
Draw text with Q8 fixed-point scale factors (256 = 1.0x).
This keeps the font atlas monochrome while allowing fractional
screen-space glyph sizes such as 1.5x (384).
Sourcepub fn draw_text_scaled_with_spacing(
&self,
x: i16,
y: i16,
text: &str,
scale_x: u8,
scale_y: u8,
letter_spacing: i8,
tint: (u8, u8, u8),
)
pub fn draw_text_scaled_with_spacing( &self, x: i16, y: i16, text: &str, scale_x: u8, scale_y: u8, letter_spacing: i8, tint: (u8, u8, u8), )
Draw scaled text with signed spacing inserted between adjacent
characters. letter_spacing is measured in final screen pixels, not
source-glyph pixels.
Sourcepub fn draw_text_scaled_with_spacing_q8(
&self,
x: i16,
y: i16,
text: &str,
scale_x_q8: u16,
scale_y_q8: u16,
letter_spacing: i8,
tint: (u8, u8, u8),
)
pub fn draw_text_scaled_with_spacing_q8( &self, x: i16, y: i16, text: &str, scale_x_q8: u16, scale_y_q8: u16, letter_spacing: i8, tint: (u8, u8, u8), )
Draw scaled text with Q8 fixed-point scale and signed spacing inserted
between adjacent characters. letter_spacing is measured in final
screen pixels, not source-glyph pixels.
Sourcepub fn draw_text_rotated(
&self,
cx: i16,
cy: i16,
text: &str,
angle_q12: u16,
tint: (u8, u8, u8),
)
pub fn draw_text_rotated( &self, cx: i16, cy: i16, text: &str, angle_q12: u16, tint: (u8, u8, u8), )
Draw text rotated around the pivot (cx, cy) by
angle_q12 (Q0.12, one revolution = 4096). The string is
centred on the pivot at angle 0 – its natural extent is
text_width × glyph_h, anchored so (cx, cy) sits at the
centre of the baseline.
Quad path – 9 GP0 words per glyph. Sin/cos come from a
compact 256-entry Q1.12 table ([sincos]), good to ~1.4° –
imperceptible at 8px glyph scale.
See crate-level docs for the Q0.12 angle convention.
§Example
// Spin a title once per second (frame_idx updates each vsync).
let angle = ((frame_idx * 68) & 0xFFF) as u16; // ~4096/60 ≈ 68
atlas.draw_text_rotated(160, 120, "SPIN", angle, (255, 255, 255));Sourcepub fn draw_text_affine(
&self,
origin: (i16, i16),
text: &str,
m: [[i16; 2]; 2],
tint: (u8, u8, u8),
)
pub fn draw_text_affine( &self, origin: (i16, i16), text: &str, m: [[i16; 2]; 2], tint: (u8, u8, u8), )
Draw text through an arbitrary 2×2 affine transform.
The matrix m is Q3.12 fixed-point – m = [[4096, 0], [0, 4096]] is the identity (native size, axis-aligned). Each
glyph’s local corner (lx, ly) maps onto screen space as
(origin.0 + (m[0][0]*lx + m[0][1]*ly) >> 12, origin.1 + (m[1][0]*lx + m[1][1]*ly) >> 12).
Covers rotation, non-uniform scale, shear, reflection, and any combination – the other quad-path methods are all specializations of this one.
Quad path – 9 GP0 words per glyph.
§Example: horizontal shear
// Skew 30° right: x' = x + 0.577·y. 0.577 × 4096 ≈ 2365.
let m = [[4096, 2365], [0, 4096]];
atlas.draw_text_affine((40, 40), "SKEW", m, (200, 200, 200));Sourcepub fn draw_text_gradient(
&self,
x: i16,
y: i16,
text: &str,
top: (u8, u8, u8),
bottom: (u8, u8, u8),
)
pub fn draw_text_gradient( &self, x: i16, y: i16, text: &str, top: (u8, u8, u8), bottom: (u8, u8, u8), )
Draw text with a top-to-bottom colour gradient.
Top of each glyph is tinted top; bottom is tinted
bottom. The GPU gouraud-interpolates down each glyph,
producing a smooth vertical gradient across the whole line.
Gouraud quad path – 12 GP0 words per glyph (GP0 0x3C).
Use when you want a rainbow title, a torch-lit dialogue
box, or any per-vertex colour effect; prefer the single-
tint Self::draw_text otherwise.
§Example
// Classic "red hot" gradient.
atlas.draw_text_gradient(
40, 10, "INFERNO",
(255, 240, 80), // bright yellow at the top
(180, 30, 20), // deep red at the bottom
);Sourcepub fn draw_text_scaled_gradient(
&self,
x: i16,
y: i16,
text: &str,
scale_x: u8,
scale_y: u8,
top: (u8, u8, u8),
bottom: (u8, u8, u8),
)
pub fn draw_text_scaled_gradient( &self, x: i16, y: i16, text: &str, scale_x: u8, scale_y: u8, top: (u8, u8, u8), bottom: (u8, u8, u8), )
Draw text with a top-to-bottom gradient, scaled by
(scale_x, scale_y). Combines Self::draw_text_scaled
and Self::draw_text_gradient in one draw – a 3× title
with a fire-colour sweep costs the same 12 words per glyph
as a 1× gradient.
Nearest-neighbour sampling still applies, so integer scales produce crisp pixel-doubled output.
§Example
// 3× "TITLE" with yellow→red gradient.
atlas.draw_text_scaled_gradient(
64, 10, "TITLE", 3, 3,
(255, 220, 80), (200, 40, 20),
);Sourcepub fn draw_text_scaled_gouraud_with_spacing_q8(
&self,
x: i16,
y: i16,
text: &str,
scale_x_q8: u16,
scale_y_q8: u16,
letter_spacing: i8,
colors: [(u8, u8, u8); 4],
)
pub fn draw_text_scaled_gouraud_with_spacing_q8( &self, x: i16, y: i16, text: &str, scale_x_q8: u16, scale_y_q8: u16, letter_spacing: i8, colors: [(u8, u8, u8); 4], )
Draw text with per-vertex glyph colours, Q8 scaling, and
authored letter spacing.
colors are ordered top-left, top-right, bottom-left,
bottom-right, matching psx_gpu::draw_quad_textured_gouraud.
This is the general-purpose path behind gradient UI text:
vertical gradients use [top, top, bottom, bottom], horizontal
gradients use [left, right, left, right].
Sourcepub fn font(&self) -> &'static BitmapFont
pub fn font(&self) -> &'static BitmapFont
Access the underlying BitmapFont. Useful for UI code
that needs glyph dimensions for its own layout math.
Sourcepub fn texture_page(&self) -> TexturePage
pub fn texture_page(&self) -> TexturePage
Texture page the atlas is installed at – useful if the caller wants to restore it after drawing with a different tpage. Always 4bpp, always inside a valid VRAM page-aligned slot.
Trait Implementations§
Source§impl TextSink for FontAtlas
impl TextSink for FontAtlas
Source§fn draw(&self, x: i16, y: i16, text: &str, tint: (u8, u8, u8))
fn draw(&self, x: i16, y: i16, text: &str, tint: (u8, u8, u8))
text with its top-left corner at (x, y).Source§fn width(&self, text: &str) -> i16
fn width(&self, text: &str) -> i16
text in screen pixels, using the same metrics draw will.Source§fn line_height(&self) -> i16
fn line_height(&self) -> i16
Source§fn draw_centered(&self, x: i16, y: i16, text: &str, tint: (u8, u8, u8))
fn draw_centered(&self, x: i16, y: i16, text: &str, tint: (u8, u8, u8))
text centred on x. Provided so every caller does not repeat the
half-width arithmetic, and so a renderer with non-integer metrics can
override the rounding.impl Copy for FontAtlas
Auto Trait Implementations§
impl Freeze for FontAtlas
impl RefUnwindSafe for FontAtlas
impl Send for FontAtlas
impl Sync for FontAtlas
impl Unpin for FontAtlas
impl UnsafeUnpin for FontAtlas
impl UnwindSafe for FontAtlas
Blanket Implementations§
§impl<T> Any for Twhere
T: 'static + ?Sized,
impl<T> Any for Twhere
T: 'static + ?Sized,
§impl<T> Borrow<T> for Twhere
T: ?Sized,
impl<T> Borrow<T> for Twhere
T: ?Sized,
§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§unsafe fn clone_to_uninit(&self, dest: *mut u8)
unsafe fn clone_to_uninit(&self, dest: *mut u8)
clone_to_uninit)