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