diff --git a/firmware/export/font.h b/firmware/export/font.h index ed003f220a..d19e0b87ad 100644 --- a/firmware/export/font.h +++ b/firmware/export/font.h @@ -125,6 +125,13 @@ void font_unload(int font_id); void font_unload_all(void); void font_lock(int font_id, bool lock); +/* Closes the file descriptor if the font file (if cached) but keeps + * the cache intact, so font_get_{bits,width} still work. */ +void font_disable_all(void); +/* Re-opens the file descriptor of the font file. Should be called as + * counter-part of font_disable_all(); */ +void font_enable_all(void); + struct font* font_get(int font); int font_getstringsize(const unsigned char *str, int *w, int *h, int fontnumber); diff --git a/firmware/font.c b/firmware/font.c index e7a574f0dd..723c972e3b 100644 --- a/firmware/font.c +++ b/firmware/font.c @@ -88,9 +88,10 @@ extern struct font sysfont; #ifndef BOOTLOADER struct buflib_alloc_data { - struct font font; - int handle_locks; /* is the buflib handle currently locked? */ - int refcount; /* how many times has this font been loaded? */ + struct font font; /* must be the first member! */ + int handle_locks; /* is the buflib handle currently locked? */ + int refcount; /* how many times has this font been loaded? */ + int disabled; /* font disabled (use fallback glyphs, from sysfont) */ unsigned char buffer[]; }; static int buflib_allocations[MAXFONTS]; @@ -285,6 +286,7 @@ static struct font* font_load_cached(struct font* pf, int32_t noffset) { /* We are now at the bitmap data, this is fixed at 36.. */ + pf->width = NULL; pf->bits = NULL; /* Calculate offset to offset data */ @@ -412,11 +414,10 @@ int font_load_ex( const char *path, size_t buf_size, int glyphs ) /* load font struct f with file header */ int file_size = filesize( fd ); struct font header; - struct font *pheader = &header; struct font f; uint32_t nwidth, noffset; - if ( !font_load_header( fd, pheader, &f, &nwidth, &noffset ) + if ( !font_load_header( fd, &header, &f, &nwidth, &noffset ) #if LCD_DEPTH < 16 || f.depth #endif @@ -470,6 +471,7 @@ int font_load_ex( const char *path, size_t buf_size, int glyphs ) old_id = font_id; old_refcount = pd->refcount; pd->refcount = 1; + pd->disabled = false; font_unload(font_id); font_id = font_load_ex(path, bufsize, glyphs); if (font_id < 0) @@ -623,6 +625,58 @@ void font_unload_all(void) } } +static void font_disable(int font_id) +{ + if ( font_id < 0 || font_id >= MAXFONTS ) + return; + int handle = buflib_allocations[font_id]; + if ( handle < 0 ) + return; + struct buflib_alloc_data *pdata = core_get_data(handle); + struct font *pf = &pdata->font; + + if (pf->fd >= 0) + { + /* save the cache, but it keep it in-RAM so that cache lookups + * can still succeed on the same font */ + glyph_cache_save(font_id); + close(pf->fd); + pf->fd = -1; + pdata->disabled = true; + } +} + +void font_disable_all(void) +{ + for(int i = 0; i < MAXFONTS; i++) + font_disable(i); +} + +static void font_enable(int font_id) +{ + if ( font_id < 0 || font_id >= MAXFONTS ) + return; + int handle = buflib_allocations[font_id]; + if ( handle < 0 ) + return; + struct buflib_alloc_data *pdata = core_get_data(handle); + struct font *pf = &pdata->font; + + if (pdata->disabled && pf->fd < 0) + { + const char *filename = font_filename(font_id); + pf->fd = open(filename, O_RDONLY); + pdata->disabled = false; + } +} + +void font_enable_all(void) +{ + for(int i = 0; i < MAXFONTS; i++) + font_enable(i); +} + + /* * Return a pointer to an incore font structure. * If the requested font isn't loaded/compiled-in, @@ -715,10 +769,15 @@ load_cache_entry(struct font_cache_entry* p, void* callback_data) static void cache_create(struct font* pf) { /* maximum size of rotated bitmap */ - int bitmap_size = glyph_bytes( pf, pf->maxwidth); - + int bitmap_size = glyph_bytes(pf, pf->maxwidth); + /* reserve one blank glyph that is guaranteed to be available, even + * when the font file is closed during USB */ + unsigned char *cache_buf = pf->buffer_start + bitmap_size; + size_t cache_size = pf->buffer_size - (cache_buf - pf->buffer_start); + ALIGN_BUFFER(cache_buf, cache_size, 2); + memset(pf->buffer_start, 0, bitmap_size); /* Initialise cache */ - font_cache_create(&pf->cache, pf->buffer_start, pf->buffer_size, bitmap_size); + font_cache_create(&pf->cache, cache_buf, cache_size, bitmap_size); } /* @@ -726,32 +785,65 @@ static void cache_create(struct font* pf) */ int font_get_width(struct font* pf, unsigned short char_code) { + int width; + struct font_cache_entry *e; + struct buflib_alloc_data *data = (struct buflib_alloc_data *) pf; + bool cache_only = data->disabled; + /* check input range*/ if (char_code < pf->firstchar || char_code >= pf->firstchar+pf->size) char_code = pf->defaultchar; char_code -= pf->firstchar; - return (pf->fd >= 0 && pf != &sysfont)? - font_cache_get(&pf->cache,char_code,load_cache_entry,pf)->width: - pf->width? pf->width[char_code]: pf->maxwidth; + if ((pf->fd >= 0 || cache_only) && pf != &sysfont + && (e = font_cache_get(&pf->cache,char_code,cache_only,load_cache_entry,pf))) + width = e->width; + else if (pf->width) + width = pf->width[char_code]; + else + width = pf->maxwidth; + + return width; } const unsigned char* font_get_bits(struct font* pf, unsigned short char_code) { const unsigned char* bits; + struct buflib_alloc_data *data; /* check input range*/ if (char_code < pf->firstchar || char_code >= pf->firstchar+pf->size) char_code = pf->defaultchar; char_code -= pf->firstchar; + data = (struct buflib_alloc_data *) pf; + if (pf->fd >= 0 && pf != &sysfont) { bits = - (unsigned char*)font_cache_get(&pf->cache,char_code,load_cache_entry, pf)->bitmap; + (unsigned char*)font_cache_get(&pf->cache, char_code, + false, load_cache_entry, data)->bitmap; + } + else if (data->disabled) + { + /* the font handle is closed, but the cache is intact. Attempt + * a lookup, which is very likely to succeed. Return a placeholder + * glyph on miss (again, this is very unlikely */ + struct font_cache_entry *e = font_cache_get(&pf->cache, char_code, + true, NULL, NULL); + if (LIKELY(e)) + bits = (unsigned char *) e->bitmap; + else + { + /* Could attempt to find a suitable fallback glyph from the same + * font. For now just return blank space which is + * reserved by cache_create() at buffer_start */ + bits = pf->buffer_start; + } } else { + /* This font is entirely in RAM */ bits = pf->bits; if (pf->offset) { diff --git a/firmware/font_cache.c b/firmware/font_cache.c index 4604d1524e..0b03edaf76 100644 --- a/firmware/font_cache.c +++ b/firmware/font_cache.c @@ -125,6 +125,7 @@ static int search(struct font_cache* fcache, struct font_cache_entry* font_cache_get( struct font_cache* fcache, unsigned short char_code, + bool cache_only, void (*callback) (struct font_cache_entry* p, void *callback_data), void *callback_data) { @@ -166,6 +167,8 @@ struct font_cache_entry* font_cache_get( } /* not found */ + if (cache_only) + return NULL; /* find index to replace */ short lru_handle_to_replace = fcache->_lru._head; diff --git a/firmware/include/font_cache.h b/firmware/include/font_cache.h index a4c959e336..1809720ed5 100644 --- a/firmware/include/font_cache.h +++ b/firmware/include/font_cache.h @@ -17,8 +17,10 @@ * KIND, either express or implied. * ****************************************************************************/ + #ifndef _FONT_CACHE_H_ #define _FONT_CACHE_H_ +#include #include "lru.h" /******************************************************************************* @@ -45,10 +47,16 @@ struct font_cache_entry /* Create an auto sized font cache from buf */ void font_cache_create( struct font_cache* fcache, void* buf, int buf_size, int bitmap_bytes_size); -/* Get font cache entry */ + +/* Get font cache entry for the given char_code + * + * cache_only: true if only a cache lookup should be performed and loading on miss should be avoided + * + * Note: With cache_only this can return NULL, which otherwise never happens */ struct font_cache_entry* font_cache_get( struct font_cache* fcache, unsigned short char_code, + bool cache_only, void (*callback) (struct font_cache_entry* p, void *callback_data), void *callback_data);