aijuadd libttf1d1c8b78 years agoHistory
274 lines · 5.5 KB · C
RawDownload
#include <u.h>#include <libc.h>#include <bio.h>#include <ttf.h>#include "impl.h" static intttfparsekern(TTFontU *f){	u16int ver, len, cov, ntab;	int i; 	if(ttfgototable(f, "kern") < 0)		return -1;	ttfunpack(f, "ww", &ver, &ntab);	if(ver != 0)		return -1;	if(ntab == 0)		return -1;	for(i = 0; i < ntab; i++){		ttfunpack(f, "www", &ver, &len, &cov);		if((cov & 1) != 0) break;		Bseek(f->bin, len - 6, 1);	}	ttfunpack(f, "w6", &len);	f->nkern = len;	f->kern = mallocz(sizeof(TTKern) * len, 1);	for(i = 0; i < len; i++)		ttfunpack(f, "lS", &f->kern[i].idx, &f->kern[i].val);	return 0;} static intttfkern(TTFont *f, int l, int r){	u32int idx;	int a, b, c;	TTFontU *u;		u = f->u;	if(u->nkern == 0)		return 0;	idx = l << 16 | r;	a = 0;	b = u->nkern - 1;	if(u->kern[a].idx > idx || u->kern[b].idx < idx)		return 0;	while(a <= b){		c = (a + b) / 2;		if(u->kern[c].idx < idx){			a = c + 1;		}else if(u->kern[c].idx > idx){			b = c - 1;		}else			return ttfrounddiv(u->kern[c].val * f->ppem, u->emsize);	}	return 0;} typedef struct {	TTBitmap *b;	TTFont *font;	TTGlyph **glyph;	int *gwidth;	char **cpos;	char *pp;	int nglyph, aglyph;	int linew;	int adj;	int nspc;	int oy, lh;	int spcidx;	int flags;	int spcw;	int spcminus;} Render; static intaddglyph(Render *r, char *p, TTGlyph *g){	void *v;	int k; 	if(r->nglyph >= r->aglyph){		r->aglyph += 32;		v = realloc(r->glyph, sizeof(TTGlyph *) * r->aglyph);		if(v == nil) return -1;		r->glyph = v;		v = realloc(r->gwidth, sizeof(int) * r->aglyph);		if(v == nil) return -1;		r->gwidth = v;		v = realloc(r->cpos, sizeof(char *) * r->aglyph);		if(v == nil) return -1;		r->cpos = v;	}	r->glyph[r->nglyph] = g;	r->cpos[r->nglyph] = p;	r->gwidth[r->nglyph] = g->advanceWidthpx;	if(r->nglyph > 0){		k = ttfkern(r->font, r->glyph[r->nglyph-1]->idx, g->idx);		r->gwidth[r->nglyph-1] += k;		r->linew += k;	}	r->nglyph++;	r->linew += r->gwidth[r->nglyph-1];	if(g->idx == r->spcidx)		r->nspc++;	return 0;} static voidflushglyphs(Render *r, int justify){	int i, n, k, x, y;	int llen;	int adj, spcw, nspc, c;	TTFont *f; 	f = r->font;	if((r->flags & TTFMODE) == TTFLALIGN && !justify)		while(r->nglyph > 0 && r->glyph[r->nglyph - 1]->idx == r->spcidx){			r->linew -= r->gwidth[--r->nglyph];			r->nspc--;		}	llen = r->linew;	k = n = r->nglyph;	nspc = r->nspc;	adj = (nspc * r->spcminus + 63) / 64;	if(r->linew - adj > r->b->width){		n = r->nglyph;		while(n > 0 && r->glyph[n - 1]->idx != r->spcidx)			llen -= r->gwidth[--n];		k = n;		while(n > 0 && r->glyph[n - 1]->idx == r->spcidx){			llen -= r->gwidth[--n];			nspc--;		}		if(n == 0){			while(n < r->nglyph && llen + r->gwidth[n] < r->b->width)				llen += r->gwidth[n++];			k = n;		}	}	if(justify){		if(nspc == 0)			spcw = 0;		else			spcw = (r->b->width - llen + nspc * r->spcw) * 64 / nspc;	}else		spcw = r->spcw * 64;	switch(r->flags & TTFMODE | justify * TTFJUSTIFY){	case TTFRALIGN:		x = r->b->width - llen;		break;	case TTFCENTER:		x = (r->b->width - llen)/2;		break;	default:		x = 0;	}	y = r->oy + f->ascentpx;	c = 0;	for(i = 0; i < k; i++){		if(r->glyph[i]->idx == r->spcidx){			c += spcw;			x += c >> 6;			c &= 63;			r->nspc--;		}else{			ttfblit(r->b, x + r->glyph[i]->xminpx, y - r->glyph[i]->ymaxpx, r->glyph[i], 0, 0, r->glyph[i]->width, r->glyph[i]->height);			x += r->gwidth[i];		}		r->linew -= r->gwidth[i];		ttfputglyph(r->glyph[i]);	}	if(n > 0)		r->pp = r->cpos[n-1];	r->oy += r->lh;	memmove(r->glyph, r->glyph + k, (r->nglyph - k) * sizeof(TTGlyph *));	memmove(r->cpos, r->cpos + k, (r->nglyph - k) * sizeof(char *));	memmove(r->gwidth, r->gwidth + k, (r->nglyph - k) * sizeof(int));	r->nglyph -= k;} TTBitmap *_ttfrender(TTFont *f, int (*getrune)(Rune *, char *), char *p, char *end, int w, int h, int flags, char **rp){	Render r;	Rune ch;	int i, adj;	TTGlyph *g;		if(rp != nil) *rp = p;	if(f->u->nkern < 0 && ttfparsekern(f->u) < 0)		f->u->nkern = 0;	memset(&r, 0, sizeof(Render));	r.flags = flags;	r.font = f;	r.b = ttfnewbitmap(w, h);	if(r.b == nil) goto error;	r.oy = 0;	r.lh = f->ascentpx + f->descentpx;	r.pp = p;		g = ttfgetglyph(f, ttffindchar(f, ' '), 1);	r.spcidx = g->idx;	r.spcw = g->advanceWidthpx;	if((flags & TTFJUSTIFY) != 0)		r.spcminus = r.spcw * 21;	else		r.spcminus = 0;			while(p < end && r.oy + r.lh < h){		p += getrune(&ch, p);		if(ch == '\n'){			flushglyphs(&r, 0);			continue;		}		g = ttfgetglyph(f, ttffindchar(f, ch), 1);		if(g == nil){			g = ttfgetglyph(f, 0, 1);			if(g == nil)				continue;		}		if(addglyph(&r, p, g) < 0)			goto error;		adj = (r.nspc * r.spcminus + 63) / 64;		if(r.linew - adj > r.b->width){			flushglyphs(&r, (flags & TTFJUSTIFY) != 0);		}	}	if(r.oy + r.lh < h)		flushglyphs(&r, 0);	for(i = 0; i < r.nglyph; i++)		ttfputglyph(r.glyph[i]);	free(r.glyph);	free(r.gwidth);	free(r.cpos);	if(rp != nil)		*rp = r.pp;	return r.b;error:	ttffreebitmap(r.b);	free(r.glyph);	free(r.gwidth);	return nil;} TTBitmap *ttfrender(TTFont *f, char *str, char *end, int w, int h, int flags, char **rstr){	if(str == nil)		end = nil;	else if(end == nil)		end = str + strlen(str);	return _ttfrender(f, chartorune, str, end, w, h, flags, rstr);} static intincrune(Rune *r, char *s){	*r = *(Rune*)s;	return sizeof(Rune);} TTBitmap *ttfrunerender(TTFont *f, Rune *str, Rune *end, int w, int h, int flags, Rune **rstr){	if(str == nil)		end = nil;	else if(end == nil)		end = str + runestrlen(str);	return _ttfrender(f, incrune, (char *) str, (char *) end, w, h, flags, (char **) rstr);}