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