123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403 |
- #include <u.h>
- #include <libc.h>
- #include <draw.h>
- static int fontresize(Font*, int, int, int);
- static int freeup(Font*);
- #define PJW 0 /* use NUL==pjw for invisible characters */
- 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 = L"";
- }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(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){
- if(f->display && 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;
- 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 && 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 */
- 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;
- }
- /* 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;
- }
|