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