Carry project memory in the repo, with tools/memory-sync.sh
Claude Code keeps per-project memory at <home>/.claude/projects/<key>/,
where <key> is the project's absolute path with every separator and the
drive colon turned into a dash. That makes it both rename-fragile and
machine-specific, and this project has hit each of those once:
- D:\DEV\Schunix -> D:\DEV\SchOS changed the key, and 43 memories were
invisible until moved across by hand.
- Development happens from Windows and from Linux, and each looks in a
different place for the same checkout.
Committing .claude/memory/ fixes both -- git carries it across renames
and between hosts -- and `import` puts it wherever the local machine
expects. .claude/settings.local.json stays ignored: those are per-machine
permissions.
The colon maps to a dash rather than being dropped. Deleting it gives
D-DEV-SchOS, which matches nothing and silently finds zero memories; that
was the first attempt. On Git Bash the key comes from `cygpath -w`,
because Claude Code is a Windows program and sees D:\DEV\SchOS, not
/d/DEV/SchOS.
This commit is contained in:
parent
cda5908660
commit
3182da3965
47 changed files with 2810 additions and 0 deletions
86
.claude/memory/schunix-ttf-rasteriser.md
Normal file
86
.claude/memory/schunix-ttf-rasteriser.md
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
---
|
||||
name: schunix-ttf-rasteriser
|
||||
description: The TrueType rasteriser — why glyf and not CFF, and the composite-glyph trap that loses the letter "i".
|
||||
metadata:
|
||||
type: project
|
||||
---
|
||||
|
||||
Built 2026-08-09. `include/ttf.h` + `kernel/dev/ttf.c`, the second `font_ops`
|
||||
provider behind [[schunix-text-rendering]]. Host harness: `tools/ttftest.c` →
|
||||
`build/tools/ttftest font.ttf px "text" out.pgm`.
|
||||
|
||||
Renders real JetBrains Mono at 32 px: correct advances, anti-aliased edges,
|
||||
the slashed zero, dots on i and j.
|
||||
|
||||
## glyf, not CFF — and that was checked, not assumed
|
||||
|
||||
`.ttf` and `.otf` are both sfnt containers with the same table directory, cmap
|
||||
and hmtx. Only the *outlines* differ: TrueType puts quadratic B-splines in
|
||||
`glyf`; OpenType/PostScript puts cubic béziers in `CFF ` as Type 2 charstrings,
|
||||
a stack-based interpreted format and a separate project.
|
||||
|
||||
**Both chosen fonts ship `glyf` and neither ships `CFF `** — verified by dumping
|
||||
JetBrains Mono's table directory before writing a line. So CFF buys nothing
|
||||
today. `ttf_open()` rejects `OTTO` explicitly rather than failing later with an
|
||||
empty glyph and no explanation.
|
||||
|
||||
## The trap: composite glyphs are not an edge case
|
||||
|
||||
First working render produced **"Schun x"** — the `i` was missing. `i` is a
|
||||
*composite* glyph: dotless-i plus the dot, assembled from two other glyphs at
|
||||
offsets. Skipping composites (`numberOfContours < 0`) does not just lose
|
||||
accented characters, it loses a letter from the project's own name.
|
||||
|
||||
Now assembled: parse the component list (flags, glyph index, dx/dy, optional
|
||||
scale), recurse, merge contours with the offset applied. Only the
|
||||
**translation** case is handled — components with a scale or 2×2 matrix are
|
||||
skipped, because applying a transform wrongly is worse than not applying one.
|
||||
|
||||
## Other things that bite
|
||||
|
||||
- Every sfnt field is **big-endian**; read byte by byte, never cast. The file is
|
||||
not guaranteed aligned and a cast byte-swaps everything on x86.
|
||||
- Glyph flags are **run-length encoded** (bit 3 = "next byte is a repeat
|
||||
count"). Missing that turns any glyph with a repeated flag into garbage.
|
||||
- Coordinates are **deltas** in one of three encodings per axis, selected by two
|
||||
flag bits — including "same as previous", which occupies no bytes.
|
||||
- TrueType **omits the on-curve point between two consecutive off-curve
|
||||
points**; it must be synthesised at the midpoint or curves render as corners.
|
||||
- `hmtx` stores `numberOfHMetrics` full entries then bearings only — clamp the
|
||||
index.
|
||||
- cmap format 4's `idRangeOffset` is a byte offset **from its own address**,
|
||||
which is why that arithmetic looks the way it does.
|
||||
|
||||
## Glyph cache (added the same day)
|
||||
|
||||
Direct-mapped over printable ASCII 32..126 into a 48 KB pool, filled linearly,
|
||||
never freed. No hashing, no eviction policy, no allocation — the contents are a
|
||||
pure function of (codepoint, size), so nothing ever goes stale. A **size change
|
||||
drops the whole cache**, which is the only event that invalidates it.
|
||||
|
||||
It fixes two things, not one: the obvious cost of re-rasterising every glyph,
|
||||
and the contract — the single `ttf_cov` scratch buffer meant a glyph was valid
|
||||
only until the next call, so a caller could not hold two at once. Cached glyphs
|
||||
now point into the pool. Codepoints outside ASCII, and anything that arrives
|
||||
after the pool is full, still render correctly through the scratch buffer under
|
||||
the old contract.
|
||||
|
||||
`tools/ttftest.c` calls through `fo->ops->glyph`, not `ttf_render`, so the cache
|
||||
is on the tested path rather than bypassed by the harness.
|
||||
|
||||
## Still not done
|
||||
|
||||
- **No font loaded from disk.** The kernel compiles the rasteriser but no `.ttf`
|
||||
is in the tree and nothing reads one. This is what blocks pointing the console
|
||||
at it.
|
||||
- **Hinting ignored** (`fpgm`/`prep`/`cvt ` skipped). Fine above ~16 px, blurry
|
||||
below. Real hinting is a bytecode interpreter — a project, not a fix.
|
||||
- **No GSUB**, so ligatures do not form: JetBrains Mono's `==` renders as two
|
||||
glyphs. Correct, just not what the font can do.
|
||||
- **CFF/OTF outlines.** Requested 2026-08-09 for people who want a non-TrueType
|
||||
font in the terminal. Not started, and it is not "more of the same": Type 2
|
||||
charstrings are a stack-based bytecode to *interpret* — INDEX/DICT structures,
|
||||
charset, local and global subrs with the biasing rule, ~40 operators, cubic
|
||||
béziers. Roughly the size of everything else here, and a subtly wrong
|
||||
interpreter draws plausible-looking wrong glyphs. It plugs in as a third
|
||||
`font_ops` provider, so nothing above it changes.
|
||||
Loading…
Reference in a new issue