Subversion Repositories planix.SVN

Rev

Rev 2 | Blame | Compare with Previous | Last modification | View Log | RSS feed

#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(display &&
                                            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;
        if(wid <= 0)
                wid = 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;
}