275 lines
5.5 KiB
C
275 lines
5.5 KiB
C
#include <u.h>
|
|
#include <libc.h>
|
|
#include <bio.h>
|
|
#include <ttf.h>
|
|
#include "impl.h"
|
|
|
|
static int
|
|
ttfparsekern(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 int
|
|
ttfkern(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 int
|
|
addglyph(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 void
|
|
flushglyphs(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 int
|
|
incrune(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);
|
|
}
|