plan9port

[fork] Plan 9 from user space
git clone git://src.adamsgaard.dk/plan9port # fast
git clone https://src.adamsgaard.dk/plan9port.git # slow
Log | Files | Refs | README | LICENSE Back to index

font.c (7635B)


      1 #include <u.h>
      2 #include <libc.h>
      3 #include <draw.h>
      4 
      5 static int	fontresize(Font*, int, int, int);
      6 #if 0
      7 static int	freeup(Font*);
      8 #endif
      9 
     10 #define	PJW	0	/* use NUL==pjw for invisible characters */
     11 
     12 static Rune empty[] = { 0 };
     13 int
     14 cachechars(Font *f, char **ss, Rune **rr, ushort *cp, int max, int *wp, char **subfontname)
     15 {
     16 	int i, th, sh, h, ld, w, rw, wid, nc;
     17 	char *sp;
     18 	Rune r, *rp, vr;
     19 	ulong a;
     20 	Cacheinfo *c, *tc, *ec;
     21 
     22 	if(ss){
     23 		sp = *ss;
     24 		rp = empty;
     25 	}else{
     26 		sp = "";
     27 		rp = *rr;
     28 	}
     29 	wid = 0;
     30 	*subfontname = 0;
     31 	for(i=0; i<max && (*sp || *rp); sp+=w, rp+=rw){
     32 		if(ss){
     33 			r = *(uchar*)sp;
     34 			if(r < Runeself)
     35 				w = 1;
     36 			else{
     37 				w = chartorune(&vr, sp);
     38 				r = vr;
     39 			}
     40 			rw = 0;
     41 		}else{
     42 			r = *rp;
     43 			w = 0;
     44 			rw = 1;
     45 		}
     46 
     47 		sh = (17 * (uint)r) & (f->ncache-NFLOOK-1);
     48 		c = &f->cache[sh];
     49 		ec = c+NFLOOK;
     50 		h = sh;
     51 		while(c < ec){
     52 			if(c->value==r && c->age)
     53 				goto Found;
     54 			c++;
     55 			h++;
     56 		}
     57 
     58 		/*
     59 		 * Not found; toss out oldest entry
     60 		 */
     61 		a = ~0;
     62 		th = sh;
     63 		tc = &f->cache[th];
     64 		while(tc < ec){
     65 			if(tc->age < a){
     66 				a = tc->age;
     67 				h = th;
     68 				c = tc;
     69 			}
     70 			tc++;
     71 			th++;
     72 		}
     73 
     74 		if(a && (f->age-a)<500){	/* kicking out too recent; resize */
     75 			nc = 2*(f->ncache-NFLOOK) + NFLOOK;
     76 			if(nc <= MAXFCACHE){
     77 				if(i == 0)
     78 					fontresize(f, f->width, nc, f->maxdepth);
     79 				/* else flush first; retry will resize */
     80 				break;
     81 			}
     82 		}
     83 
     84 		if(c->age == f->age)	/* flush pending string output */
     85 			break;
     86 
     87 		ld = loadchar(f, r, c, h, i, subfontname);
     88 		if(ld <= 0){
     89 			if(ld == 0)
     90 				continue;
     91 			break;
     92 		}
     93 		c = &f->cache[h];	/* may have reallocated f->cache */
     94 
     95 	    Found:
     96 		wid += c->width;
     97 		c->age = f->age;
     98 		cp[i] = h;
     99 		i++;
    100 	}
    101 	if(ss)
    102 		*ss = sp;
    103 	else
    104 		*rr = rp;
    105 	*wp = wid;
    106 	return i;
    107 }
    108 
    109 void
    110 agefont(Font *f)
    111 {
    112 	Cacheinfo *c, *ec;
    113 	Cachesubf *s, *es;
    114 
    115 	f->age++;
    116 	if(f->age == 65536){
    117 		/*
    118 		 * Renormalize ages
    119 		 */
    120 		c = f->cache;
    121 		ec = c+f->ncache;
    122 		while(c < ec){
    123 			if(c->age){
    124 				c->age >>= 2;
    125 				c->age++;
    126 			}
    127 			c++;
    128 		}
    129 		s = f->subf;
    130 		es = s+f->nsubf;
    131 		while(s < es){
    132 			if(s->age){
    133 				if(s->age<SUBFAGE && s->cf->name != nil){
    134 					/* clean up */
    135 					freesubfont(s->f);
    136 					s->cf = nil;
    137 					s->f = nil;
    138 					s->age = 0;
    139 				}else{
    140 					s->age >>= 2;
    141 					s->age++;
    142 				}
    143 			}
    144 			s++;
    145 		}
    146 		f->age = (65536>>2) + 1;
    147 	}
    148 }
    149 
    150 static Subfont*
    151 cf2subfont(Cachefont *cf, Font *f)
    152 {
    153 	int depth;
    154 	char *name;
    155 	Subfont *sf;
    156 
    157 	name = cf->subfontname;
    158 	if(name == nil){
    159 		depth = 0;
    160 		if(f->display){
    161 			if(f->display->screenimage)
    162 				depth = f->display->screenimage->depth;
    163 		}else
    164 			depth = 8;
    165 		name = subfontname(cf->name, f->name, depth);
    166 		if(name == nil)
    167 			return nil;
    168 		cf->subfontname = name;
    169 	}
    170 	sf = lookupsubfont(f->display, name);
    171 	return sf;
    172 }
    173 
    174 /* return 1 if load succeeded, 0 if failed, -1 if must retry */
    175 int
    176 loadchar(Font *f, Rune r, Cacheinfo *c, int h, int noflush, char **subfontname)
    177 {
    178 	int i, oi, wid, top, bottom;
    179 	int pic;	/* need >16 bits for adding offset below */
    180 	Fontchar *fi;
    181 	Cachefont *cf;
    182 	Cachesubf *subf, *of;
    183 	uchar *b;
    184 
    185 	pic = r;
    186     Again:
    187 	for(i=0; i<f->nsub; i++){
    188 		cf = f->sub[i];
    189 		if(cf->min<=pic && pic<=cf->max)
    190 			goto Found;
    191 	}
    192     TryPJW:
    193 	if(pic != PJW){
    194 		pic = PJW;
    195 		goto Again;
    196 	}
    197 	return 0;
    198 
    199     Found:
    200 	/*
    201 	 * Choose exact or oldest
    202 	 */
    203 	oi = 0;
    204 	subf = &f->subf[0];
    205 	for(i=0; i<f->nsubf; i++){
    206 		if(cf == subf->cf)
    207 			goto Found2;
    208 		if(subf->age < f->subf[oi].age)
    209 			oi = i;
    210 		subf++;
    211 	}
    212 	subf = &f->subf[oi];
    213 
    214 	if(subf->f){
    215 		if(f->age-subf->age>SUBFAGE || f->nsubf>MAXSUBF){
    216     Toss:
    217 			/* ancient data; toss */
    218 			freesubfont(subf->f);
    219 			subf->cf = nil;
    220 			subf->f = nil;
    221 			subf->age = 0;
    222 		}else{				/* too recent; grow instead */
    223 			of = f->subf;
    224 			f->subf = realloc(of, (f->nsubf+DSUBF)*sizeof *subf);
    225 			if(f->subf == nil){
    226 				f->subf = of;
    227 				goto Toss;
    228 			}
    229 			memset(f->subf+f->nsubf, 0, DSUBF*sizeof *subf);
    230 			subf = &f->subf[f->nsubf];
    231 			f->nsubf += DSUBF;
    232 		}
    233 	}
    234 	subf->age = 0;
    235 	subf->cf = nil;
    236 	subf->f = cf2subfont(cf, f);
    237 	if(subf->f == nil){
    238 		if(cf->subfontname == nil)
    239 			goto TryPJW;
    240 		*subfontname = cf->subfontname;
    241 		return -1;
    242 	}
    243 
    244 	subf->cf = cf;
    245 	if(subf->f->ascent > f->ascent && f->display){
    246 		/* should print something? this is a mistake in the font file */
    247 		/* must prevent c->top from going negative when loading cache */
    248 		Image *b;
    249 		int d, t;
    250 		d = subf->f->ascent - f->ascent;
    251 		b = subf->f->bits;
    252 		draw(b, b->r, b, nil, addpt(b->r.min, Pt(0, d)));
    253 		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);
    254 		for(i=0; i<subf->f->n; i++){
    255 			t = subf->f->info[i].top-d;
    256 			if(t < 0)
    257 				t = 0;
    258 			subf->f->info[i].top = t;
    259 			t = subf->f->info[i].bottom-d;
    260 			if(t < 0)
    261 				t = 0;
    262 			subf->f->info[i].bottom = t;
    263 		}
    264 		subf->f->ascent = f->ascent;
    265 	}
    266 
    267     Found2:
    268 	subf->age = f->age;
    269 
    270 	/* possible overflow here, but works out okay */
    271 	pic += cf->offset;
    272 	pic -= cf->min;
    273 	if(pic >= subf->f->n)
    274 		goto TryPJW;
    275 	fi = &subf->f->info[pic];
    276 	if(fi->width == 0)
    277 		goto TryPJW;
    278 	wid = (fi+1)->x - fi->x;
    279 	if(f->width < wid || f->width == 0 || f->maxdepth < subf->f->bits->depth){
    280 		/*
    281 		 * Flush, free, reload (easier than reformatting f->b)
    282 		 */
    283 		if(noflush)
    284 			return -1;
    285 		if(f->width < wid)
    286 			f->width = wid;
    287 		if(f->maxdepth < subf->f->bits->depth)
    288 			f->maxdepth = subf->f->bits->depth;
    289 		i = fontresize(f, f->width, f->ncache, f->maxdepth);
    290 		if(i <= 0)
    291 			return i;
    292 		/* c is still valid as didn't reallocate f->cache */
    293 	}
    294 	c->value = r;
    295 	top = fi->top + (f->ascent-subf->f->ascent);
    296 	bottom = fi->bottom + (f->ascent-subf->f->ascent);
    297 	c->width = fi->width;
    298 	c->x = h*f->width;
    299 	c->left = fi->left;
    300 	if(f->display == nil)
    301 		return 1;
    302 	flushimage(f->display, 0);	/* flush any pending errors */
    303 	b = bufimage(f->display, 37);
    304 	if(b == 0)
    305 		return 0;
    306 	b[0] = 'l';
    307 	BPLONG(b+1, f->cacheimage->id);
    308 	BPLONG(b+5, subf->f->bits->id);
    309 	BPSHORT(b+9, c-f->cache);
    310 	BPLONG(b+11, c->x);
    311 	BPLONG(b+15, top);
    312 	BPLONG(b+19, c->x+((fi+1)->x-fi->x));
    313 	BPLONG(b+23, bottom);
    314 	BPLONG(b+27, fi->x);
    315 	BPLONG(b+31, fi->top);
    316 	b[35] = fi->left;
    317 	b[36] = fi->width;
    318 	return 1;
    319 }
    320 
    321 /* release all subfonts, return number freed */
    322 #if 0
    323 static
    324 int
    325 freeup(Font *f)
    326 {
    327 	Cachesubf *s, *es;
    328 	int nf;
    329 
    330 	if(f->sub[0]->name == nil)	/* font from mkfont; don't free */
    331 		return 0;
    332 	s = f->subf;
    333 	es = s+f->nsubf;
    334 	nf = 0;
    335 	while(s < es){
    336 		if(s->age){
    337 			freesubfont(s->f);
    338 			s->cf = nil;
    339 			s->f = nil;
    340 			s->age = 0;
    341 			nf++;
    342 		}
    343 		s++;
    344 	}
    345 	return nf;
    346 }
    347 #endif
    348 
    349 /* return whether resize succeeded && f->cache is unchanged */
    350 static int
    351 fontresize(Font *f, int wid, int ncache, int depth)
    352 {
    353 	Cacheinfo *i;
    354 	int ret;
    355 	Image *new;
    356 	uchar *b;
    357 	Display *d;
    358 
    359 	ret = 0;
    360 	if(depth <= 0)
    361 		depth = 1;
    362 
    363 	d = f->display;
    364 	if(d == nil)
    365 		goto Nodisplay;
    366 
    367 	new = allocimage(d, Rect(0, 0, ncache*wid, f->height), CHAN1(CGrey, depth), 0, 0);
    368 	if(new == nil){
    369 		fprint(2, "font cache resize failed: %r\n");
    370 		abort();
    371 		goto Return;
    372 	}
    373 	flushimage(d, 0);	/* flush any pending errors */
    374 	b = bufimage(d, 1+4+4+1);
    375 	if(b == 0){
    376 		freeimage(new);
    377 		goto Return;
    378 	}
    379 	b[0] = 'i';
    380 	BPLONG(b+1, new->id);
    381 	BPLONG(b+5, ncache);
    382 	b[9] = f->ascent;
    383 	if(flushimage(d, 0) < 0){
    384 		fprint(2, "resize: init failed: %r\n");
    385 		freeimage(new);
    386 		goto Return;
    387 	}
    388 	freeimage(f->cacheimage);
    389 	f->cacheimage = new;
    390     Nodisplay:
    391 	f->width = wid;
    392 	f->maxdepth = depth;
    393 	ret = 1;
    394 	if(f->ncache != ncache){
    395 		i = malloc(ncache*sizeof f->cache[0]);
    396 		if(i != nil){
    397 			ret = 0;
    398 			free(f->cache);
    399 			f->ncache = ncache;
    400 			f->cache = i;
    401 		}
    402 		/* else just wipe the cache clean and things will be ok */
    403 	}
    404     Return:
    405 	memset(f->cache, 0, f->ncache*sizeof f->cache[0]);
    406 	return ret;
    407 }