font: the console draws outlines now, and a byte could not have held Ą
"Ą Ę Ś Ł Ż" drew as "?????" and the obvious diagnosis -- the font has no glyphs for them -- was wrong twice over. `struct vtcell.ch` was **one byte** holding a CP437 index, and the console mapped UTF-8 down to it on the way in. U+0104 does not fit in a byte. No font could have fixed that; the cell had to get wider first. So: `ch` is a uint32 codepoint, the mapping on the way in is gone, and the cell draws through kernel/dev/ttf.c against JetBrains Mono linked into the image. Cell size comes from the font's measured advance instead of a hardcoded 8x16. The grid costs 864 KB of BSS instead of 432 KB, which is the honest price of holding a codepoint per cell for six terminals at 240x75. The rasteriser needed three fixes before it could be trusted with this. **21 KB of locals on a 4 KB kernel stack.** `ttf_render` held an outline (512 points as three arrays) and a raster (1024 edges) as automatics. KSTACKSIZE is 4096. They walked off the bottom of the stack into whatever was underneath, which surfaced as user processes faulting on addresses they never touched and then a panic inside freevm() on an overwritten page directory. Nothing pointed anywhere near a font. **And then again, recursively.** load_outline() held a 4736-byte outline as a local *and recursed* for composite glyphs. Composite glyphs are exactly Ą, Ę, Ś, Ł, Ż -- so the second crash arrived the moment the first fix let Unicode reach the screen, looking like a fresh bug. Now a static outline per recursion level with a depth cap of 4 (the font's deepest real nesting is 2), and the leaf parse split into load_simple() so its 512-byte flag array is not multiplied by the depth. Worst chain measured from the object file: ~2.3 KB. Neither showed in tools/ttftest, which has an 8 MB stack. That is the standing hazard of one source file built for host and kernel: the host is most forgiving about exactly the resource the kernel is strictest about. **Component transforms were being skipped**, on the stated theory that a wrong transform is worse than none. It is not. `‘` in this font is the comma glyph with a scale of -1 -- a 180-degree rotation -- and the offset that ships with it only lands correctly after the rotation. Without it the mark went twelve pixels into a ten-pixel cell and was clipped away entirely, so the character read as missing from the font rather than as misplaced. F2Dot14 2x2 now, with SCALED_COMPONENT_OFFSET honoured. The glyph cache was ASCII-only, so every accented letter re-rasterised on every repaint: a screenful of Polish did hundreds of bezier flattenings per scroll where the same screenful of English did none. 95 reserved ASCII slots plus 224 shared slots keyed by codepoint, 64 KB pool. Verified 13680 cached renders byte-identical to direct ones across collisions and a pool flush. tools/ttftest speaks UTF-8 now. Iterating bytes meant it could only ever ask for ASCII, and ASCII is entirely simple glyphs -- it could not reach the code path that mattered. Also: resolvconf's success line moves behind /proc/bootmode, same rule as vgpud. A quiet boot opened with "resolvconf: nameserver 10.0.2.3" where the one line was supposed to be "Welcome to SchOS!". Failures stay loud. Verified on screen, not just on the host: Ą Ę Ś Ł Ż ć ń ó ź, Ω λ π Œ Ð Æ ß, ∞ ‘x’ → ≤ all render, panic=0 across four boots. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
ca28978995
commit
5618164754
11 changed files with 568 additions and 132 deletions
|
|
@ -99,9 +99,36 @@ So the text layer fixes three things:
|
|||
|
||||
### The TrueType rasteriser
|
||||
|
||||
`kernel/dev/ttf.c`: `glyf` outlines, composite glyphs, `cmap` format 4,
|
||||
anti-aliased scanline fill, with a host harness at `tools/ttftest.c`. A
|
||||
direct-mapped glyph cache over ASCII in a 48 KB pool.
|
||||
`kernel/dev/ttf.c`: `glyf` outlines, composite glyphs with their component
|
||||
transforms, `cmap` format 4, anti-aliased scanline fill, with a host harness at
|
||||
`tools/ttftest.c`. A direct-mapped glyph cache in a 64 KB pool: one reserved
|
||||
slot per printable ASCII character, plus 224 shared slots keyed by codepoint
|
||||
for everything above it.
|
||||
|
||||
**It is the console font.** The regular weight is linked into the kernel image
|
||||
by the `consfont` rule in the Makefile and opened at 16 px before `vt_init()`,
|
||||
which is why `gfx_font_init()` prints nothing — there is no terminal to print
|
||||
to yet. A failure there is not fatal; the 8x16 bitmap font is still the
|
||||
fallback, and `gfx_font_ready()` says which one is in use.
|
||||
|
||||
**Two bugs here were the same bug.** `KSTACKSIZE` is 4096, and both the render
|
||||
path and the composite-glyph path put multi-kilobyte outlines on it as locals.
|
||||
They corrupted whatever was below the stack, which surfaced as user processes
|
||||
faulting on addresses they never touched and a panic inside `freevm()` — never
|
||||
anywhere near a font. Neither showed in the host harness, which has an 8 MB
|
||||
stack. That is the standing hazard of a rasteriser built from one source for
|
||||
both: the host is most forgiving about exactly the resource the kernel is
|
||||
strictest about. The outlines are `static` now, one per recursion level, and
|
||||
`load_simple()` is split out so the leaf's 512-byte flag array is not
|
||||
multiplied by the recursion depth.
|
||||
|
||||
**Component transforms are applied, not skipped.** An earlier version read a
|
||||
composite component's offset and stepped over its scale, reasoning that a wrong
|
||||
transform is worse than none. The left single quote in JetBrains Mono is the
|
||||
comma glyph with a scale of -1 — a 180-degree rotation — and its offset only
|
||||
makes sense after the rotation. Dropping the scale put the mark twelve pixels
|
||||
into a ten-pixel cell, where it was clipped away completely, so the character
|
||||
read as missing from the font rather than as misplaced.
|
||||
|
||||
**The typefaces**, both licence-checked at source:
|
||||
|
||||
|
|
@ -126,8 +153,9 @@ looking at is showing them a developer's log, and that single fact is most of
|
|||
what makes an OS look like a hobby project. Everything still goes out the UART.
|
||||
`console_takeover()` clears the flag on the first `exec()`.
|
||||
|
||||
The console still uses the 8×16 cell grid. Porting it onto the text layer and
|
||||
deleting the grid is open work.
|
||||
The console uses a cell grid whose size comes from the console font's measured
|
||||
advance rather than a hardcoded 8x16. Porting it onto the text layer's
|
||||
proportional path and deleting the grid is open work.
|
||||
|
||||
Two things the ANSI parser got wrong until a large program nobody here wrote
|
||||
exercised it properly:
|
||||
|
|
@ -249,8 +277,10 @@ makes it mean something.
|
|||
|
||||
## What it does not do
|
||||
|
||||
- **The console still has its cell grid.** The text layer exists and the console
|
||||
does not use it; the rasteriser exists and no font is loaded from disk.
|
||||
- **The console still has its cell grid.** Cells are now a Unicode codepoint
|
||||
drawn by the TrueType rasteriser rather than a CP437 byte drawn from a bitmap,
|
||||
and the cell size follows the font's measured advance — but it is still a
|
||||
fixed grid, and the text layer's proportional path is unused by it.
|
||||
- **No mode setting.** Resolution is whatever the loader asked for. There is no
|
||||
`/dev/dri`-style device and no way to change it at runtime.
|
||||
- **No double buffering in practice.** `display_ops.acquire` is specified and
|
||||
|
|
@ -262,8 +292,13 @@ makes it mean something.
|
|||
path. TrueType hinting is deliberately out of scope.
|
||||
- **No text layout library.** Advance widths exist per glyph; kerning, shaping
|
||||
and line breaking do not.
|
||||
- **The glyph cache covers ASCII only**, direct-mapped. A codepoint outside it
|
||||
is rasterised every time.
|
||||
- **The glyph cache has 224 slots above ASCII**, direct-mapped by codepoint
|
||||
modulo that. One European language fits without collisions; mixing scripts
|
||||
costs collisions, and a full pool is emptied and refilled rather than evicted
|
||||
entry by entry, because a linear pool cannot reclaim one glyph's bytes.
|
||||
- **No composite point-matching.** Components positioned by matching a point in
|
||||
the base rather than by an offset are placed at the origin. Fonts that use it
|
||||
are rare and none in the tree do.
|
||||
- **PSOGM renders and stops.** The interactive crash mode described in
|
||||
[../plan/roadmap.md](../plan/roadmap.md) — walk the stack, read another
|
||||
process's memory, dump page tables — is designed and not built.
|
||||
|
|
|
|||
|
|
@ -45,23 +45,29 @@ switched away from mid-sequence comes back wrong.
|
|||
|
||||
## A cell
|
||||
|
||||
Four bytes:
|
||||
Eight bytes:
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| `ch` | the glyph the font draws — **not** a codepoint |
|
||||
| `ch` | the Unicode codepoint, 32 bits |
|
||||
| `fg`, `bg` | palette indices, 0-15 ANSI plus two theme defaults |
|
||||
| `attr` | reverse video; room for underline and the rest |
|
||||
| `pad` | to eight, so the grid indexes without a multiply |
|
||||
|
||||
`ch` stores the glyph rather than the codepoint because the console maps UTF-8
|
||||
to CP437 before it gets here, and that is what `include/font.h` contains.
|
||||
Storing the codepoint would mean re-running the mapping on every repaint and
|
||||
getting a different answer for anything the font cannot draw.
|
||||
`ch` was one byte holding a CP437 glyph, not a codepoint: the console mapped
|
||||
UTF-8 down to CP437 on the way in, because that is what `include/font.h`
|
||||
contained. Widening it to the codepoint is what made Ą, Ę, Ś, Ω and the rest
|
||||
possible, and no font change could have done it alone — a byte cannot *hold*
|
||||
U+0104 whatever is drawing it. Mapping on the way in also meant the decision
|
||||
was made once, at write time, and a later font could not revisit it.
|
||||
|
||||
The colours are indices rather than RGB because a cell holding two 32-bit
|
||||
colours would be twelve bytes instead of four, and the grids are static: six
|
||||
terminals at 240x75 is 432 KB of BSS, paid once at link time rather than failed
|
||||
to pay at run time. There is no contiguous multi-page allocator to ask.
|
||||
The cost is real: the grid went from four bytes per cell to eight, and six
|
||||
terminals at 240x75 is 864 KB of BSS rather than 432 KB. Paid once at link
|
||||
time, which is the only way to pay it — there is no contiguous multi-page
|
||||
allocator to ask.
|
||||
|
||||
The colours stay indices rather than RGB, for the same reason they always
|
||||
were: a cell holding two 32-bit colours would be sixteen bytes.
|
||||
|
||||
## What is per-terminal
|
||||
|
||||
|
|
@ -316,5 +322,16 @@ waiting for a different editor.
|
|||
- **Only 240x75 of any screen.** Larger framebuffers work; the grid is clamped
|
||||
and the remainder stays background. Raising `VT_MAXCOLS`/`VT_MAXROWS` is the
|
||||
fix, and costs BSS.
|
||||
- **No UTF-8 the font lacks.** CP437 has ~256 glyphs. Anything else draws as
|
||||
`?`, which is deliberate: drawing a *different* character would be worse.
|
||||
- **No UTF-8 the font lacks.** The console draws through the TrueType
|
||||
rasteriser now, so the limit is the font's `cmap` rather than a 256-entry
|
||||
table — Latin, Latin Extended, Greek, Cyrillic and the common punctuation and
|
||||
maths symbols all render. Anything genuinely absent still draws as `?`, which
|
||||
is deliberate: drawing a *different* character would be worse.
|
||||
- **No shaping, and no wide characters.** One codepoint is one cell of one
|
||||
advance. Combining marks land on top of their own cell rather than the
|
||||
preceding one, and CJK draws at half the width it wants. Both need a
|
||||
width table and a cell that can span two columns.
|
||||
- **Typed input above U+07FF is unreliable.** Two-byte UTF-8 arrives intact;
|
||||
three-byte sequences typed at the shell come through mangled. The console's
|
||||
own decoder is not the problem — the same characters `cat`ed from a file
|
||||
render correctly — so this belongs with keyboard and locale handling.
|
||||
|
|
|
|||
Loading…
Reference in a new issue