libdraw: autoscale fonts when moving between low and high dpi screens

Change-Id: I6093955b222db89dfe437fb723593b173d888d01
Reviewed-on: https://plan9port-review.googlesource.com/1170
Reviewed-by: Russ Cox <rsc@swtch.com>
This commit is contained in:
Russ Cox 2015-02-17 12:16:20 -05:00
parent 77f23268f7
commit 213fc4f6fb
9 changed files with 243 additions and 28 deletions

View file

@ -206,6 +206,9 @@ struct Display
struct Mux *mux; struct Mux *mux;
int srvfd; int srvfd;
int dpi; int dpi;
Font *firstfont;
Font *lastfont;
}; };
struct Image struct Image
@ -319,6 +322,15 @@ struct Font
Cachesubf *subf; Cachesubf *subf;
Cachefont **sub; /* as read from file */ Cachefont **sub; /* as read from file */
Image *cacheimage; Image *cacheimage;
/* doubly linked list of fonts known to display */
int ondisplaylist;
Font *next;
Font *prev;
/* on hi-dpi systems, one of these is set to f and the other is the other-dpi version of f */
Font *lodpi;
Font *hidpi;
}; };
#define Dx(r) ((r).max.x-(r).min.x) #define Dx(r) ((r).max.x-(r).min.x)
@ -460,6 +472,7 @@ extern void borderop(Image*, Rectangle, int, Image*, Point, Drawop);
* Font management * Font management
*/ */
extern Font* openfont(Display*, char*); extern Font* openfont(Display*, char*);
extern int parsefontscale(char*, char**);
extern Font* buildfont(Display*, char*, char*); extern Font* buildfont(Display*, char*, char*);
extern void freefont(Font*); extern void freefont(Font*);
extern Font* mkfont(Subfont*, Rune); extern Font* mkfont(Subfont*, Rune);
@ -483,11 +496,13 @@ extern int runestringnwidth(Font*, Rune*, int);
extern Point strsubfontwidth(Subfont*, char*); extern Point strsubfontwidth(Subfont*, char*);
extern int loadchar(Font*, Rune, Cacheinfo*, int, int, char**); extern int loadchar(Font*, Rune, Cacheinfo*, int, int, char**);
extern char* subfontname(char*, char*, int); extern char* subfontname(char*, char*, int);
extern Subfont* _getsubfont(Display*, Font*, char*); extern Subfont* _getsubfont(Display*, char*);
extern Subfont* getdefont(Display*); extern Subfont* getdefont(Display*);
extern void lockdisplay(Display*); extern void lockdisplay(Display*);
extern void unlockdisplay(Display*); extern void unlockdisplay(Display*);
extern int drawlsetrefresh(u32int, int, void*, void*); extern int drawlsetrefresh(u32int, int, void*, void*);
extern void loadhidpi(Font*);
extern void swapfont(Font*, Font**, Font**);
/* /*
* Predefined * Predefined

View file

@ -487,6 +487,21 @@ point to the portion of the window inside the border;
sophisticated clients may use sophisticated clients may use
.B _screen .B _screen
to make further subwindows. to make further subwindows.
If
.I getwindow
is being called due to a resizing of the window,
the resize may be accompanied by a change in screen pixel density (DPI),
in which case the value of the
.BR Display 's
.B dpi
field and any open
.BR Font 's
.B height
and
.B ascent
fields may be updated during the call to
.IR getwindow .
Programs should discard any cached information about display or font sizes.
.\" Programs desiring multiple independent windows .\" Programs desiring multiple independent windows
.\" may use the mechanisms of .\" may use the mechanisms of
.\" .IR rio (4) .\" .IR rio (4)

View file

@ -138,5 +138,23 @@ freefont(Font *f)
free(f->cache); free(f->cache);
free(f->subf); free(f->subf);
free(f->sub); free(f->sub);
if(f->ondisplaylist) {
f->ondisplaylist = 0;
if(f->next)
f->next->prev = f->prev;
else
f->display->lastfont = f->prev;
if(f->prev)
f->prev->next = f->next;
else
f->display->firstfont = f->next;
}
if(f->lodpi != f)
freefont(f->lodpi);
if(f->hidpi != f)
freefont(f->hidpi);
free(f); free(f);
} }

View file

@ -11,17 +11,20 @@ int _fontpipe(char*);
static void scalesubfont(Subfont*, int); static void scalesubfont(Subfont*, int);
Subfont* Subfont*
_getsubfont(Display *d, Font *ff, char *name) _getsubfont(Display *d, char *name)
{ {
int fd; int fd;
Subfont *f; Subfont *f;
int scale;
fd = open(name, OREAD); char *fname;
if(fd < 0 && strncmp(name, "/mnt/font/", 10) == 0)
fd = _fontpipe(name+10); scale = parsefontscale(name, &fname);
fd = open(fname, OREAD);
if(fd < 0 && strncmp(fname, "/mnt/font/", 10) == 0)
fd = _fontpipe(fname+10);
if(fd < 0){ if(fd < 0){
fprint(2, "getsubfont: can't open %s: %r\n", name); fprint(2, "getsubfont: can't open %s: %r\n", fname);
return 0; return 0;
} }
/* /*
@ -38,8 +41,8 @@ _getsubfont(Display *d, Font *ff, char *name)
if(f == 0) if(f == 0)
fprint(2, "getsubfont: can't read %s: %r\n", name); fprint(2, "getsubfont: can't read %s: %r\n", name);
close(fd); close(fd);
if(ff->scale != 1 && ff->scale != 0) if(scale > 1)
scalesubfont(f, ff->scale); scalesubfont(f, scale);
return f; return f;
} }

View file

@ -199,6 +199,7 @@ int
getwindow(Display *d, int ref) getwindow(Display *d, int ref)
{ {
Image *i, *oi; Image *i, *oi;
Font *f;
/* XXX check for destroyed? */ /* XXX check for destroyed? */
@ -219,6 +220,17 @@ getwindow(Display *d, int ref)
_freeimage1(screen); _freeimage1(screen);
screen = _allocwindow(screen, _screen, i->r, ref, DWhite); screen = _allocwindow(screen, _screen, i->r, ref, DWhite);
d->screenimage = screen; d->screenimage = screen;
if(d->dpi >= DefaultDPI*3/2) {
for(f=d->firstfont; f != nil; f=f->next)
loadhidpi(f);
} else {
for(f=d->firstfont; f != nil; f=f->next)
if(f->lodpi != nil && f->lodpi != f)
swapfont(f, &f->hidpi, &f->lodpi);
}
return 0; return 0;
} }

View file

@ -5,23 +5,41 @@
extern vlong _drawflength(int); extern vlong _drawflength(int);
int _fontpipe(char*); int _fontpipe(char*);
int
parsefontscale(char *name, char **base)
{
char *p;
int scale;
p = name;
scale = 0;
while('0' <= *p && *p <= '9') {
scale = scale*10 + *p - '0';
p++;
}
if(*p == '*' && scale > 0)
*base = p+1;
else {
*base = name;
scale = 1;
}
return scale;
}
Font* Font*
openfont(Display *d, char *name) openfont1(Display *d, char *name)
{ {
Font *fnt; Font *fnt;
int fd, i, n, scale; int fd, i, n, scale;
char *buf, *nambuf; char *buf, *nambuf, *fname, *freename;
nambuf = 0; nambuf = 0;
scale = 1; freename = nil;
if('1' <= name[0] && name[0] <= '9' && name[1] == '*') { scale = parsefontscale(name, &fname);
scale = name[0] - '0';
name += 2;
}
fd = open(name, OREAD);
if(fd < 0 && strncmp(name, "/lib/font/bit/", 14) == 0){ fd = open(fname, OREAD);
nambuf = smprint("#9/font/%s", name+14); if(fd < 0 && strncmp(fname, "/lib/font/bit/", 14) == 0){
nambuf = smprint("#9/font/%s", fname+14);
if(nambuf == nil) if(nambuf == nil)
return 0; return 0;
nambuf = unsharp(nambuf); nambuf = unsharp(nambuf);
@ -31,12 +49,18 @@ openfont(Display *d, char *name)
free(nambuf); free(nambuf);
return 0; return 0;
} }
name = nambuf; fname = nambuf;
if(scale > 1) {
name = smprint("%d*%s", scale, fname);
freename = name;
} else {
name = fname;
}
} }
if(fd >= 0) if(fd >= 0)
n = _drawflength(fd); n = _drawflength(fd);
if(fd < 0 && strncmp(name, "/mnt/font/", 10) == 0) { if(fd < 0 && strncmp(fname, "/mnt/font/", 10) == 0) {
fd = _fontpipe(name+10); fd = _fontpipe(fname+10);
n = 8192; n = 8192;
} }
if(fd < 0) if(fd < 0)
@ -59,6 +83,7 @@ openfont(Display *d, char *name)
fnt = buildfont(d, buf, name); fnt = buildfont(d, buf, name);
free(buf); free(buf);
free(nambuf); free(nambuf);
free(freename);
if(scale != 1) { if(scale != 1) {
fnt->scale = scale; fnt->scale = scale;
fnt->height *= scale; fnt->height *= scale;
@ -68,6 +93,120 @@ openfont(Display *d, char *name)
return fnt; return fnt;
} }
void
swapfont(Font *targ, Font **oldp, Font **newp)
{
Font f, *old, *new;
if(targ != *oldp)
sysfatal("bad swapfont %p %p %p", targ, *oldp, *newp);
old = *oldp;
new = *newp;
f.name = old->name;
f.display = old->display;
f.height = old->height;
f.ascent = old->ascent;
f.width = old->width;
f.nsub = old->nsub;
f.age = old->age;
f.maxdepth = old->maxdepth;
f.ncache = old->ncache;
f.nsubf = old->nsubf;
f.scale = old->scale;
f.cache = old->cache;
f.subf = old->subf;
f.sub = old->sub;
f.cacheimage = old->cacheimage;
old->name = new->name;
old->display = new->display;
old->height = new->height;
old->ascent = new->ascent;
old->width = new->width;
old->nsub = new->nsub;
old->age = new->age;
old->maxdepth = new->maxdepth;
old->ncache = new->ncache;
old->nsubf = new->nsubf;
old->scale = new->scale;
old->cache = new->cache;
old->subf = new->subf;
old->sub = new->sub;
old->cacheimage = new->cacheimage;
new->name = f.name;
new->display = f.display;
new->height = f.height;
new->ascent = f.ascent;
new->width = f.width;
new->nsub = f.nsub;
new->age = f.age;
new->maxdepth = f.maxdepth;
new->ncache = f.ncache;
new->nsubf = f.nsubf;
new->scale = f.scale;
new->cache = f.cache;
new->subf = f.subf;
new->sub = f.sub;
new->cacheimage = f.cacheimage;
*oldp = new;
*newp = old;
}
void
loadhidpi(Font *f)
{
char *name;
Font *fnew;
if(f->hidpi == f)
return;
if(f->hidpi != nil) {
swapfont(f, &f->lodpi, &f->hidpi);
return;
}
name = smprint("%d*%s", f->scale*2, f->name);
fnew = openfont1(f->display, name);
if(fnew == nil)
return;
f->hidpi = fnew;
free(name);
swapfont(f, &f->lodpi, &f->hidpi);
}
Font*
openfont(Display *d, char *name)
{
Font *f;
f = openfont1(d, name);
f->lodpi = f;
/* add to display list for when dpi changes */
/* d can be nil when invoked from mc. */
if(d != nil) {
f->ondisplaylist = 1;
f->prev = d->lastfont;
f->next = nil;
if(f->prev)
f->prev->next = f;
else
d->firstfont = f;
d->lastfont = f;
/* if this is a hi-dpi display, find hi-dpi version and swap */
if(d->dpi >= DefaultDPI*3/2)
loadhidpi(f);
}
return f;
}
int int
_fontpipe(char *name) _fontpipe(char *name)
{ {

View file

@ -130,7 +130,7 @@ _string(Image *dst, Point pt, Image *src, Point sp, Font *f, char *s, Rune *r, i
} }
if(subfontname){ if(subfontname){
freesubfont(sf); freesubfont(sf);
if((sf=_getsubfont(f->display, f, subfontname)) == 0){ if((sf=_getsubfont(f->display, subfontname)) == 0){
def = f->display ? f->display->defaultfont : nil; def = f->display ? f->display->defaultfont : nil;
if(def && f!=def) if(def && f!=def)
f = def; f = def;

View file

@ -48,7 +48,7 @@ _stringnwidth(Font *f, char *s, Rune *r, int len)
} }
if(subfontname){ if(subfontname){
freesubfont(sf); freesubfont(sf);
if((sf=_getsubfont(f->display, f, subfontname)) == 0){ if((sf=_getsubfont(f->display, subfontname)) == 0){
def = f->display ? f->display->defaultfont : nil; def = f->display ? f->display->defaultfont : nil;
if(def && f!=def) if(def && f!=def)
f = def; f = def;

View file

@ -9,14 +9,16 @@
char* char*
subfontname(char *cfname, char *fname, int maxdepth) subfontname(char *cfname, char *fname, int maxdepth)
{ {
char *t, *u, *tmp1, *tmp2; char *t, *u, *tmp1, *tmp2, *base;
int i; int i, scale;
scale = parsefontscale(fname, &base);
t = strdup(cfname); /* t is the return string */ t = strdup(cfname); /* t is the return string */
if(strcmp(cfname, "*default*") == 0) if(strcmp(cfname, "*default*") == 0)
return t; return t;
if(t[0] != '/'){ if(t[0] != '/'){
tmp2 = strdup(fname); tmp2 = strdup(base);
u = utfrrune(tmp2, '/'); u = utfrrune(tmp2, '/');
if(u) if(u)
u[0] = 0; u[0] = 0;
@ -38,13 +40,24 @@ subfontname(char *cfname, char *fname, int maxdepth)
tmp2 = smprint("%s.%d", t, i); tmp2 = smprint("%s.%d", t, i);
if(access(tmp2, AREAD) == 0) { if(access(tmp2, AREAD) == 0) {
free(t); free(t);
if(scale > 1) {
t = smprint("%d*%s", scale, tmp2);
free(tmp2);
tmp2 = t;
}
return tmp2; return tmp2;
} }
} }
/* try default */ /* try default */
if(strncmp(t, "/mnt/font/", 10) == 0 || access(t, AREAD) == 0) if(strncmp(t, "/mnt/font/", 10) == 0 || access(t, AREAD) == 0) {
if(scale > 1) {
tmp2 = smprint("%d*%s", scale, t);
free(t);
t = tmp2;
}
return t; return t;
}
return nil; return nil;
} }