28 KiB
Text and Fonts — TextFont, TextAttr, Rendering
Overview
AmigaOS renders all text through bitmap fonts — each glyph is a pre-drawn grid of pixels stored in a single monolithic bitmap strip. The graphics.library Text() function blits these glyph pixels to the screen via the Blitter, making text rendering essentially free in CPU terms. This was a deliberate design: a 7 MHz 68000 could not rasterize TrueType curves at interactive speeds, so the Amiga pre-rendered everything.
Two fonts live in ROM — topaz 8 and topaz 9 — available instantly at boot with no disk access. Every other font (helvetica, times, courier, garnet, sapphire, third-party) lives on disk under the FONTS: assign and must be loaded via diskfont.library.
flowchart TD
subgraph "Font Sources"
ROM["ROM Fonts<br/>(topaz 8, topaz 9)"]
DISK["Disk Fonts<br/>(FONTS: directory)"]
OUTLINE["Compugraphic Outlines<br/>(OS 2.0+)"]
end
ROM -->|"OpenFont"| TF["struct TextFont"]
DISK -->|"OpenDiskFont"| TF
OUTLINE -->|"OpenDiskFont\n(auto-rasterized)"| TF
TF -->|"SetFont(rp, font)"| RP["RastPort"]
RP -->|"Text(rp, str, len)"| BM["BitMap\n(Blitter blits glyph pixels)"]
STYLE["Algorithmic Styles\n(Bold/Italic/Underline)"] -->|"SetSoftStyle"| RP
COLOR["Color Fonts\n(OS 3.0+, up to 8 bitplanes)"] --> TF
style TF fill:#e8f4fd,stroke:#2196f3,color:#333
style BM fill:#c8e6c9,stroke:#2e7d32,color:#333
style STYLE fill:#fff9c4,stroke:#f9a825,color:#333
style COLOR fill:#fff9c4,stroke:#f9a825,color:#333
Key Structures
/* graphics/text.h — NDK39 */
/* Font request — describes what you want: */
struct TextAttr {
STRPTR ta_Name; /* font name, e.g. "topaz.font" */
UWORD ta_YSize; /* desired height in pixels */
UBYTE ta_Style; /* FSF_BOLD, FSF_ITALIC, FSF_UNDERLINED */
UBYTE ta_Flags; /* FPF_ROMFONT, FPF_DISKFONT, FPF_PROPORTIONAL, etc. */
};
/* Loaded font instance: */
struct TextFont {
struct Message tf_Message; /* standard message header */
UWORD tf_YSize; /* actual font height */
UBYTE tf_Style; /* styles this font has built-in */
UBYTE tf_Flags; /* FPF_ROMFONT, FPF_DISKFONT, FPF_PROPORTIONAL, etc. */
UWORD tf_XSize; /* nominal character width */
UWORD tf_Baseline; /* pixels from top to baseline */
UWORD tf_BoldSmear; /* extra pixels for algorithmic bold */
UWORD tf_Accessors; /* current open count */
UBYTE tf_LoChar; /* first character code (usually 32) */
UBYTE tf_HiChar; /* last character code (usually 127 or 255) */
APTR tf_CharData; /* bitmap strip containing all glyphs */
UWORD tf_Modulo; /* bytes per row of font bitmap */
APTR tf_CharLoc; /* location table: offset + width per char */
APTR tf_CharSpace; /* proportional spacing table (NULL = fixed) */
APTR tf_CharKern; /* kerning adjustment table (NULL = none) */
};
Field Reference
| Field | Purpose | Important Notes |
|---|---|---|
tf_YSize |
Font height in pixels | Amiga "point size" = pixel height, not typographic points |
tf_Baseline |
Distance from top to baseline | Position text here — not at the top of the cell |
tf_BoldSmear |
Pixels to smear for algorithmic bold | Usually 1; affects rendered width |
tf_LoChar / tf_HiChar |
Character range (typically 32–127 or 32–255) | Characters outside range render as default glyph |
tf_CharData |
Pointer to the monolithic glyph bitmap strip | 1 bitplane for monochrome; multiple for ColorFont |
tf_CharLoc |
Array of {offset, width} pairs, one per character |
ULONG per char: high word = bit offset, low word = pixel width |
tf_CharSpace |
Per-character horizontal advance | NULL means fixed-width (tf_XSize used for all) |
tf_CharKern |
Per-character kerning offset | NULL means no kerning |
Font Bitmap Layout
All characters are stored in a single bitmap strip. The tf_CharLoc table tells the renderer where each character starts:
tf_CharData bitmap:
┌──┬───┬──┬───┬──┬──┬───────────────────┐
│A │ B │C │ D │E │F │ ... all chars ... │
└──┴───┴──┴───┴──┴──┴───────────────────┘
tf_CharLoc[ch - tf_LoChar]:
bits 31–16 = bit offset into tf_CharData
bits 15–0 = character width in pixels
tf_CharSpace[ch - tf_LoChar]:
spacing advance (proportional fonts)
Opening and Closing Fonts
Font Opening Decision Guide
flowchart TD
NEED{"What font?"} -->|"topaz 8 or 9"| ROM["OpenFont()\nNo disk access needed"]
NEED -->|"Any other font"| DISK["OpenDiskFont()\nLoads from FONTS:"]
NEED -->|"User's screen font"| SCR["Use screen->RastPort.Font\nAlready open, no loading"]
DISK --> FOUND{"Found on disk?"}
FOUND -->|"Yes"| LOAD["Load and return TextFont*"]
FOUND -->|"No exact match"| SCALE{"Can scale existing?"}
SCALE -->|"Yes"| SCALED["Scale bitmap and return\nQuality may be poor"]
SCALE -->|"No"| FAIL["Return NULL"]
style ROM fill:#c8e6c9,stroke:#2e7d32,color:#333
style LOAD fill:#c8e6c9,stroke:#2e7d32,color:#333
style SCALED fill:#fff9c4,stroke:#f9a825,color:#333
style FAIL fill:#ffcdd2,stroke:#c62828,color:#333
Code Examples
/* ROM font (topaz — always available, no disk access): */
struct TextAttr ta = {"topaz.font", 8, 0, FPF_ROMFONT};
struct TextFont *font = OpenFont(&ta);
/* Disk font (requires diskfont.library): */
struct Library *DiskfontBase = OpenLibrary("diskfont.library", 0);
struct TextAttr ta2 = {"helvetica.font", 24, 0, FPF_DISKFONT};
struct TextFont *font2 = OpenDiskFont(&ta2);
/* Request with style (may get algorithmically generated): */
struct TextAttr ta3 = {"topaz.font", 8, FSF_BOLD, FPF_ROMFONT};
struct TextFont *bold = OpenFont(&ta3);
/* Use the screen's current font (respects user preferences): */
struct TextFont *scrFont = screen->RastPort.Font;
SetFont(rp, scrFont);
Font Flags
| Flag | Meaning | Notes |
|---|---|---|
FPF_ROMFONT |
Font lives in ROM | Only topaz 8 and topaz 9 |
FPF_DISKFONT |
Font loaded from disk | Requires diskfont.library |
FPF_PROPORTIONAL |
Variable-width characters | tf_CharSpace != NULL |
FPF_DESIGNED |
Explicitly designed at this size | Set by the font designer; if absent, font was auto-scaled |
FPF_TALLDOT |
Designed for HiRes (640×200 NTSC non-interlaced) | Taller pixel aspect |
FPF_WIDEDOT |
Designed for LoRes interlaced (320×400 NTSC) | Wider pixel aspect |
FPF_REVPATH |
Right-to-left rendering | For Hebrew, Arabic |
Rendering Text
Draw Mode Effects on Text
| Draw Mode | Effect on Text | FgPen | BgPen |
|---|---|---|---|
JAM1 |
Draw foreground only — transparent background | Used | Ignored |
JAM2 |
Draw foreground + solid background | Used | Used |
COMPLEMENT |
XOR text with destination | Ignored | Ignored |
INVERSVID |
Invert video (swap fg/bg roles) | Modifier | Modifier |
/* Position cursor then render: */
Move(rp, 10, 20 + rp->Font->tf_Baseline); /* baseline-relative! */
Text(rp, "Hello Amiga", 11);
/* Measure width before rendering (for centering/alignment): */
UWORD width = TextLength(rp, "Hello Amiga", 11);
/* Centre text: */
WORD centreX = (screenWidth - width) / 2;
Move(rp, centreX, 100);
Text(rp, "Hello Amiga", 11);
/* Pixel-perfect extent info: */
struct TextExtent te;
TextExtent(rp, "Hello", 5, &te);
/* te.te_Width = total pixel width */
/* te.te_Height = total pixel height */
/* te.te_Extent = bounding rectangle */
Important
Text()renders at the current pen position, which should be at the font's baseline — not the top of the character. The baseline offset isfont->tf_Baselinepixels below the top. If you position at the top instead, descenders (g, j, p, q, y) will clip.
Rendering API Reference
| Function | Library | Purpose |
|---|---|---|
Text() |
graphics | Render count characters at current position |
TextLength() |
graphics | Return pixel width of a string (no rendering) |
TextExtent() |
graphics | Return width, height, and bounding rect |
TextFit() |
graphics | Max characters that fit in a given width |
SetFont() |
graphics | Set the RastPort's current font |
OpenFont() |
graphics | Open a font already in system memory |
CloseFont() |
graphics | Release a font (decrement accessor count) |
OpenDiskFont() |
diskfont | Load a font from disk (or system memory) |
AvailFonts() |
diskfont | Enumerate all available fonts |
Algorithmic Styles
When a font doesn't have a built-in bold/italic variant, the Amiga can generate these styles algorithmically at render time:
/* Style flags: */
#define FSF_UNDERLINED 0x01
#define FSF_BOLD 0x02
#define FSF_ITALIC 0x04
#define FSF_EXTENDED 0x08
/* Ask which styles this font supports algorithmically: */
UWORD supported = AskSoftStyle(rp);
/* Apply bold + italic: */
SetSoftStyle(rp, FSF_BOLD | FSF_ITALIC, supported);
Text(rp, "Bold Italic Text", 16);
/* Reset to normal: */
SetSoftStyle(rp, 0, supported);
| Style | Method | Visual Effect | Performance Impact |
|---|---|---|---|
| Bold | Smear right by tf_BoldSmear pixels |
Characters become slightly wider | Negligible — extra blit per scanline |
| Italic | Shear top scanlines right | Fixed-angle slant (~12°) | Negligible — per-scanline offset |
| Underline | Draw 1-pixel line at descender level | Line below baseline | Negligible — one extra line draw |
| Extended | Widen each character by 50% | Extra-wide spacing | Moderate — affects spacing calculation |
Note
Algorithmic styles work on any bitmap font. The
AskSoftStyle()function returns which styles are supported — always check before applying. All four basic styles are supported by the standard renderer.
Color Fonts (OS 3.0+)
OS 3.0 extended bitmap fonts with multi-bitplane color support. Instead of a single bitplane (1-bit: foreground or background), color fonts store up to 8 bitplanes per glyph, enabling 256-color text:
/* graphics/text.h — NDK39 */
struct ColorTextFont {
struct TextFont ctf_TF; /* standard TextFont (must be first!) */
UWORD ctf_Flags; /* CT_COLORFONT, CT_GREYFONT */
UBYTE ctf_Depth; /* number of bitplanes (1–8) */
UBYTE ctf_FgColor; /* default foreground pen */
UBYTE ctf_Low; /* lowest color register used */
UBYTE ctf_High; /* highest color register used */
APTR ctf_PlanePick; /* plane selection for rendering */
APTR ctf_PlaneOnOff; /* plane on/off masks */
struct ColorFontColors *ctf_ColorTable;
APTR ctf_CharData[8]; /* per-plane glyph data pointers */
};
Traditional font: 1 bitplane → FgPen or BgPen only
Color font: up to 8 bitplanes → 256 colors per glyph pixel
Per-plane glyph storage:
┌─────────────────┐
│ ctf_CharData[0] │ bitplane 0 (LSB)
├─────────────────┤
│ ctf_CharData[1] │ bitplane 1
├─────────────────┤
│ ... │ ...
├─────────────────┤
│ ctf_CharData[N] │ bitplane N
└─────────────────┘
Each plane is a separate bitmap strip, same layout as tf_CharData.
ctf_ColorTable maps pen indices to screen palette entries.
Warning
Color fonts require Chip RAM for all glyph data planes. A 24-pixel color font at 8 bitplanes uses 8× the memory of a monochrome bitmap font. On a 512 KB Chip RAM system, loading multiple color fonts can exhaust memory fast.
Color fonts render automatically with Text() — the renderer detects FPF_COLORFONT in tf_Flags and uses the multi-plane blit path. No special code is needed.
Compugraphic Outline Fonts (OS 2.0+)
OS 2.0 added support for AGFA Compugraphic outline fonts via diskfont.library. These store mathematical curve descriptions instead of pre-drawn pixels. From the programmer's perspective, they work identically to bitmap fonts — OpenDiskFont() handles rasterization internally:
/* Same API — diskfont.library auto-detects outline vs bitmap: */
struct TextAttr ta = {"CGTriumvirate.font", 36, 0, 0};
struct TextFont *outlineFont = OpenDiskFont(&ta);
/* If no 36-pixel bitmap exists but a CG outline does,
diskfont.library rasterizes it to a bitmap automatically */
| Aspect | Bitmap Font | Compugraphic Outline |
|---|---|---|
| Source | Pre-drawn pixels per size | Mathematical curves |
| Scaling | Distorts at non-designed sizes | Clean at any size |
| File format | .font + numbered descriptor files |
.font with outline data |
| API difference | None — same OpenDiskFont() |
None — transparent to application |
| Speed | Instant (blit pixels) | Slower (first rasterize, then blit) |
| Availability | OS 1.0+ | OS 2.0+ (requires diskfont.library V36+) |
Note
Compugraphic outlines are not TrueType or PostScript. They are AGFA's proprietary curve format. The Amiga never gained native TrueType support; third-party libraries (like TrueDot) filled this gap.
Font Scaling and Aspect Ratio
When OpenDiskFont() can't find an exact size match, it scales an existing bitmap. This produces usable but not beautiful results:
/* Request a size that doesn't exist on disk: */
struct TextAttr ta = {"topaz.font", 15, 0, 0};
/* diskfont.library scales topaz 8 or topaz 9 to 15 pixels */
/* Result: usable but slightly blurry or jagged */
struct TextFont *scaled = OpenDiskFont(&ta);
The FPF_DESIGNED flag in tf_Flags indicates whether the font was explicitly designed at this size (clear) or auto-scaled (set). Always check:
if (font->tf_Flags & FPF_DESIGNED) {
/* This size was designed — crisp rendering */
} else {
/* Auto-scaled — may look rough at large sizes */
}
Aspect Ratio Mismatch
Fonts designed for one display mode may look wrong on another:
| Source Mode | Target Mode | Visual Effect |
|---|---|---|
| LoRes 320×256 | HiRes 640×256 | Font appears half-width (compressed) |
| HiRes 640×256 | LoRes 320×256 | Font appears double-width (stretched) |
| Non-interlaced | Interlaced | Font appears half-height (thin) |
| Interlaced | Non-interlaced | Font appears double-height (fat) |
The FPF_TALLDOT and FPF_WIDEDOT flags indicate which pixel aspect the font was designed for. Check these when rendering across different screen modes.
Available Font Lists
/* List all fonts available on FONTS: */
struct AvailFontsHeader *afh;
LONG bufSize = 4096;
do {
afh = AllocMem(bufSize, MEMF_ANY);
LONG shortBy = AvailFonts((STRPTR)afh, bufSize,
AFF_DISK | AFF_MEMORY | AFF_SCALED);
if (shortBy > 0) {
FreeMem(afh, bufSize);
bufSize += shortBy;
afh = NULL;
}
} while (!afh);
struct AvailFonts *af = &afh->afh_AF;
for (int i = 0; i < afh->afh_NumEntries; i++)
{
Printf("Font: %s, size %ld, type %s\n",
af[i].af_Attr.ta_Name,
af[i].af_Attr.ta_YSize,
(af[i].af_Type & AFF_DISK) ? "disk" : "ROM");
}
FreeMem(afh, bufSize);
Practical Cookbooks
Cookbook: Centered Title Bar
void DrawCenteredTitle(struct RastPort *rp, const char *title,
UWORD width, UWORD y)
{
struct TextFont *font = rp->Font;
UWORD textW = TextLength(rp, title, strlen(title));
WORD x = (width - textW) / 2;
SetDrMd(rp, JAM1);
SetAPen(rp, 1); /* foreground */
Move(rp, x, y + font->tf_Baseline);
Text(rp, title, strlen(title));
}
Cookbook: Multi-Font Label + Value
void DrawLabelValue(struct RastPort *rp,
struct TextFont *labelFont,
struct TextFont *valueFont,
const char *label, const char *value,
UWORD x, UWORD y)
{
/* Draw label in small font */
SetFont(rp, labelFont);
SetAPen(rp, 2);
SetDrMd(rp, JAM1);
Move(rp, x, y + labelFont->tf_Baseline);
Text(rp, label, strlen(label));
/* Draw value in large font, right after label */
UWORD labelW = TextLength(rp, label, strlen(label));
SetFont(rp, valueFont);
SetAPen(rp, 1);
Move(rp, x + labelW + 4, y + valueFont->tf_Baseline);
Text(rp, value, strlen(value));
}
Cookbook: Word-Wrap Text Block
/* Returns the number of lines rendered */
UWORD DrawWrappedText(struct RastPort *rp, const char *text,
UWORD maxWidth, UWORD startX, UWORD startY)
{
struct TextFont *font = rp->Font;
UWORD y = startY;
UWORD lineH = font->tf_YSize + 1; /* +1 for line spacing */
const char *lineStart = text;
const char *wordStart = text;
const char *bestBreak = text;
while (*wordStart)
{
/* Skip to end of word */
const char *wordEnd = wordStart;
while (*wordEnd && *wordEnd != ' ' && *wordEnd != '\n') wordEnd++;
/* Measure from lineStart to wordEnd */
UWORD w = TextLength(rp, lineStart, wordEnd - lineStart);
if (w > maxWidth && bestBreak > lineStart)
{
/* Break at the last good position */
Move(rp, startX, y + font->tf_Baseline);
Text(rp, lineStart, bestBreak - lineStart);
y += lineH;
lineStart = bestBreak + 1; /* skip the space */
bestBreak = lineStart;
}
else
{
bestBreak = wordEnd;
}
if (*wordEnd == '\n')
{
Move(rp, startX, y + font->tf_Baseline);
Text(rp, lineStart, wordEnd - lineStart);
y += lineH;
lineStart = wordEnd + 1;
bestBreak = lineStart;
wordEnd++;
}
wordStart = wordEnd;
while (*wordStart == ' ') wordStart++;
}
/* Final line */
if (lineStart < wordStart)
{
Move(rp, startX, y + font->tf_Baseline);
Text(rp, lineStart, strlen(lineStart));
y += lineH;
}
return (y - startY) / lineH;
}
Historical Context & Modern Analogies
Competitive Landscape
| Platform | Font System | Scalable? | Color Fonts? | Max Glyph Quality |
|---|---|---|---|---|
| AmigaOS 1.x | Bitmap (graphics.library) | No | No | Fixed-size pixel glyphs |
| AmigaOS 2.0+ | Bitmap + Compugraphic outlines | Yes (outlines) | No | Scalable via curves |
| AmigaOS 3.0+ | Bitmap + outlines + ColorFont | Yes | Yes (up to 8 planes) | Multi-color pixel glyphs |
| Mac System 1–6 | Bitmap "suitcase" + FOND resources | No | No | 72 DPI assumption |
| Mac System 7 (1991) | TrueType + bitmap fallbacks | Yes | No | Full curve rasterization |
| Windows 3.0 | Bitmap .FON |
No | No | System fixed-width |
| Windows 3.1 (1992) | TrueType + bitmap | Yes | No | WYSIWYG via TrueType |
| Atari ST TOS | 8×8 bitmap, GDOS optional | No (SpeedoGDOS in '91) | No | Minimal system font |
The Amiga was ahead of Mac and Windows in color fonts (OS 3.0, 1992) but behind in scalable font technology — Compugraphic outlines were less capable than Apple's TrueType.
Modern Analogies
| Amiga Concept | Modern Equivalent | Notes |
|---|---|---|
TextFont / TextAttr |
CTFont (macOS) / IDWriteFont (Windows) / PangoFontDescription |
Font description + loaded instance |
SetFont() |
CGContextSetFont() / SelectObject(hDC, hFont) |
Bind font to drawing context |
Text() |
CTLineDraw() / DrawText() / pango_layout_show() |
Render string at current position |
TextLength() |
CTLineGetTypographicBounds() / GetTextExtentPoint32() |
Measure without rendering |
tf_CharData bitmap strip |
Glyph atlas texture (game engines) | Same concept: all glyphs in one bitmap |
tf_CharLoc table |
Font atlas lookup table (UV coordinates) | Same concept: offset + width per glyph |
| Algorithmic bold | NSFontManager.convertWeight() |
Amiga smears pixels; modern re-strokes curves |
AvailFonts() |
NSFontManager.availableFonts / IDWriteFontCollection |
Enumerate installed fonts |
FONTS: assign |
/usr/share/fonts / C:\Windows\Fonts |
System font directory |
Best Practices
- Always use
OpenDiskFont()overOpenFont()— it loads from disk if needed;OpenFont()only finds already-loaded fonts - Check the return value —
OpenDiskFont()returnsNULLif the font doesn't exist; drawing with a NULL font crashes - Restore the original font if you borrowed a shared RastPort (e.g., window's RPort)
- Position at the baseline, not the top — use
font->tf_Baselineoffset - Use
TextLength()beforeText()for alignment and clipping - Respect user font preferences — use
screen->RastPort.Fontas the default - Check
FPF_DESIGNEDbefore relying on auto-scaled font quality - Open font, set font, draw, close font — don't hold fonts open longer than needed
- Use
JAM2with a matchingBgPenfor text that must be readable over any background - Don't assume ASCII 32–127 — check
tf_LoChar/tf_HiCharbefore indexing
Named Antipatterns
"The Baseline Blind Spot" — Positioning at the Top Instead of Baseline
/* BAD: Positioning at the top of the character cell —
descenders on g, j, p, q, y clip into the next line */
Move(rp, 10, 50); /* y=50 is the TOP of the cell */
Text(rp, "jumpy", 5);
/* CORRECT: Position at the baseline */
Move(rp, 10, 50 + font->tf_Baseline);
Text(rp, "jumpy", 5);
"The Font Leak" — Forgetting CloseFont
/* BAD: OpenDiskFont increments the accessor count.
Forgetting CloseFont leaks the font in memory forever. */
struct TextFont *font = OpenDiskFont(&ta);
SetFont(rp, font);
/* ... draw stuff ... */
/* Missing CloseFont()! Font stays loaded. */
/* CORRECT: Always pair OpenDiskFont/OpenFont with CloseFont */
struct TextFont *font = OpenDiskFont(&ta);
if (font) {
struct TextFont *oldFont = rp->Font;
SetFont(rp, font);
/* ... draw stuff ... */
SetFont(rp, oldFont); /* restore */
CloseFont(font);
}
"The Scale Surprise" — Assuming Auto-Scaled Fonts Look Good
/* BAD: Requesting a size that doesn't exist, assuming clean rendering */
struct TextAttr ta = {"topaz.font", 48, 0, 0};
struct TextFont *huge = OpenDiskFont(&ta);
/* topaz has no 48-pixel design — you get a scaled-up 8-pixel bitmap.
It looks like pixelated garbage. */
/* CORRECT: Check FPF_DESIGNED and warn/fallback */
struct TextFont *font = OpenDiskFont(&ta);
if (font && !(font->tf_Flags & FPF_DESIGNED)) {
/* Font was auto-scaled — consider using a different size
or an outline font (Compugraphic) for better quality */
}
"The Proportional Trap" — Assuming Fixed Width
/* BAD: Calculating text width by multiplying character count */
UWORD badWidth = strlen(title) * font->tf_XSize;
/* WRONG for proportional fonts (helvetica, times, etc.) */
/* CORRECT: Always measure with TextLength */
UWORD goodWidth = TextLength(rp, title, strlen(title));
Pitfalls & Common Mistakes
1. NULL Font After Failed OpenDiskFont
Symptom: System crash or garbage rendering after SetFont(rp, NULL).
Cause: OpenDiskFont() returns NULL when the font doesn't exist. Passing NULL to SetFont() causes undefined behavior.
Fix: Always check the return value:
struct TextFont *font = OpenDiskFont(&ta);
if (!font) {
/* Fallback to a ROM font */
struct TextAttr fallback = {"topaz.font", 8, 0, FPF_ROMFONT};
font = OpenFont(&fallback);
}
if (font) SetFont(rp, font);
2. Color Font Memory Exhaustion
Symptom: System runs out of Chip RAM after loading several large color fonts.
Cause: Each color font bitplane is a full copy of the glyph bitmap. An 8-plane color font at 24 pixels with 256 characters uses roughly 24 × 256/8 × 8 = ~49 KB of Chip RAM per font. Multiple fonts multiply this.
Fix: Load color fonts only when needed and close them immediately after use. Prefer monochrome fonts for large text blocks.
3. Aspect Ratio Distortion Across Screen Modes
Symptom: Text looks squished or stretched when opening a window on a different resolution screen than the font was designed for.
Cause: FPF_TALLDOT/FPF_WIDEDOT flags indicate the font's native pixel aspect. The renderer does not auto-compensate.
Fix: Check the screen mode's aspect ratio and choose a font designed for that mode, or accept the distortion.
4. Font Name Case Sensitivity
Symptom: OpenDiskFont() fails even though the font exists on disk.
Cause: Font names are case-sensitive on the Amiga filesystem. "Topaz.font" ≠ "topaz.font".
Fix: Always use lowercase font names (the standard convention):
/* WRONG: */
struct TextAttr ta = {"Topaz.font", 8, 0, 0}; /* uppercase T */
/* CORRECT: */
struct TextAttr ta = {"topaz.font", 8, 0, 0}; /* lowercase */
FAQ
Q: Can I render rotated text?
A: No — the Text() function only renders horizontally. Rotated text requires custom glyph-by-glyph blitting with coordinate transforms. Compugraphic outlines in OS 2.0+ were planned to support rotation but the API was never exposed.
Q: What is tf_BoldSmear?
A: The number of pixels the renderer shifts and ORs the glyph rightward to produce algorithmic bold. Usually 1. The smear makes characters slightly wider — account for this in width calculations.
Q: Can I use TrueType fonts? A: Not natively. AmigaOS uses bitmap and Compugraphic outlines. Third-party solutions like TrueDot, TypeManager, and AmigaOS 4's bullet.library add TrueType support. For retro development, stick with bitmap fonts.
Q: What happens if I call Text() with characters outside tf_LoChar–tf_HiChar?
A: The renderer substitutes a default glyph (usually the character for tf_LoChar). It does not crash, but you get wrong characters.
Q: How do I change the system font for all new windows?
A: Use the Font Preferences editor (Prefs → Font). The user's selection is stored in ENV:sys/font.prefs. Applications should read screen->RastPort.Font rather than hardcoding a specific font.
Q: What is the difference between OpenFont() and OpenDiskFont()?
A: OpenFont() (graphics.library) only finds fonts already loaded in memory. OpenDiskFont() (diskfont.library) can also load from disk and scale. Always prefer OpenDiskFont().
References
NDK Headers
graphics/text.h—TextFont,TextAttr,TextExtent, style/flag constantsgraphics/rastport.h— RastPort font-related fieldsgraphics/gfx.h—ColorTextFont,ColorFontColorslibraries/diskfont.h—DiskFontHeader,AvailFonts
Autodocs
- ADCD 2.1: graphics.library text functions
- ADCD 2.1: diskfont.library font loading
Related Knowledge Base Articles
- diskfont.md — font file format, disk loading pipeline, ColorFont memory layout
- rastport.md — RastPort text rendering context and drawing modes
- bitmap.md — BitMap structure (font glyph data is a BitMap)
- blitter.md — hardware that performs the actual glyph blitting
- console.md — console text rendering uses these fonts
- utility.md —
Hookstructure (used by font enumeration)