/*
Bitmap Font: load a .btfont file and compare it to the built-in system font.
@description Load a .btfont bitmap font and draw rainbow, alpha-pulsing, and measured text with it.
Part of the BLIT386 demo series.
Prerequisites:
- Basics: https://demos.blit386.dev/basics
- Colors: https://demos.blit386.dev/colors
- Fonts: https://demos.blit386.dev/fonts
Live version: https://demos.blit386.dev/bitmap-font
Guides:
- Bitmap Fonts: https://blit386.dev/docs/guides/bitmap-fonts#custom-bitmap-fonts-btfont
- Palette: https://blit386.dev/docs/guides/palette#per-draw-palette-offsets-zero-cost-color-variants
Reference:
- Assets: https://blit386.dev/docs/api/assets#bitmap-fonts
- Rendering: https://blit386.dev/docs/api/rendering#bitmap-fonts
- Core Types: https://blit386.dev/docs/api/core-types#color32
---
WHAT YOU WILL SEE
-----------------
A dark screen with a column of text down the left side. The first two lines are the same
title drawn twice: "A:" marks the loaded .btfont (PragmataPro at 14 pt), "B:" marks the
built-in 6x14 system font, so you can compare the two shapes directly. Below them come
four lines in red, green, blue, and yellow, then "Rainbow Animation!" with every letter in
its own color and the colors sliding left over time, then "Pulsing Text" fading in and out
three times a second, then "Special: 3 × 4 = 12" using a real multiplication sign, and
finally "Measured Width" in light gray with an orange underline exactly as wide as the
text.
A small "Font Info" panel in the bottom-right corner lists the font's name, point size,
and glyph count, read straight from the loaded font object.
WHAT YOU WILL LEARN
-------------------
- BitmapFont.load(url) downloads a .btfont file - a JSON glyph table plus a PNG atlas -
and init() awaits it before the demo loop starts.
- font.getSpriteSheet().indexize(palette) must run before the first draw: the atlas holds
white pixels, and indexize swaps each one for the palette slot that holds white (slot 1
here).
- BT.printFont(font, pos, text, paletteOffset) adds the offset to every glyph pixel's
slot, so offset 0 draws slot 1, offset 2 draws slot 3, and so on. BT.systemPrint(pos,
slot, text) takes the slot number directly instead.
- font.getGlyph(char).advance says how far to step right after a character. PragmataPro is
monospaced, so every glyph here advances 7 pixels; a proportional font would return a
different advance per letter, and the same loop would still work.
- font.measureText(text) returns the pixel width of a string before you draw it. The
system font has the same idea in BT.systemPrintMeasure(text).
- font.name, font.size, font.lineHeight, and font.glyphCount describe the loaded font.
- Text color animates by rewriting palette slots in update() - Color32.fromHSL() for the
rainbow, an alpha value for the pulse - while render() keeps passing the same offsets.
WHY LOAD A FONT AT ALL?
-----------------------
A bitmap font is a font where every letter is a small pre-drawn picture, not a set of
curves. That is what gives pixel-art text its crisp look. The built-in system font is one
too, and BT.systemPrint() needs no file, which makes it the right pick for quick labels
and debug readouts.
Load a .btfont instead when you want a different size or style, a glyph set you choose
yourself, or proportional spacing. The loading step is the only extra cost: one await in
init(), and a short wait before the first frame.
*/
import { type BitmapFont = BitmapFont
class BitmapFont
Bitmap font backed by a sprite-sheet texture atlas.
The class is responsible for:
- loading `.btfont` metadata and its referenced texture
- exposing glyph lookup by character or character code
- measuring string widths with a small reusable cache
- providing the underlying
{@link
SpriteSheet
}
used for rendering glyph quadsBitmapFont, function bootstrap(DemoClass: DemoConstructor, options?: BootstrapOptions): Promise<boolean>One-liner bootstrap function for BLIT386 demos.
Handles canvas retrieval and engine initialization. Backend selection
(WebGPU or software fallback) is managed internally by BTAPI.
This function provides a streamlined way to start a demo with sensible defaults
while allowing customization through options.bootstrap, const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT, class Color32Mutable 32-bit RGBA color value with 8-bit channels.Color32, class Vector2iInteger 2D vector for pixel-perfect positioning.
Used for points, sizes, directions, and camera offsets throughout the engine.
The API includes both allocation-free `*To()` / `*InPlace()` variants and
convenience methods that return new vectors.Vector2i } from 'blit386';
import { import applyThemeapplyTheme, import uiui, import UI_ANCHORSUI_ANCHORS } from './shared/ui.js';
/** @typedef {import('blit386').Palette} Palette */
/** @typedef {import('blit386').BitmapFont} BitmapFont */
// The x position where the rainbow text row starts. update() (hue calculation) and
// renderRainbowText() (glyph drawing) both use it so the two stay in sync.
const const RAINBOW_ORIGIN_X: 10RAINBOW_ORIGIN_X = 10;
// Every color used for drawing lives in a numbered palette slot. Slot 0 is always
// transparent, so custom colors start at 1. The screen background and the Font Info panel
// use the shared UI theme instead (installed by applyTheme() in init()), so there are no
// background slots here. Slot 2 is simply left unused.
const const C_WHITE: 1C_WHITE = 1; // pure white: title, special characters, 'A' / 'B' labels
const const C_RED_TEXT: 3C_RED_TEXT = 3; // soft red: "Red Text" sample line
const const C_GREEN_TEXT: 4C_GREEN_TEXT = 4; // soft green: "Green Text" sample line
const const C_BLUE_TEXT: 5C_BLUE_TEXT = 5; // soft blue: "Blue Text" sample line
const const C_YELLOW_TEXT: 6C_YELLOW_TEXT = 6; // yellow: "Yellow Text" sample line
const const C_GRAY_TEXT: 7C_GRAY_TEXT = 7; // light gray: "Measured Width" text
const const C_ORANGE_LINE: 8C_ORANGE_LINE = 8; // orange: underline below the measured-width text
// The rainbow text string comes before the slot constants that depend on it, so C_PULSE
// can be derived from its length. Change the string and update() computes the right
// number of palette colors automatically.
const const RAINBOW_TEXT: "Rainbow Animation!"RAINBOW_TEXT = 'Rainbow Animation!';
// Dynamic slots: each character in RAINBOW_TEXT gets its own animated color slot,
// starting at C_RAINBOW_BASE. update() computes each character's current hue and stores
// it there; render() only passes the slot offset, so no Color32 math happens while
// drawing.
const const C_RAINBOW_BASE: 20C_RAINBOW_BASE = 20; // first slot for the rainbow characters
// Dynamic slot for the pulsing-text color. It sits right after the rainbow slots, so it
// can never overlap them even if RAINBOW_TEXT changes length (20 + 18 = slot 38 today).
const const C_PULSE: anyC_PULSE = const C_RAINBOW_BASE: 20C_RAINBOW_BASE + const RAINBOW_TEXT: "Rainbow Animation!"RAINBOW_TEXT.length;
// Left margin for the "A: " / "B: " labels and the sample text below them.
const const LABEL_X: 10LABEL_X = 10;
// Loads a bitmap font and draws text with it in several ways: static colors, an animated
// rainbow, alpha pulsing, text measurement, and a font metadata panel. Compare it with
// BT.systemPrint() in the Fonts demo.
class class DemoDemo {
// The loaded bitmap font. Starts as null because nothing is loaded yet.
/** @type {BitmapFont | null} */
Demo.font: BitmapFont | nullfont = null;
// All the colors this demo uses.
/** @type {Palette | null} */
Demo.palette: Palette | nullpalette = null;
// Palette slot numbers of the shared UI kit colors, filled in by applyTheme() in
// init(). theme.bg clears the screen so this demo's background matches the rest of
// the series.
Demo.theme: nulltheme = null;
// A timer that counts up in seconds. It drives the speed of the color animations.
Demo.animTime: numberanimTime = 0;
// Size of one system-font glyph, measured in init() with BT.systemPrintMeasure('M').
// The built-in font is 6x14, but measuring keeps the demo honest if that ever
// changes.
Demo.systemCharWidth: numbersystemCharWidth = 6;
Demo.systemLineHeight: numbersystemLineHeight = 14;
// Pixel width of the "A: " prefix, so the title text lines up after the label.
Demo.labelPrefixWidth: numberlabelPrefixWidth = 18;
// Sets up the palette and downloads the bitmap font. Screen size and FPS come from
// the engine's defaultConfig(), since this demo has no configure(). Returns true when
// the font loaded and false when it did not - the engine only starts the demo loop on
// true.
async Demo.init(): unknowninit() {
console.log('[BitmapFontDemo] Initializing...');
// Set up the color palette. Every color is picked before anything is drawn, like
// an artist mixing paint first.
this.Demo.palette: Palette | nullpalette = const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.paletteCreate: (size?: number) => PaletteCreates a standalone palette instance.paletteCreate(256);
// Static colors that never change from frame to frame.
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_WHITE: 1C_WHITE, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(255, 255, 255)); // pure white
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_RED_TEXT: 3C_RED_TEXT, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(255, 100, 100)); // soft red
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_GREEN_TEXT: 4C_GREEN_TEXT, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(100, 255, 100)); // soft green
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_BLUE_TEXT: 5C_BLUE_TEXT, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(100, 100, 255)); // soft blue
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_YELLOW_TEXT: 6C_YELLOW_TEXT, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(255, 255, 100)); // yellow
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_GRAY_TEXT: 7C_GRAY_TEXT, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(200, 200, 200)); // light gray
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_ORANGE_LINE: 8C_ORANGE_LINE, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(255, 200, 100)); // orange underline
// Pre-fill the dynamic rainbow slots with gray so they are not empty on the first
// frame.
for (let let i: numberi = 0; let i: numberi < const RAINBOW_TEXT: "Rainbow Animation!"RAINBOW_TEXT.length; let i: numberi++) {
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_RAINBOW_BASE: 20C_RAINBOW_BASE + let i: numberi, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(128, 128, 128));
}
// Pre-fill the pulse slot too.
this.Demo.palette: Palettepalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_PULSE: anyC_PULSE, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(100, 100, 255));
// Install the shared UI kit colors. They land in slots 240-251, well above this
// demo's highest slot (C_PULSE = 38), so the two can never collide. The returned
// map remembers which slot each UI color went to (theme.bg, theme.text, ...).
this.Demo.theme: nulltheme = import applyThemeapplyTheme(this.Demo.palette: Palettepalette);
// Tell the engine to use this palette for all drawing.
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.paletteSet: (palette: Palette) => voidStores the active engine palette.
Use this to swap the **entire palette** (e.g. switch between a day and night
theme). After this call the renderer uploads the new palette uniform on the
next frame.
**Palette-value swap (change what a slot looks like):** mutate the live
{@link
BT.palette
}
in place with `palette.set(slot, newColor)`. The renderer
uploads dirty slots on the next frame; no `paletteSet()` or
{@link
BT.spritesRefresh
}
needed.
**Palette-layout swap (same colors, different slot positions):** build a new
palette with the same colors at new indices, call `paletteSet()`, then call
{@link
BT.spritesRefresh
}
so every sprite sheet re-maps its original RGBA
pixels against the new slot layout.paletteSet(this.Demo.palette: Palettepalette);
// Measure the built-in system font once, the same way the Fonts demo does.
const const glyphSize: Vector2iglyphSize = const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.systemPrintMeasure: (text: string) => Vector2iMeasures the pixel dimensions of a string rendered with the built-in
system font.systemPrintMeasure('M');
this.Demo.systemCharWidth: numbersystemCharWidth = const glyphSize: Vector2iglyphSize.Vector2i.x: numberHorizontal component (defaults to 0).x;
this.Demo.systemLineHeight: numbersystemLineHeight = const glyphSize: Vector2iglyphSize.Vector2i.y: numberVertical component (defaults to 0).y;
this.Demo.labelPrefixWidth: numberlabelPrefixWidth = const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.systemPrintMeasure: (text: string) => Vector2iMeasures the pixel dimensions of a string rendered with the built-in
system font.systemPrintMeasure('A: ').Vector2i.x: numberHorizontal component (defaults to 0).x;
// Load the font file from the server. A .btfont is a JSON glyph table next to a
// PNG atlas of the glyph images. This is the step BT.systemPrint() skips - the
// system font is built in. `await` pauses here until the download finishes.
try {
this.Demo.font: BitmapFont | nullfont = await class BitmapFontBitmap font backed by a sprite-sheet texture atlas.
The class is responsible for:
- loading `.btfont` metadata and its referenced texture
- exposing glyph lookup by character or character code
- measuring string widths with a small reusable cache
- providing the underlying
{@link
SpriteSheet
}
used for rendering glyph quadsBitmapFont.BitmapFont.load(url: string): Promise<BitmapFont>Loads a bitmap font from a `.btfont` JSON file.
The font descriptor can reference either an embedded PNG data URI
(`data:image/png;base64,...`) or a texture file path relative to the font JSON file.load('/fonts/PragmataPro14.btfont');
// The font object exposes metadata you can read and display.
console.log(`[BitmapFontDemo] Loaded font: ${this.Demo.font: BitmapFont | nullfont.BitmapFont.name: stringFont display name.name}`);
console.log(` Size: ${this.Demo.font: BitmapFont | nullfont.BitmapFont.size: numberOriginal font size in points.size}pt`);
console.log(` Line height: ${this.Demo.font: BitmapFont | nullfont.BitmapFont.lineHeight: numberPixels between baselines for multi-line text.lineHeight}px`);
console.log(` Glyphs: ${this.Demo.font: BitmapFont | nullfont.BitmapFont.glyphCount: numberReturns the total number of glyphs loaded into the font.glyphCount}`);
} catch (function (local var) error: unknownerror) {
console.error('[BitmapFontDemo] Failed to load font:', function (local var) error: unknownerror);
return false;
}
// Tell the font about the palette. Glyphs are stored as white pixels in the
// font's sprite sheet; indexize() replaces each one with the slot that holds
// white, C_WHITE (1). After this call BT.printFont() can recolor the glyphs by
// shifting that index.
this.Demo.font: BitmapFont | nullfont.BitmapFont.getSpriteSheet(): SpriteSheetReturns the sprite sheet that owns the font texture atlas.getSpriteSheet().SpriteSheet.indexize(palette: Palette): voidConverts the sprite sheet's RGBA pixels to palette indices.
Each non-transparent pixel is looked up in the provided palette via exact
color matching. Index 0 is always transparent. The resulting indices are
stored internally; an `r8uint` GPU texture is created lazily on the next
`getTexture()` call.
The original RGBA data is retained so `reindexize()` can re-convert after a
palette swap without reloading the image.indexize(this.Demo.palette: Palettepalette);
console.log('[BitmapFontDemo] Font loaded successfully!');
return true;
}
// Runs at a fixed rate (60 times per second, as the Basics demo explains). Advances
// the animation timer and rewrites the dynamic palette colors.
Demo.update(): voidupdate() {
// Move the animation clock forward by one fixed update step, in seconds.
this.Demo.animTime: numberanimTime += const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.deltaSeconds: numberFixed-step seconds per update tick.
Equivalent to `1 / BT.targetFPS` when `BT.targetFPS` is finite and positive.
Falls back to `1 / 60` when target FPS is non-finite or non-positive.deltaSeconds;
// The pulse: Math.sin returns a wave that swings smoothly between -1 and +1. The
// "2 * Math.PI * frequency" part converts seconds into radians, which is what
// Math.sin expects; with frequency 3 the wave completes three full cycles per
// second. Multiplying by 0.5 and adding 0.5 moves the output from [-1, 1] to [0,
// 1].
const const pulse: numberpulse = Math.sin(2 * Math.PI * 3 * this.Demo.animTime: numberanimTime) * 0.5 + 0.5;
// The alpha channel controls how opaque the text is. At pulse 0 the text
// disappears; at pulse 1 it is solid. RGB stays fixed at (100, 100, 255), a
// medium blue, so only the opacity changes.
this.Demo.palette: Palette | nullpalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_PULSE: anyC_PULSE, new new Color32(r?: number, g?: number, b?: number, a?: number): Color32Creates a clamped 8-bit RGBA color.Color32(100, 100, 255, Math.floor(const pulse: numberpulse * 255)));
// The rainbow: compute a hue (a position on the color wheel) for each character
// from its x position and animTime. The font is always loaded here, because the
// demo loop only starts after init() returned true. HSL colors were introduced in
// the Colors demo: https://demos.blit386.dev/colors
let let charX: numbercharX = const RAINBOW_ORIGIN_X: 10RAINBOW_ORIGIN_X; // same x where render() draws the rainbow text
for (let let i: numberi = 0; let i: numberi < const RAINBOW_TEXT: "Rainbow Animation!"RAINBOW_TEXT.length; let i: numberi++) {
// hue is a position on the color wheel: 0 is red, 120 green, 240 blue, 360
// back to red. Using the character's real x position gives the rainbow its
// spacing; adding animTime * 100 scrolls it left over time. `%` is the
// remainder after division, so `% 360` keeps hue in 0-359 and the wheel
// cycles instead of growing forever.
const const hue: numberhue = (let charX: numbercharX * 3 + this.Demo.animTime: numberanimTime * 100) % 360;
this.Demo.palette: Palette | nullpalette.Palette.set(index: number, color: Color32): voidWrites a color into a palette slot.set(const C_RAINBOW_BASE: 20C_RAINBOW_BASE + let i: numberi, class Color32Mutable 32-bit RGBA color value with 8-bit channels.Color32.Color32.fromHSL(h: number, s: number, l: number, a?: number): Color32Creates a color from HSL values.fromHSL(const hue: numberhue, 100, 60));
// Step charX by this character's advance from the font. PragmataPro is
// monospaced, so this is always 7 here; a proportional font would vary.
const const glyph: Glyph | nullglyph = this.Demo.font: BitmapFont | nullfont.BitmapFont.getGlyph(char: string): Glyph | nullReturns glyph data for a character.
Uses the ASCII lookup table for single-byte characters and falls back to the Unicode glyph
map for everything else, then to the font's fallback glyph (see
{@link
FALLBACK_GLYPH_CHAR
}
)
when neither has an entry for the character.getGlyph(const RAINBOW_TEXT: "Rainbow Animation!"RAINBOW_TEXT[let i: numberi]);
let charX: numbercharX += const glyph: Glyph | nullglyph ? const glyph: Glyphglyph.Glyph.advance: numberHorizontal advance after drawing (distance to next character).advance : 7;
}
}
// Runs once per screen refresh and draws every text sample.
Demo.render(): voidrender() {
// Fill the screen with the shared UI theme's deep navy background.
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.clear: (paletteIndex: number) => voidSets the frame clear color using a palette index.
The renderer uses this color when clearing the full display at the start
of the next frame.clear(this.Demo.theme: nulltheme.bg);
// Start drawing near the top of the screen.
let let y: numbery = 10;
// Where the title text starts after the "A: " / "B: " labels (measured in
// init()).
const const textX: numbertextX = const LABEL_X: 10LABEL_X + this.Demo.labelPrefixWidth: numberlabelPrefixWidth;
// How many pixels tall one line of text is, plus a little breathing room. The
// bitmap font's line height comes from the .btfont file; the system font's was
// measured.
const const bitmapLineHeight: numberbitmapLineHeight = this.Demo.font: BitmapFont | nullfont.BitmapFont.lineHeight: numberPixels between baselines for multi-line text.lineHeight + 2;
const const systemLineHeight: numbersystemLineHeight = this.Demo.systemLineHeight: numbersystemLineHeight + 2;
// BT.printFont() takes (font, position, text, paletteOffset). The offset is added
// to the slot the glyph pixels were indexized to (slot 1), so offset 0 draws slot
// 1 (C_WHITE), offset 2 draws slot 3 (C_RED_TEXT), and so on. BT.systemPrint()
// takes the palette slot number directly instead.
// Draw the title in both fonts, one below the other. "A:" marks the bitmap font
// (7 pixels per character at 14 pt), "B:" the built-in system font (a fixed 6x14
// block per character).
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.systemPrint: (pos: Vector2i, paletteIndex: number, text: string) => voidDraws text using the built-in 6x14 system font.
The system font covers printable ASCII (characters 32-126). For custom
bitmap fonts with proportional glyphs, use
{@link
BT.printFont
}
instead.systemPrint(new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(const LABEL_X: 10LABEL_X, let y: numbery), const C_WHITE: 1C_WHITE, 'A:');
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.printFont: (font: BitmapFont, pos: Vector2i, text: string, paletteOffset?: number) => voidDraws text with a bitmap font through the indexed sprite pipeline.
Supports proportional glyph widths and glyph-level offsets defined by the
supplied
{@link
BitmapFont
}
. The font's underlying sprite sheet must have
been indexized before calling this.
Palette offset semantics and out-of-range behavior are identical to
{@link
BT.drawSprite
}
.printFont(this.Demo.font: BitmapFont | nullfont, new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(const textX: numbertextX, let y: numbery), 'BLIT386 Bitmap Font Demo', 0);
let y: numbery += const bitmapLineHeight: numberbitmapLineHeight;
// The same title in the system font, so the difference is easy to see.
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.systemPrint: (pos: Vector2i, paletteIndex: number, text: string) => voidDraws text using the built-in 6x14 system font.
The system font covers printable ASCII (characters 32-126). For custom
bitmap fonts with proportional glyphs, use
{@link
BT.printFont
}
instead.systemPrint(new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(const LABEL_X: 10LABEL_X, let y: numbery), const C_WHITE: 1C_WHITE, 'B:');
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.systemPrint: (pos: Vector2i, paletteIndex: number, text: string) => voidDraws text using the built-in 6x14 system font.
The system font covers printable ASCII (characters 32-126). For custom
bitmap fonts with proportional glyphs, use
{@link
BT.printFont
}
instead.systemPrint(new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(const textX: numbertextX, let y: numbery), const C_WHITE: 1C_WHITE, 'BLIT386 Bitmap Font Demo');
// Move down past both title lines, with a little extra gap before the next
// section.
let y: numbery += const systemLineHeight: numbersystemLineHeight + 4;
// Draw each section in order, updating y as we go so nothing overlaps.
let y: numbery = this.Demo.renderColoredText(y: any, lineHeight: any): anyrenderColoredText(let y: numbery, const bitmapLineHeight: numberbitmapLineHeight);
let y: numbery = this.Demo.renderRainbowText(y: any, lineHeight: any): anyrenderRainbowText(let y: numbery, const bitmapLineHeight: numberbitmapLineHeight);
let y: numbery = this.Demo.renderPulsingText(y: any, lineHeight: any): anyrenderPulsingText(let y: numbery, const bitmapLineHeight: numberbitmapLineHeight);
let y: numbery = this.Demo.renderSpecialCharacters(y: any, lineHeight: any): anyrenderSpecialCharacters(let y: numbery, const bitmapLineHeight: numberbitmapLineHeight);
this.Demo.renderTextMeasurement(y: any, lineHeight: any): anyrenderTextMeasurement(let y: numbery, const bitmapLineHeight: numberbitmapLineHeight);
// The font metadata panel in the bottom-right corner. FPS and the demo title are
// drawn by the engine overlay, not here.
this.Demo.renderFontInfo(): voidrenderFontInfo();
}
// Draws four sample lines, each in a different color, to show how the palette offset
// picks the color. `y` is where the first line starts and `lineHeight` how far to
// move down between lines. Returns the y position after the last line.
Demo.renderColoredText(y: any, lineHeight: any): anyrenderColoredText(y: anyy, lineHeight: anylineHeight) {
// A local copy, so the parameter itself is never modified - readers expect a
// parameter to keep its value throughout the function.
let let currentY: anycurrentY = y: anyy;
// Each color is reached by offset: slot = 1 + offset, so offset = slot - 1. For
// C_RED_TEXT (slot 3) that is offset 2. With BT.systemPrint() you would simply
// pass C_RED_TEXT.
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.printFont: (font: BitmapFont, pos: Vector2i, text: string, paletteOffset?: number) => voidDraws text with a bitmap font through the indexed sprite pipeline.
Supports proportional glyph widths and glyph-level offsets defined by the
supplied
{@link
BitmapFont
}
. The font's underlying sprite sheet must have
been indexized before calling this.
Palette offset semantics and out-of-range behavior are identical to
{@link
BT.drawSprite
}
.printFont(this.Demo.font: BitmapFont | nullfont, new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(10, let currentY: anycurrentY), 'Red Text', const C_RED_TEXT: 3C_RED_TEXT - 1);
let currentY: anycurrentY += lineHeight: anylineHeight;
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.printFont: (font: BitmapFont, pos: Vector2i, text: string, paletteOffset?: number) => voidDraws text with a bitmap font through the indexed sprite pipeline.
Supports proportional glyph widths and glyph-level offsets defined by the
supplied
{@link
BitmapFont
}
. The font's underlying sprite sheet must have
been indexized before calling this.
Palette offset semantics and out-of-range behavior are identical to
{@link
BT.drawSprite
}
.printFont(this.Demo.font: BitmapFont | nullfont, new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(10, let currentY: anycurrentY), 'Green Text', const C_GREEN_TEXT: 4C_GREEN_TEXT - 1);
let currentY: anycurrentY += lineHeight: anylineHeight;
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.printFont: (font: BitmapFont, pos: Vector2i, text: string, paletteOffset?: number) => voidDraws text with a bitmap font through the indexed sprite pipeline.
Supports proportional glyph widths and glyph-level offsets defined by the
supplied
{@link
BitmapFont
}
. The font's underlying sprite sheet must have
been indexized before calling this.
Palette offset semantics and out-of-range behavior are identical to
{@link
BT.drawSprite
}
.printFont(this.Demo.font: BitmapFont | nullfont, new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(10, let currentY: anycurrentY), 'Blue Text', const C_BLUE_TEXT: 5C_BLUE_TEXT - 1);
let currentY: anycurrentY += lineHeight: anylineHeight;
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.printFont: (font: BitmapFont, pos: Vector2i, text: string, paletteOffset?: number) => voidDraws text with a bitmap font through the indexed sprite pipeline.
Supports proportional glyph widths and glyph-level offsets defined by the
supplied
{@link
BitmapFont
}
. The font's underlying sprite sheet must have
been indexized before calling this.
Palette offset semantics and out-of-range behavior are identical to
{@link
BT.drawSprite
}
.printFont(this.Demo.font: BitmapFont | nullfont, new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(10, let currentY: anycurrentY), 'Yellow Text', const C_YELLOW_TEXT: 6C_YELLOW_TEXT - 1);
// Extra space after this section.
let currentY: anycurrentY += lineHeight: anylineHeight + 4;
return let currentY: anycurrentY;
}
// Draws text where every character has its own color, pre-computed in update() and
// stored in slots C_RAINBOW_BASE + i. Drawing one character per BT.printFont() call
// lets each glyph take a different palette offset. Returns the y position after the
// text.
Demo.renderRainbowText(y: any, lineHeight: any): anyrenderRainbowText(y: anyy, lineHeight: anylineHeight) {
// Start at the left margin (must match RAINBOW_ORIGIN_X used in update()).
let let x: numberx = const RAINBOW_ORIGIN_X: 10RAINBOW_ORIGIN_X;
let let slotIndex: numberslotIndex = 0;
// Loop through the string one character at a time.
for (const const char: anychar of const RAINBOW_TEXT: "Rainbow Animation!"RAINBOW_TEXT) {
// The offset for character i is C_RAINBOW_BASE + i - 1, because offset N
// draws slot 1 + N and we want slot C_RAINBOW_BASE + i.
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.printFont: (font: BitmapFont, pos: Vector2i, text: string, paletteOffset?: number) => voidDraws text with a bitmap font through the indexed sprite pipeline.
Supports proportional glyph widths and glyph-level offsets defined by the
supplied
{@link
BitmapFont
}
. The font's underlying sprite sheet must have
been indexized before calling this.
Palette offset semantics and out-of-range behavior are identical to
{@link
BT.drawSprite
}
.printFont(this.Demo.font: BitmapFont | nullfont, new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(let x: numberx, y: anyy), const char: anychar, const C_RAINBOW_BASE: 20C_RAINBOW_BASE - 1 + let slotIndex: numberslotIndex);
// Look up how wide this character is. "advance" is how many pixels to move
// right before drawing the next one - 7 for every glyph in this monospaced
// font, but a proportional font would give each letter its own value. The
// system font always advances systemCharWidth (6).
const const glyph: Glyph | nullglyph = this.Demo.font: BitmapFont | nullfont.BitmapFont.getGlyph(char: string): Glyph | nullReturns glyph data for a character.
Uses the ASCII lookup table for single-byte characters and falls back to the Unicode glyph
map for everything else, then to the font's fallback glyph (see
{@link
FALLBACK_GLYPH_CHAR
}
)
when neither has an entry for the character.getGlyph(const char: anychar);
// If the glyph exists use its advance, otherwise fall back to 7 pixels.
let x: numberx += const glyph: Glyph | nullglyph ? const glyph: Glyphglyph.Glyph.advance: numberHorizontal advance after drawing (distance to next character).advance : 7;
let slotIndex: numberslotIndex++;
}
return y: anyy + lineHeight: anylineHeight + 4;
}
// Draws text that fades in and out. The alpha was pre-computed in update() with
// Math.sin and stored in slot C_PULSE; the same palette trick works with
// BT.systemPrint(). Returns the y position after the text.
Demo.renderPulsingText(y: any, lineHeight: any): anyrenderPulsingText(y: anyy, lineHeight: anylineHeight) {
// C_PULSE - 1 = 37, so the glyphs draw slot 1 + 37 = 38, the animated color.
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.printFont: (font: BitmapFont, pos: Vector2i, text: string, paletteOffset?: number) => voidDraws text with a bitmap font through the indexed sprite pipeline.
Supports proportional glyph widths and glyph-level offsets defined by the
supplied
{@link
BitmapFont
}
. The font's underlying sprite sheet must have
been indexized before calling this.
Palette offset semantics and out-of-range behavior are identical to
{@link
BT.drawSprite
}
.printFont(this.Demo.font: BitmapFont | nullfont, new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(10, y: anyy), 'Pulsing Text', const C_PULSE: anyC_PULSE - 1);
return y: anyy + lineHeight: anylineHeight + 4;
}
// Shows that the font can draw characters outside plain ASCII. Returns the y position
// after the text.
Demo.renderSpecialCharacters(y: any, lineHeight: any): anyrenderSpecialCharacters(y: anyy, lineHeight: anylineHeight) {
// Offset 0 draws slot 1 (C_WHITE). '\u00D7' is the Unicode multiplication sign
// (×), a non-ASCII character, so this line only shows the sign if the
// .btfont includes a glyph for it.
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.printFont: (font: BitmapFont, pos: Vector2i, text: string, paletteOffset?: number) => voidDraws text with a bitmap font through the indexed sprite pipeline.
Supports proportional glyph widths and glyph-level offsets defined by the
supplied
{@link
BitmapFont
}
. The font's underlying sprite sheet must have
been indexized before calling this.
Palette offset semantics and out-of-range behavior are identical to
{@link
BT.drawSprite
}
.printFont(this.Demo.font: BitmapFont | nullfont, new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(10, y: anyy), 'Special: 3 \u00D7 4 = 12', 0);
return y: anyy + lineHeight: anylineHeight;
}
// Shows font.measureText(): the exact pixel width of a string before you draw it. The
// width is used for an underline that ends exactly under the last letter. The system
// font has the same idea in BT.systemPrintMeasure(). Returns the y position after the
// text and underline.
Demo.renderTextMeasurement(y: any, lineHeight: any): anyrenderTextMeasurement(y: anyy, lineHeight: anylineHeight) {
const const measureText: "Measured Width"measureText = 'Measured Width';
// Ask the font how many pixels wide this string will be.
const const textWidth: numbertextWidth = this.Demo.font: BitmapFont | nullfont.BitmapFont.measureText(text: string): numberMeasures the horizontal pixel width of a text string.
Results are cached for repeated measurements. Iterates by Unicode code point (`for...of`),
not by UTF-16 code unit, so an astral character (a surrogate pair) is measured as the single
glyph it is instead of two lone-surrogate lookups. Glyph resolution (including the ASCII
fast path and fallback-glyph substitution) goes through
{@link
getGlyph
}
, so a measured
width always matches what actually renders.measureText(const measureText: "Measured Width"measureText);
// Draw the text in light gray: C_GRAY_TEXT - 1 = 6, so the glyphs draw slot 7.
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.printFont: (font: BitmapFont, pos: Vector2i, text: string, paletteOffset?: number) => voidDraws text with a bitmap font through the indexed sprite pipeline.
Supports proportional glyph widths and glyph-level offsets defined by the
supplied
{@link
BitmapFont
}
. The font's underlying sprite sheet must have
been indexized before calling this.
Palette offset semantics and out-of-range behavior are identical to
{@link
BT.drawSprite
}
.printFont(this.Demo.font: BitmapFont | nullfont, new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(10, y: anyy), const measureText: "Measured Width"measureText, const C_GRAY_TEXT: 7C_GRAY_TEXT - 1);
// Draw an orange underline exactly as wide as the measured text, 2 pixels above
// the bottom of the line. BT.drawLine() takes a start point, an end point, and a
// palette slot number (not an offset).
const BT: {
FLIP_H: number;
FLIP_V: number;
ROT_90_CW: number;
ROT_180_CW: number;
ROT_270_CW: number;
BTN_UP: number;
BTN_DOWN: number;
BTN_LEFT: number;
BTN_RIGHT: number;
BTN_A: number;
BTN_B: number;
BTN_X: number;
BTN_Y: number;
BTN_L: number;
BTN_R: number;
BTN_START: number;
BTN_SELECT: number;
BTN_POINTER_A: number;
BTN_POINTER_B: number;
BTN_POINTER_C: number;
BTN_POINTER_D: number;
PLAYER_ONE: number;
PLAYER_TWO: number;
PLAYER_THREE: number;
PLAYER_FOUR: number;
AXIS_LEFT_X: number;
AXIS_LEFT_Y: number;
AXIS_RIGHT_X: number;
AXIS_RIGHT_Y: number;
AXIS_TRIGGER_L: number;
... 124 more ...;
spritesRefresh: () => void;
}
Main BLIT386 API namespace used by runtime demos.BT.drawLine: (p0: Vector2i, p1: Vector2i, paletteIndex: number) => voidDraws a pixel-perfect line between two points.
Uses rasterized line drawing without antialiasing.drawLine(
new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(10, y: anyy + lineHeight: anylineHeight - 2),
new new Vector2i(x?: number, y?: number): Vector2iCreates an integer 2D vector, truncating inputs toward zero.Vector2i(10 + const textWidth: numbertextWidth, y: anyy + lineHeight: anylineHeight - 2),
const C_ORANGE_LINE: 8C_ORANGE_LINE,
);
return y: anyy + lineHeight: anylineHeight + 4;
}
// Draws a small panel of font metadata: name, point size, and glyph count, all read
// from the loaded BitmapFont. (The built-in font is a BitmapFont too, reachable as
// BT.systemFont, so the same fields exist there.) The panel uses the shared UI kit:
// everything between ui.begin() and ui.end() stacks into one bordered group that the
// kit sizes and anchors.
Demo.renderFontInfo(): voidrenderFontInfo() {
// Anchor the group to the bottom-right corner, away from the sample text on the
// left and the engine overlay's toggle hint in the bottom-left corner.
import uiui.begin(import UI_ANCHORSUI_ANCHORS.BOTTOM_RIGHT);
// Give the group a background, a border, and an amber title.
import uiui.panel('Font Info');
// ui.kv() draws an aligned "KEY: value" row - the key dim, the value bright.
import uiui.kv('FONT', this.Demo.font: BitmapFont | nullfont.BitmapFont.name: stringFont display name.name);
import uiui.kv('SIZE', `${this.Demo.font: BitmapFont | nullfont.BitmapFont.size: numberOriginal font size in points.size}pt`);
import uiui.kv('GLYPHS', this.Demo.font: BitmapFont | nullfont.BitmapFont.glyphCount: numberReturns the total number of glyphs loaded into the font.glyphCount);
// end() closes the group: the kit measures the rows, places the panel, and draws
// it.
import uiui.end();
}
}
// Hand the Demo class to BLIT386 to start the demo loop.
function bootstrap(DemoClass: DemoConstructor, options?: BootstrapOptions): Promise<boolean>One-liner bootstrap function for BLIT386 demos.
Handles canvas retrieval and engine initialization. Backend selection
(WebGPU or software fallback) is managed internally by BTAPI.
This function provides a streamlined way to start a demo with sensible defaults
while allowing customization through options.bootstrap(class DemoDemo);