shithub: libdraw.myr

ref: d180b70c6c69ffe8168cc8571772ad6e15ac6aba
dir: /font.myr/

View raw version
use std
use bio
use iter

use "types"
use "image"
use "load"
use "pack"

pkg draw =
	const openfont	: (dpy : display#, name : byte[:] -> std.result(font#, err))

	pkglocal const loadsubfont	: (dpy : display#, sub : subfont# -> void)
	pkglocal const cachechar	: (font : font#, c : char -> int16)
;;

const openfont = {dpy, name
	var dir

	dir = std.dirname(name)
	match bio.open(name, bio.Rd)
	| `std.Ok f:	-> loadfont(dpy, dir, f)
	| `std.Err e:	-> `std.Err `Efont
	;;
}

const cachechar = {ft, c
	match std.htget(ft.cache, c)
	| `std.Some inf:
		inf.hits++
		-> inf.idx
	| `std.None:
		-> loadglyph(ft, c)
	;;
}

const loadglyph = {ft, c
	var r : rect
	var p : point
	var g, info

	for sub : iter.byref(ft.subf)
		if c < sub.lo || c >= sub.hi
			continue
		;;
		if sub.glyph.len == 0
			loadsubfont(ft.cimage.dpy, sub)
		;;
		/*
		  l cacheid[4] srcid[4] index[2] r[4*4] sp[2*4] left[1]
		  width[1]
		 */
		g = &sub.glyph[c - sub.lo]
		p = [.x=g.x, .y=0]
		r = [.x0=ft.x, .y0=0, .x1=ft.x + g.width, .y1=ft.ascent]
		pack(ft.cimage.dpy, "biisrpbb", 0, \
			('l' : byte), \
			ft.cimage.id, \
			sub.image.id, \
			ft.nextidx, \
			r, \
			p, \
			g.left, \
			g.width)
		flush(ft.cimage.dpy)
		info.hits = 0
		info.idx = ft.nextidx
		std.htput(ft.cache, c, info)
		ft.x += g.width
		ft.nextidx++
		-> info.idx
	;;
	-> -1
}

const loadfont = {dpy, dir, f
	var sp : byte[:][3]
	var lo, hi, start, n, r
	var font

	font = std.zalloc()
	match bio.readln(f)
	| `std.Ok ln:
		std.bstrtok(sp[:2], ln)
		font.height = std.getv(std.intparse(sp[0]), -1)
		font.ascent = std.getv(std.intparse(sp[0]), -1)
	| `std.Err `bio.Eof:
	| `std.Err e:
	;;

	for ln : bio.byline(f)
		n = 0
		start = 0
		std.bstrtok(sp[:3], ln)
		lo = std.getv(std.intparse(sp[n++]), -1)
		hi = std.getv(std.intparse(sp[n++]), -1)
		if sp.len == 4
			start = std.getv(std.intparse(sp[n++]), -1)
		;;
		std.slpush(&font.subf, [
			.lo=lo,
			.hi=hi,
			.start=start,
			.path=std.pathcat(dir, sp[n]),
		])
	;;

	/* heuristic: probably big enough... */
	font.cache = std.mkht()
	r = [.x0 = 0, .y0 = 0, .x1 = 1000, .y1 = font.ascent]
	font.cimage = genallocimage(dpy, 0, Refnone, Cgrey8, DBlack, false, r, r)
	pack(dpy, "biib", 0, \
		('i' : byte), \
		font.cimage.id, \
		128, \
		font.ascent)
	-> `std.Ok font
}

const loadsubfont = {dpy, subf
	var buf

	match std.slurp(subf.path)
	| `std.Err e:	std.fatal("could not open subf {}: {}\n", subf.path, e)
	| `std.Ok b:	buf = b
	;;

	match xload(dpy, buf)
	| `std.Err e:	std.fatal("could not load image {}: {}\n")
	| `std.Ok (img, off):
		match readglyphs(buf[off:])
		| `std.Ok gd:
			subf.image = img
			subf.glyph = gd
		| `std.Err e:
			std.fatal("invalid glyph data in subf {}\n", subf.path)
		;;
	;;
}

const readglyphs = {glyphdata
	var sp : byte[:][3]
	var n, height, ascent, err
	var gd, g

	if glyphdata.len < 3*12 || std.bstrtok(sp[:], glyphdata[:3*12]).len != 3
		-> `std.Err `Efmt
	;;

	err = false
	n = getint(sp[0], &err)
	height = getint(sp[1], &err)
	ascent = getint(sp[2], &err)
	glyphdata = glyphdata[3*12:]
	if err || glyphdata.len < n
		-> `std.Err `Efmt
	;;

	gd = [][:]
	for var i = 0; i <= n; i++
		g = [
			.x=std.getle16(glyphdata[6*i+0 : 6*i+2]),
			.top=(glyphdata[6*i + 2] : int),
			.bottom=(glyphdata[6*i + 3] : int),
			.left=(glyphdata[6*i + 4] : int),
			.width=(glyphdata[6*i + 5] : int),
		]
		std.slpush(&gd, g)
	;;
	-> `std.Ok gd
}