hachem.wtf
aboutsummaryrefslogtreecommitdiff
path: root/kernel/ttf.rv
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/ttf.rv')
-rw-r--r--kernel/ttf.rv459
1 files changed, 294 insertions, 165 deletions
diff --git a/kernel/ttf.rv b/kernel/ttf.rv
index 17d1a19..fb1bab7 100644
--- a/kernel/ttf.rv
+++ b/kernel/ttf.rv
@@ -6,84 +6,120 @@
# 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_mod(value, modulus) -> num do
+ return value - math.floor(value / modulus) * modulus
+end
+
+fn ttf_u8(data, offset) -> num do
+ return mem_read(data + offset, 1)
+end
-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
+fn ttf_u16(data, offset) -> num do
+ return mem_read(data + offset, 1) * 256 + mem_read(data + offset + 1, 1)
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
+fn ttf_i16(data, offset) -> num do
+ const value = ttf_u16(data, offset)
+ if (value >= 32768)
+ return value - 65536
+ return value
+end
+
+fn ttf_u32(data, offset) -> num do
+ return ((mem_read(data + offset, 1) * 256 + mem_read(data + offset + 1, 1)) * 256 + mem_read(data + offset + 2, 1)) * 256 + mem_read(data + offset + 3, 1)
+end
+
+fn ttf_tag_eq(data, offset, tag) -> bool do
+ for i in 0..4 do
+ if (mem_read(data + offset + 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)
+fn ttf_table(data, tag) -> num do
+ const count = ttf_u16(data, 4)
+ for i in 0..count do
+ const record = 12 + i * 16
+ if (ttf_tag_eq(data, record, tag))
+ return ttf_u32(data, record + 8)
end
return -1
end
-fn ttf_cmap_sub(d, cmap_off) do
- const n = ttf_u16(d, cmap_off + 2)
+fn ttf_cmap_sub(data, cmap_off) -> num do
+ const count = ttf_u16(data, 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
+
+ for i in 0..count do
+ const record = cmap_off + 4 + i * 8
+ const platform = ttf_u16(data, record)
+ const encoding = ttf_u16(data, record + 2)
+ if (platform == 3 and (encoding == 1 or encoding == 0)) or platform == 0 do
+ best = cmap_off + ttf_u32(data, record + 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
+fn ttf_glyph_of(data, subtable, code) -> num do
+ if (ttf_u16(data, subtable) != 4)
+ return 0
+
+ const segment_count = ttf_u16(data, subtable + 6) / 2
+ const end_base = subtable + 14
+ const start_base = end_base + segment_count * 2 + 2
+ const delta_base = start_base + segment_count * 2
+ const range_base = delta_base + segment_count * 2
+
+ for i in 0..segment_count do
+ if code <= ttf_u16(data, end_base + i * 2) do
+ const start_code = ttf_u16(data, start_base + i * 2)
+ if (code < start_code)
+ return 0
+
+ const idro = ttf_u16(data, range_base + i * 2)
+ if (idro == 0)
+ return (code + ttf_i16(data, delta_base + i * 2)) band 0xFFFF
+
+ const glyph_id = ttf_u16(data, range_base + i * 2 + idro + (code - start_code) * 2)
+ if (glyph_id == 0)
+ return 0
+
+ return (glyph_id + ttf_i16(data, delta_base + 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
+fn ttf_glyph_offset(data, loca, long, glyph_id) -> num do
+ if (long)
+ return ttf_u32(data, loca + glyph_id * 4)
+ return ttf_u16(data, loca + glyph_id * 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)
+fn ttf_advance(data, hmtx, num_hmetrics, glyph_id) -> num do
+ if (glyph_id < num_hmetrics)
+ return ttf_u16(data, hmtx + glyph_id * 4)
+ return ttf_u16(data, hmtx + (num_hmetrics - 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_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
+fn ttf_add_quad(edges, x0, y0, cx, cy, x1, y1, steps) do
let px = x0
let py = y0
- for i in 1..(n + 1) do
- const t = i / n
+
+ for i in 1..(steps + 1) do
+ const t = i / steps
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
@@ -91,196 +127,289 @@ fn ttf_add_quad(edges, x0, y0, cx, cy, x1, y1, n) do
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 })
+ const point_count = pts:len()
+ if (point_count < 2)
+ return :nil
+
+ let expanded = {}
+ for i in 0..point_count do
+ expanded:push(pts[i])
+ const j = ttf_mod(i + 1, point_count)
+
+ if pts[i].on == :false and pts[j].on == :false do
+ expanded: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
+
+ const expanded_count = expanded:len()
+ let start = -1
+ for i in 0..expanded_count do
+ if (start < 0 and expanded[i].on)
+ start = i
+ end
+
+ if (start < 0)
+ return :nil
+
+ let cx = expanded[start].x
+ let cy = expanded[start].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
+
+ while (k <= expanded_count) do
+ const point = expanded[ttf_mod(start + k, expanded_count)]
+
+ if point.on do
+ ttf_add_line(edges, cx, cy, point.x, point.y)
+ cx = point.x
+ cy = point.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
+ end else do
+ const endpoint = expanded[ttf_mod(start + k + 1, expanded_count)]
+ ttf_add_quad(edges, cx, cy, point.x, point.y, endpoint.x, endpoint.y, nsub)
+ cx = endpoint.x
+ cy = endpoint.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
+fn ttf_glyph_edges(data, glyf, loca, long, glyph_id, scale, ox, baseline, edges, nsub) do
+ const offset = ttf_glyph_offset(data, loca, long, glyph_id)
+ const next = ttf_glyph_offset(data, loca, long, glyph_id + 1)
+
+ # empty glyph
+ if (next <= offset)
+ return :nil
+ const base = glyf + offset
+ const contour_count = ttf_i16(data, base)
+
+ # skip
+ if (contour_count < 0)
+ return :nil
+
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
+ for i in 0..contour_count do ends:push(ttf_u16(data, base + 10 + i * 2)) end
+
+ const point_count = ends[contour_count - 1] + 1
+ let pos = base + 10 + contour_count * 2
+
+ pos += 2 + ttf_u16(data, pos) # 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
+ while (flags:len() < point_count) do
+ const flag = ttf_u8(data, pos)
+
+ pos += 1
+ flags:push(flag)
+
+ if (flag band 0x08) != 0 do
+ let repeat = ttf_u8(data, pos)
+ pos += 1
+
+ while (repeat > 0) do
+ flags:push(flag)
+ repeat -= 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
+
+ for i in 0..point_count do
+ const flag = flags[i]
+
+ if (flag band 0x02) != 0 do
+ const dx = ttf_u8(data, pos)
+ pos += 1
+
+ if (flag band 0x10) != 0 do
+ x += dx
+ end else do
+ x -= dx
+ end
+ end else do
+ if (flag band 0x10) == 0 do
+ x += ttf_i16(data, pos)
+ pos += 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
+
+ for i in 0..point_count do
+ const flag = flags[i]
+
+ if (flag band 0x04) != 0 do
+ const dy = ttf_u8(data, pos)
+ pos += 1
+
+ if (flag band 0x20) != 0 do
+ y += dy
+ end else do
+ y -= dy
+ end
+ end else do
+ if (flag band 0x20) == 0 do
+ y += ttf_i16(data, pos)
+ pos += 2
+ end
end
+
ys:push(y)
end
+
# split into contours
let start = 0
- for ci in 0..nc do
+
+ for ci in 0..contour_count 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
+fn ttf_fill(edges, width, height) -> table do
let rows = {}
- for py in 0..h do
+
+ for py in 0..height 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
+
+ for i in 0..edges:len() do
+ const edge = edges[i]
+ const y0 = edge.ay
+ const y1 = edge.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 })
+ let direction = 1
+
+ if y1 < y0 do
+ direction = -1
+ end
+
+ cross:push({ x = edge.ax + t * (edge.bx - edge.ax), dir = direction })
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
+ for i in 1..cross:len() do
+ const key = cross[i]
+ let j = i - 1
+ while (j >= 0 and cross[j].x > key.x) do
+ cross[j + 1] = cross[j]
+ j -= 1
end
- cross[b + 1] = key
+
+ cross[j + 1] = key
end
+
let bits = 0
- let wind = 0
+ let winding = 0
+
for k in 0..cross:len() do
- wind += cross[k].dir
- if (wind != 0 and k + 1 < cross:len()) do
+ winding += cross[k].dir
+
+ if (winding != 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
+ for px in 0..width 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
+ const midpoint = math.floor((xa + xb) / 2)
+ if (midpoint >= 0 and midpoint < width)
+ bits = bits bor (1 shl midpoint)
end
end
end
end
+
rows:push(bits)
end
+
return rows
end
-global ttf_io = { buf = 0, pages = 512 }
+global ttf_io = { buffer = 0, pages = 512 }
-global load_ttf = fn(path, size) do
+global load_ttf = fn(path, size) -> { glyphs: table, width: num, height: num } | :nil 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)
+ if (total < 12)
+ return :nil
+
+ if ttf_io.buffer == 0 do
+ ttf_io.buffer = alloc_contig(ttf_io.pages)
+ end
+ if (ttf_io.buffer == 0)
+ return :nil
+ if (total > ttf_io.pages * 4096)
+ return :nil
+
+ fs:read_into(path, ttf_io.buffer)
+
+ const data = ttf_io.buffer
+ const head = ttf_table(data, "head")
+ const maxp = ttf_table(data, "maxp")
+ const cmap = ttf_table(data, "cmap")
+ const loca = ttf_table(data, "loca")
+ const glyf = ttf_table(data, "glyf")
+ const hhea = ttf_table(data, "hhea")
+ const hmtx = ttf_table(data, "hmtx")
+
+ if (head < 0 or cmap < 0 or loca < 0 or glyf < 0 or hhea < 0)
+ return :nil
+
+ const upm = ttf_u16(data, head + 18)
+ if (upm == 0)
+ return :nil
+
+ const long = ttf_i16(data, head + 50) == 1
+ const ascent = ttf_i16(data, hhea + 4)
+ const descent = ttf_i16(data, hhea + 6)
+ const num_hmetrics = ttf_u16(data, 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 subtable = ttf_cmap_sub(data, cmap)
+ if (subtable < 0)
+ return :nil
+
+ const mgate = ttf_glyph_of(data, subtable, 77) # 'M'
+ let width = math.floor(ttf_advance(data, hmtx, num_hmetrics, mgate) * scale) + 1
+ if (width < 2)
+ width = math.floor(size * 0.6) + 1
+
const nsub = 5
let glyphs = {}
+
for code in 32..128 do
- const g = ttf_glyph_of(d, sub, code)
+ const glyph_id = ttf_glyph_of(data, subtable, code)
let edges = {}
- ttf_glyph_edges(d, glyf, loca, long, g, scale, 1, baseline, edges, nsub)
+
+ ttf_glyph_edges(data, glyf, loca, long, glyph_id, scale, 1, baseline, edges, nsub)
glyphs:push(ttf_fill(edges, width, height))
end
+
return { glyphs = glyphs, width = width, height = height }
end