diff options
Diffstat (limited to 'src/libdraw/font.c')
-rw-r--r-- | src/libdraw/font.c | 401 |
1 files changed, 401 insertions, 0 deletions
diff --git a/src/libdraw/font.c b/src/libdraw/font.c new file mode 100644 index 00000000..6b14d3fc --- /dev/null +++ b/src/libdraw/font.c @@ -0,0 +1,401 @@ +#include <u.h> +#include <libc.h> +#include <draw.h> + +static int fontresize(Font*, int, int, int); +#if 0 +static int freeup(Font*); +#endif + +#define PJW 0 /* use NUL==pjw for invisible characters */ + +static Rune empty[] = { 0 }; +int +cachechars(Font *f, char **ss, Rune **rr, ushort *cp, int max, int *wp, char **subfontname) +{ + int i, th, sh, h, ld, w, rw, wid, nc; + char *sp; + Rune r, *rp, vr; + ulong a; + Cacheinfo *c, *tc, *ec; + + if(ss){ + sp = *ss; + rp = empty; + }else{ + sp = ""; + rp = *rr; + } + wid = 0; + *subfontname = 0; + for(i=0; (*sp || *rp) && i<max; sp+=w, rp+=rw){ + if(ss){ + r = *(uchar*)sp; + if(r < Runeself) + w = 1; + else{ + w = chartorune(&vr, sp); + r = vr; + } + rw = 0; + }else{ + r = *rp; + w = 0; + rw = 1; + } + + sh = (17 * (uint)r) & (f->ncache-NFLOOK-1); + c = &f->cache[sh]; + ec = c+NFLOOK; + h = sh; + while(c < ec){ + if(c->value==r && c->age) + goto Found; + c++; + h++; + } + + /* + * Not found; toss out oldest entry + */ + a = ~0; + th = sh; + tc = &f->cache[th]; + while(tc < ec){ + if(tc->age < a){ + a = tc->age; + h = th; + c = tc; + } + tc++; + th++; + } + + if(a && (f->age-a)<500){ /* kicking out too recent; resize */ + nc = 2*(f->ncache-NFLOOK) + NFLOOK; + if(nc <= MAXFCACHE){ + if(i == 0) + fontresize(f, f->width, nc, f->maxdepth); + /* else flush first; retry will resize */ + break; + } + } + + if(c->age == f->age) /* flush pending string output */ + break; + + ld = loadchar(f, r, c, h, i, subfontname); + if(ld <= 0){ + if(ld == 0) + continue; + break; + } + c = &f->cache[h]; /* may have reallocated f->cache */ + + Found: + wid += c->width; + c->age = f->age; + cp[i] = h; + i++; + } + if(ss) + *ss = sp; + else + *rr = rp; + *wp = wid; + return i; +} + +void +agefont(Font *f) +{ + Cacheinfo *c, *ec; + Cachesubf *s, *es; + + f->age++; + if(f->age == 65536){ + /* + * Renormalize ages + */ + c = f->cache; + ec = c+f->ncache; + while(c < ec){ + if(c->age){ + c->age >>= 2; + c->age++; + } + c++; + } + s = f->subf; + es = s+f->nsubf; + while(s < es){ + if(s->age){ + if(s->age<SUBFAGE && s->cf->name != nil){ + /* clean up */ + if(s->f != display->defaultsubfont) + freesubfont(s->f); + s->cf = nil; + s->f = nil; + s->age = 0; + }else{ + s->age >>= 2; + s->age++; + } + } + s++; + } + f->age = (65536>>2) + 1; + } +} + +static Subfont* +cf2subfont(Cachefont *cf, Font *f) +{ + int depth; + char *name; + Subfont *sf; + + name = cf->subfontname; + if(name == nil){ + depth = 0; + if(f->display){ + if(f->display->screenimage) + depth = f->display->screenimage->depth; + } + name = subfontname(cf->name, f->name, depth); + if(name == nil) + return nil; + cf->subfontname = name; + } + sf = lookupsubfont(f->display, name); + return sf; +} + +/* return 1 if load succeeded, 0 if failed, -1 if must retry */ +int +loadchar(Font *f, Rune r, Cacheinfo *c, int h, int noflush, char **subfontname) +{ + int i, oi, wid, top, bottom; + Rune pic; + Fontchar *fi; + Cachefont *cf; + Cachesubf *subf, *of; + uchar *b; + + pic = r; + Again: + for(i=0; i<f->nsub; i++){ + cf = f->sub[i]; + if(cf->min<=pic && pic<=cf->max) + goto Found; + } + TryPJW: + if(pic != PJW){ + pic = PJW; + goto Again; + } + return 0; + + Found: + /* + * Choose exact or oldest + */ + oi = 0; + subf = &f->subf[0]; + for(i=0; i<f->nsubf; i++){ + if(cf == subf->cf) + goto Found2; + if(subf->age < f->subf[oi].age) + oi = i; + subf++; + } + subf = &f->subf[oi]; + + if(subf->f){ + if(f->age-subf->age>SUBFAGE || f->nsubf>MAXSUBF){ + Toss: + /* ancient data; toss */ + freesubfont(subf->f); + subf->cf = nil; + subf->f = nil; + subf->age = 0; + }else{ /* too recent; grow instead */ + of = f->subf; + f->subf = malloc((f->nsubf+DSUBF)*sizeof *subf); + if(f->subf == nil){ + f->subf = of; + goto Toss; + } + memmove(f->subf, of, (f->nsubf+DSUBF)*sizeof *subf); + memset(f->subf+f->nsubf, 0, DSUBF*sizeof *subf); + subf = &f->subf[f->nsubf]; + f->nsubf += DSUBF; + free(of); + } + } + subf->age = 0; + subf->cf = nil; + subf->f = cf2subfont(cf, f); + if(subf->f == nil){ + if(cf->subfontname == nil) + goto TryPJW; + *subfontname = cf->subfontname; + return -1; + } + + subf->cf = cf; + if(subf->f->ascent > f->ascent){ + /* should print something? this is a mistake in the font file */ + /* must prevent c->top from going negative when loading cache */ + Image *b; + int d, t; + d = subf->f->ascent - f->ascent; + b = subf->f->bits; + draw(b, b->r, b, nil, addpt(b->r.min, Pt(0, d))); + draw(b, Rect(b->r.min.x, b->r.max.y-d, b->r.max.x, b->r.max.y), f->display->black, nil, b->r.min); + for(i=0; i<subf->f->n; i++){ + t = subf->f->info[i].top-d; + if(t < 0) + t = 0; + subf->f->info[i].top = t; + t = subf->f->info[i].bottom-d; + if(t < 0) + t = 0; + subf->f->info[i].bottom = t; + } + subf->f->ascent = f->ascent; + } + + Found2: + subf->age = f->age; + + pic += cf->offset; + if(pic-cf->min >= subf->f->n) + goto TryPJW; + fi = &subf->f->info[pic - cf->min]; + if(fi->width == 0) + goto TryPJW; + wid = (fi+1)->x - fi->x; + if(f->width < wid || f->width == 0 || f->maxdepth < subf->f->bits->depth){ + /* + * Flush, free, reload (easier than reformatting f->b) + */ + if(noflush) + return -1; + if(f->width < wid) + f->width = wid; + if(f->maxdepth < subf->f->bits->depth) + f->maxdepth = subf->f->bits->depth; + i = fontresize(f, f->width, f->ncache, f->maxdepth); + if(i <= 0) + return i; + /* c is still valid as didn't reallocate f->cache */ + } + c->value = r; + top = fi->top + (f->ascent-subf->f->ascent); + bottom = fi->bottom + (f->ascent-subf->f->ascent); + c->width = fi->width; + c->x = h*f->width; + c->left = fi->left; + flushimage(f->display, 0); /* flush any pending errors */ + b = bufimage(f->display, 37); + if(b == 0) + return 0; + b[0] = 'l'; + BPLONG(b+1, f->cacheimage->id); + BPLONG(b+5, subf->f->bits->id); + BPSHORT(b+9, c-f->cache); + BPLONG(b+11, c->x); + BPLONG(b+15, top); + BPLONG(b+19, c->x+((fi+1)->x-fi->x)); + BPLONG(b+23, bottom); + BPLONG(b+27, fi->x); + BPLONG(b+31, fi->top); + b[35] = fi->left; + b[36] = fi->width; + return 1; +} + +/* release all subfonts, return number freed */ +#if 0 +static +int +freeup(Font *f) +{ + Cachesubf *s, *es; + int nf; + + if(f->sub[0]->name == nil) /* font from mkfont; don't free */ + return 0; + s = f->subf; + es = s+f->nsubf; + nf = 0; + while(s < es){ + if(s->age){ + freesubfont(s->f); + s->cf = nil; + s->f = nil; + s->age = 0; + nf++; + } + s++; + } + return nf; +} +#endif + +/* return whether resize succeeded && f->cache is unchanged */ +static int +fontresize(Font *f, int wid, int ncache, int depth) +{ + Cacheinfo *i; + int ret; + Image *new; + uchar *b; + Display *d; + + ret = 0; + d = f->display; + if(depth <= 0) + depth = 1; + + new = allocimage(d, Rect(0, 0, ncache*wid, f->height), CHAN1(CGrey, depth), 0, 0); + if(new == nil){ + fprint(2, "font cache resize failed: %r\n"); + abort(); + goto Return; + } + flushimage(d, 0); /* flush any pending errors */ + b = bufimage(d, 1+4+4+1); + if(b == 0){ + freeimage(new); + goto Return; + } + b[0] = 'i'; + BPLONG(b+1, new->id); + BPLONG(b+5, ncache); + b[9] = f->ascent; + if(flushimage(d, 0) < 0){ + fprint(2, "resize: init failed: %r\n"); + freeimage(new); + goto Return; + } + freeimage(f->cacheimage); + f->cacheimage = new; + f->width = wid; + f->maxdepth = depth; + ret = 1; + if(f->ncache != ncache){ + i = malloc(ncache*sizeof f->cache[0]); + if(i != nil){ + ret = 0; + free(f->cache); + f->ncache = ncache; + f->cache = i; + } + /* else just wipe the cache clean and things will be ok */ + } + Return: + memset(f->cache, 0, f->ncache*sizeof f->cache[0]); + return ret; +} |