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