| #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; i<max && (*sp || *rp); 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(display==nil || 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; |
| }else |
| depth = 8; |
| 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; |
| int pic; /* need >16 bits for adding offset below */ |
| 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 = realloc(of, (f->nsubf+DSUBF)*sizeof *subf); |
| if(f->subf == nil){ |
| f->subf = of; |
| goto Toss; |
| } |
| memset(f->subf+f->nsubf, 0, DSUBF*sizeof *subf); |
| subf = &f->subf[f->nsubf]; |
| f->nsubf += DSUBF; |
| } |
| } |
| 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 && f->display){ |
| /* 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; |
| |
| /* possible overflow here, but works out okay */ |
| pic += cf->offset; |
| pic -= cf->min; |
| if(pic >= subf->f->n) |
| goto TryPJW; |
| fi = &subf->f->info[pic]; |
| 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; |
| if(f->display == nil) |
| return 1; |
| 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; |
| if(depth <= 0) |
| depth = 1; |
| |
| d = f->display; |
| if(d == nil) |
| goto Nodisplay; |
| |
| 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; |
| Nodisplay: |
| 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; |
| } |