From e55291a32a8b3f7b5c105cd79e86f3c19f19b3d4 Mon Sep 17 00:00:00 2001 From: hachem Date: Wed, 7 Oct 2026 21:43:38 +0200 Subject: feat: tty and ttf, you'd think somethn starting with tt would be fun to do... --- kernel/ttf.rv | 286 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 286 insertions(+) create mode 100644 kernel/ttf.rv (limited to 'kernel/ttf.rv') diff --git a/kernel/ttf.rv b/kernel/ttf.rv new file mode 100644 index 0000000..17d1a19 --- /dev/null +++ b/kernel/ttf.rv @@ -0,0 +1,286 @@ +# so many fucking hours wasted man i hate fonts so much +# fuck bezier +# fuck revo +# fuck revel +# fuck ttf +# fuck lung +# fuck me +# im like 90% confident this works, but im like 10% confident its fucked +fn ttf_mod(a, b) a - math.floor(a / b) * b + +fn ttf_u8(d, o) mem_read(d + o, 1) +fn ttf_u16(d, o) mem_read(d + o, 1) * 256 + mem_read(d + o + 1, 1) +fn ttf_i16(d, o) do + const v = ttf_u16(d, o) + if v >= 32768 return v - 65536 + return v +end +fn ttf_u32(d, o) ((mem_read(d + o, 1) * 256 + mem_read(d + o + 1, 1)) * 256 + mem_read(d + o + 2, 1)) * 256 + mem_read(d + o + 3, 1) + +fn ttf_tag_eq(d, o, tag) do + for i in 0..4 do if mem_read(d + o + i, 1) != tag[i]:ascii() return :false end + return :true +end + +fn ttf_table(d, tag) do + const n = ttf_u16(d, 4) + for i in 0..n do + const rec = 12 + i * 16 + if ttf_tag_eq(d, rec, tag) return ttf_u32(d, rec + 8) + end + return -1 +end + +fn ttf_cmap_sub(d, cmap_off) do + const n = ttf_u16(d, cmap_off + 2) + let best = -1 + for i in 0..n do + const rec = cmap_off + 4 + i * 8 + const plat = ttf_u16(d, rec) + const enc = ttf_u16(d, rec + 2) + if (plat == 3 and (enc == 1 or enc == 0)) or plat == 0 do best = cmap_off + ttf_u32(d, rec + 4) end + end + return best +end + +fn ttf_glyph_of(d, sub, c) do + if ttf_u16(d, sub) != 4 return 0 + const segCount = ttf_u16(d, sub + 6) / 2 + const endBase = sub + 14 + const startBase = endBase + segCount * 2 + 2 + const deltaBase = startBase + segCount * 2 + const rangeBase = deltaBase + segCount * 2 + for i in 0..segCount do + if c <= ttf_u16(d, endBase + i * 2) do + const startC = ttf_u16(d, startBase + i * 2) + if c < startC return 0 + const idro = ttf_u16(d, rangeBase + i * 2) + if idro == 0 return (c + ttf_i16(d, deltaBase + i * 2)) band 0xFFFF + const g = ttf_u16(d, rangeBase + i * 2 + idro + (c - startC) * 2) + if g == 0 return 0 + return (g + ttf_i16(d, deltaBase + i * 2)) band 0xFFFF + end + end + return 0 +end + +fn ttf_glyph_offset(d, loca, long, g) do + if long return ttf_u32(d, loca + g * 4) + return ttf_u16(d, loca + g * 2) * 2 +end + +fn ttf_advance(d, hmtx, numH, g) do + if g < numH return ttf_u16(d, hmtx + g * 4) + return ttf_u16(d, hmtx + (numH - 1) * 4) +end + +fn ttf_add_line(edges, x0, y0, x1, y1) edges:push({ ax = x0, ay = y0, bx = x1, by = y1 }) + +fn ttf_add_quad(edges, x0, y0, cx, cy, x1, y1, n) do + let px = x0 + let py = y0 + for i in 1..(n + 1) do + const t = i / n + const mt = 1 - t + const qx = mt * mt * x0 + 2 * mt * t * cx + t * t * x1 + const qy = mt * mt * y0 + 2 * mt * t * cy + t * t * y1 + ttf_add_line(edges, px, py, qx, qy) + px = qx + py = qy + end +end + +fn ttf_contour(pts, edges, nsub) do + const m = pts:len() + if m < 2 return :nil + let exp = {} + for i in 0..m do + exp:push(pts[i]) + const j = ttf_mod(i + 1, m) + if (pts[i].on == :false and pts[j].on == :false) do + exp:push({ x = (pts[i].x + pts[j].x) / 2, y = (pts[i].y + pts[j].y) / 2, on = :true }) + end + end + const n = exp:len() + let s = -1 + for i in 0..n do if (s < 0 and exp[i].on) do s = i end end + if s < 0 return :nil + let cx = exp[s].x + let cy = exp[s].y + let k = 1 + while k <= n do + const a = exp[ttf_mod(s + k, n)] + if a.on do + ttf_add_line(edges, cx, cy, a.x, a.y) + cx = a.x + cy = a.y + k += 1 + end + else do + const b = exp[ttf_mod(s + k + 1, n)] + ttf_add_quad(edges, cx, cy, a.x, a.y, b.x, b.y, nsub) + cx = b.x + cy = b.y + k += 2 + end + end +end + +# how about you contour my fucking asshole +fn ttf_glyph_edges(d, glyf, loca, long, g, scale, ox, baseline, edges, nsub) do + const off = ttf_glyph_offset(d, loca, long, g) + const next = ttf_glyph_offset(d, loca, long, g + 1) + if next <= off return :nil # empty glyph + const base = glyf + off + const nc = ttf_i16(d, base) + if nc < 0 return :nil # skip + let ends = {} + for i in 0..nc do ends:push(ttf_u16(d, base + 10 + i * 2)) end + const npts = ends[nc - 1] + 1 + let p = base + 10 + nc * 2 + p += 2 + ttf_u16(d, p) # skip instructions + let flags = {} + while flags:len() < npts do + const f = ttf_u8(d, p) + p += 1 + flags:push(f) + if (f band 0x08) != 0 do + let rep = ttf_u8(d, p) + p += 1 + while rep > 0 do flags:push(f) rep -= 1 end + end + end + let xs = {} + let x = 0 + for i in 0..npts do + const f = flags[i] + if (f band 0x02) != 0 do + const dx = ttf_u8(d, p) + p += 1 + if (f band 0x10) != 0 do x += dx end else do x -= dx end + end + else do + if (f band 0x10) == 0 do x += ttf_i16(d, p) p += 2 end + end + xs:push(x) + end + let ys = {} + let y = 0 + for i in 0..npts do + const f = flags[i] + if (f band 0x04) != 0 do + const dy = ttf_u8(d, p) + p += 1 + if (f band 0x20) != 0 do y += dy end else do y -= dy end + end + else do + if (f band 0x20) == 0 do y += ttf_i16(d, p) p += 2 end + end + ys:push(y) + end + # split into contours + let start = 0 + for ci in 0..nc do + const stop = ends[ci] + let pts = {} + for i in start..(stop + 1) do + pts:push({ x = xs[i] * scale + ox, y = baseline - ys[i] * scale, on = (flags[i] band 0x01) != 0 }) + end + ttf_contour(pts, edges, nsub) + start = stop + 1 + end +end + +fn ttf_fill(edges, w, h) do + let rows = {} + for py in 0..h do + const yc = py + 0.5 + let cross = {} + for e in 0..edges:len() do + const ed = edges[e] + const y0 = ed.ay + const y1 = ed.by + if ((y0 <= yc and yc < y1) or (y1 <= yc and yc < y0)) do + const t = (yc - y0) / (y1 - y0) + let dir = 1 + if y1 < y0 do dir = -1 end + cross:push({ x = ed.ax + t * (ed.bx - ed.ax), dir = dir }) + end + end + # sort crossings left to right + # insertion sort + for a in 1..cross:len() do + const key = cross[a] + let b = a - 1 + while (b >= 0 and cross[b].x > key.x) do + cross[b + 1] = cross[b] + b -= 1 + end + cross[b + 1] = key + end + let bits = 0 + let wind = 0 + for k in 0..cross:len() do + wind += cross[k].dir + if (wind != 0 and k + 1 < cross:len()) do + const xa = cross[k].x + const xb = cross[k + 1].x + if xb > xa do + let any = :false + for px in 0..w do + if (px + 0.5 >= xa and px + 0.5 < xb) do bits = bits bor (1 shl px) any = :true end + end + if any == :false do + const mid = math.floor((xa + xb) / 2) + if (mid >= 0 and mid < w) do bits = bits bor (1 shl mid) end + end + end + end + end + rows:push(bits) + end + return rows +end + +global ttf_io = { buf = 0, pages = 512 } + +global load_ttf = fn(path, size) do + const total = fs:size(path) + if total < 12 return :nil + if ttf_io.buf == 0 do ttf_io.buf = alloc_contig(ttf_io.pages) end + if ttf_io.buf == 0 return :nil + if total > ttf_io.pages * 4096 return :nil + fs:read_into(path, ttf_io.buf) + const d = ttf_io.buf + const head = ttf_table(d, "head") + const maxp = ttf_table(d, "maxp") + const cmap = ttf_table(d, "cmap") + const loca = ttf_table(d, "loca") + const glyf = ttf_table(d, "glyf") + const hhea = ttf_table(d, "hhea") + const hmtx = ttf_table(d, "hmtx") + if (head < 0 or cmap < 0 or loca < 0 or glyf < 0 or hhea < 0) return :nil + const upm = ttf_u16(d, head + 18) + if upm == 0 return :nil + const long = ttf_i16(d, head + 50) == 1 + const ascent = ttf_i16(d, hhea + 4) + const descent = ttf_i16(d, hhea + 6) + const numH = ttf_u16(d, hhea + 34) + const scale = size / upm + const baseline = math.floor(ascent * scale) + let height = math.floor((ascent - descent) * scale) + 1 + const sub = ttf_cmap_sub(d, cmap) + if sub < 0 return :nil + const mgate = ttf_glyph_of(d, sub, 77) # 'M' + let width = math.floor(ttf_advance(d, hmtx, numH, mgate) * scale) + 1 + if width < 2 do width = math.floor(size * 0.6) + 1 end + const nsub = 5 + let glyphs = {} + for code in 32..128 do + const g = ttf_glyph_of(d, sub, code) + let edges = {} + ttf_glyph_edges(d, glyf, loca, long, g, scale, 1, baseline, edges, nsub) + glyphs:push(ttf_fill(edges, width, height)) + end + return { glyphs = glyphs, width = width, height = height } +end -- cgit v1.3