| /*************************************************************************** |
| * __________ __ ___. |
| * Open \______ \ ____ ____ | | _\_ |__ _______ ___ |
| * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / |
| * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
| * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
| * \/ \/ \/ \/ \/ |
| * $Id$ |
| * |
| * Copyright (c) 2002 by Greg Haerr <greg@censoft.com> |
| * |
| * All files in this archive are subject to the GNU General Public License. |
| * See the file COPYING in the source tree root for full license agreement. |
| * |
| * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| * KIND, either express or implied. |
| * |
| ****************************************************************************/ |
| /* |
| * Rockbox startup font initialization |
| * This file specifies which fonts get compiled-in and |
| * loaded at startup, as well as their mapping into |
| * the FONT_SYSFIXED, FONT_UI and FONT_MP3 ids. |
| */ |
| #include "config.h" |
| |
| #if defined(HAVE_LCD_BITMAP) || defined(SIMULATOR) |
| |
| #include <stdio.h> |
| #include <string.h> |
| #include "inttypes.h" |
| #include "lcd.h" |
| #include "font.h" |
| #include "file.h" |
| #include "debug.h" |
| #include "panic.h" |
| #include "rbunicode.h" |
| /* Font cache includes */ |
| #include "font_cache.h" |
| #include "lru.h" |
| |
| #ifndef O_BINARY |
| #define O_BINARY 0 |
| #endif |
| |
| /* compiled-in font */ |
| extern struct font sysfont; |
| |
| /* structure filled in by font_load */ |
| static struct font font_ui; |
| |
| /* system font table, in order of FONT_xxx definition */ |
| static struct font* const sysfonts[MAXFONTS] = { &sysfont, &font_ui }; |
| |
| /* static buffer allocation structures */ |
| static unsigned char mbuf[MAX_FONT_SIZE]; |
| static unsigned char *freeptr = mbuf; |
| static unsigned char *fileptr; |
| static unsigned char *eofptr; |
| |
| /* Font cache structures */ |
| static struct font_cache font_cache_ui; |
| static int fnt_file = -1; /* >=0 if font is cached */ |
| uint32_t file_width_offset; /* offset to file width data */ |
| uint32_t file_offset_offset; /* offset to file offset data */ |
| static void cache_create(int maxwidth, int height); |
| static int long_offset = 0; |
| static int glyph_file; |
| /* End Font cache structures */ |
| |
| static void glyph_cache_load(void); |
| |
| void font_init(void) |
| { |
| memset(&font_ui, 0, sizeof(struct font)); |
| } |
| |
| /* Check if we have x bytes left in the file buffer */ |
| #define HAVEBYTES(x) (fileptr + (x) <= eofptr) |
| |
| /* Helper functions to read big-endian unaligned short or long from |
| the file buffer. Bounds-checking must be done in the calling |
| function. |
| */ |
| |
| static short readshort(void) |
| { |
| unsigned short s; |
| |
| s = *fileptr++ & 0xff; |
| s |= (*fileptr++ << 8); |
| return s; |
| } |
| |
| static int32_t readlong(void) |
| { |
| uint32_t l; |
| |
| l = *fileptr++ & 0xff; |
| l |= *fileptr++ << 8; |
| l |= ((uint32_t)(*fileptr++)) << 16; |
| l |= ((uint32_t)(*fileptr++)) << 24; |
| return l; |
| } |
| |
| /* read count bytes*/ |
| static void readstr(char *buf, int count) |
| { |
| while (count--) |
| *buf++ = *fileptr++; |
| } |
| |
| void font_reset(void) |
| { |
| memset(&font_ui, 0, sizeof(struct font)); |
| } |
| |
| static struct font* font_load_header(struct font *pf) |
| { |
| char version[4+1]; |
| |
| /* Check we have enough data */ |
| if (!HAVEBYTES(28)) |
| return NULL; |
| |
| /* read magic and version #*/ |
| memset(version, 0, sizeof(version)); |
| readstr(version, 4); |
| |
| if (strcmp(version, VERSION) != 0) |
| return NULL; |
| |
| /* font info*/ |
| pf->maxwidth = readshort(); |
| pf->height = readshort(); |
| pf->ascent = readshort(); |
| fileptr += 2; /* Skip padding */ |
| pf->firstchar = readlong(); |
| pf->defaultchar = readlong(); |
| pf->size = readlong(); |
| |
| /* get variable font data sizes*/ |
| /* # words of bitmap_t*/ |
| pf->bits_size = readlong(); |
| |
| return pf; |
| } |
| /* Load memory font */ |
| static struct font* font_load_in_memory(struct font* pf) |
| { |
| int32_t i, noffset, nwidth; |
| |
| if (!HAVEBYTES(4)) |
| return NULL; |
| |
| /* # longs of offset*/ |
| noffset = readlong(); |
| |
| /* # bytes of width*/ |
| nwidth = readlong(); |
| |
| /* variable font data*/ |
| pf->bits = (unsigned char *)fileptr; |
| fileptr += pf->bits_size*sizeof(unsigned char); |
| |
| if ( pf->bits_size < 0xFFDB ) |
| { |
| /* pad to 16-bit boundary */ |
| fileptr = (unsigned char *)(((int32_t)fileptr + 1) & ~1); |
| } |
| else |
| { |
| /* pad to 32-bit boundary*/ |
| fileptr = (unsigned char *)(((int32_t)fileptr + 3) & ~3); |
| } |
| |
| if (noffset) |
| { |
| if ( pf->bits_size < 0xFFDB ) |
| { |
| long_offset = 0; |
| pf->offset = (unsigned short *)fileptr; |
| |
| /* Check we have sufficient buffer */ |
| if (!HAVEBYTES(noffset * sizeof(short))) |
| return NULL; |
| |
| for (i=0; i<noffset; ++i) |
| { |
| ((unsigned short*)(pf->offset))[i] = (unsigned short)readshort(); |
| } |
| } |
| else |
| { |
| long_offset = 1; |
| pf->offset = (unsigned short *)fileptr; |
| |
| /* Check we have sufficient buffer */ |
| if (!HAVEBYTES(noffset * sizeof(int32_t))) |
| return NULL; |
| |
| for (i=0; i<noffset; ++i) |
| { |
| ((uint32_t*)(pf->offset))[i] = (uint32_t)readlong(); |
| } |
| } |
| } |
| else |
| pf->offset = NULL; |
| |
| if (nwidth) { |
| pf->width = (unsigned char *)fileptr; |
| fileptr += nwidth*sizeof(unsigned char); |
| } |
| else |
| pf->width = NULL; |
| |
| if (fileptr > eofptr) |
| return NULL; |
| |
| return pf; /* success!*/ |
| } |
| |
| /* Load cached font */ |
| static struct font* font_load_cached(struct font* pf) |
| { |
| uint32_t noffset, nwidth; |
| unsigned char* oldfileptr = fileptr; |
| |
| if (!HAVEBYTES(2 * sizeof(int32_t))) |
| return NULL; |
| |
| /* # longs of offset*/ |
| noffset = readlong(); |
| |
| /* # bytes of width*/ |
| nwidth = readlong(); |
| |
| /* We are now at the bitmap data, this is fixed at 36.. */ |
| pf->bits = NULL; |
| |
| /* Calculate offset to offset data */ |
| fileptr += pf->bits_size * sizeof(unsigned char); |
| |
| if ( pf->bits_size < 0xFFDB ) |
| { |
| long_offset = 0; |
| /* pad to 16-bit boundary */ |
| fileptr = (unsigned char *)(((int32_t)fileptr + 1) & ~1); |
| } |
| else |
| { |
| long_offset = 1; |
| /* pad to 32-bit boundary*/ |
| fileptr = (unsigned char *)(((int32_t)fileptr + 3) & ~3); |
| } |
| |
| if (noffset) |
| file_offset_offset = (uint32_t)(fileptr - freeptr); |
| else |
| file_offset_offset = 0; |
| |
| /* Calculate offset to widths data */ |
| if ( pf->bits_size < 0xFFDB ) |
| fileptr += noffset * sizeof(unsigned short); |
| else |
| fileptr += noffset * sizeof(uint32_t); |
| |
| if (nwidth) |
| file_width_offset = (uint32_t)(fileptr - freeptr); |
| else |
| file_width_offset = 0; |
| |
| fileptr = oldfileptr; |
| |
| /* Create the cache */ |
| cache_create(pf->maxwidth, pf->height); |
| |
| return pf; |
| } |
| |
| /* read and load font into incore font structure*/ |
| struct font* font_load(const char *path) |
| { |
| int size; |
| struct font* pf = &font_ui; |
| |
| /* save loaded glyphs */ |
| glyph_cache_save(); |
| |
| /* Close font file handle */ |
| if (fnt_file >= 0) |
| close(fnt_file); |
| |
| /* open and read entire font file*/ |
| fnt_file = open(path, O_RDONLY|O_BINARY); |
| |
| if (fnt_file < 0) { |
| DEBUGF("Can't open font: %s\n", path); |
| return NULL; |
| } |
| |
| /* Check file size */ |
| size = filesize(fnt_file); |
| |
| font_reset(); |
| |
| /* currently, font loading replaces earlier font allocation*/ |
| freeptr = (unsigned char *)(((int)mbuf + 3) & ~3); |
| fileptr = freeptr; |
| |
| |
| if (size > MAX_FONT_SIZE) |
| { |
| read(fnt_file, fileptr, FONT_HEADER_SIZE); |
| eofptr = fileptr + FONT_HEADER_SIZE; |
| |
| if (!font_load_header(pf)) |
| { |
| DEBUGF("Failed font header load"); |
| return NULL; |
| } |
| |
| if (!font_load_cached(pf)) |
| { |
| DEBUGF("Failed font cache load"); |
| return NULL; |
| } |
| |
| glyph_cache_load(); |
| } |
| else |
| { |
| read(fnt_file, fileptr, MAX_FONT_SIZE); |
| eofptr = fileptr + size; |
| close(fnt_file); |
| fnt_file = -1; |
| |
| if (!font_load_header(pf)) |
| { |
| DEBUGF("Failed font header load"); |
| return NULL; |
| } |
| |
| if (!font_load_in_memory(pf)) |
| { |
| DEBUGF("Failed mem load"); |
| return NULL; |
| } |
| } |
| |
| /* no need for multiple font loads currently*/ |
| /*freeptr += filesize;*/ |
| /*freeptr = (unsigned char *)(freeptr + 3) & ~3;*/ /* pad freeptr*/ |
| |
| return pf; /* success!*/ |
| } |
| |
| /* |
| * Return a pointer to an incore font structure. |
| * If the requested font isn't loaded/compiled-in, |
| * decrement the font number and try again. |
| */ |
| struct font* font_get(int font) |
| { |
| struct font* pf; |
| |
| if (font >= MAXFONTS) |
| font = 0; |
| |
| while (1) { |
| pf = sysfonts[font]; |
| if (pf && pf->height) |
| return pf; |
| if (--font < 0) |
| panicf("No font!"); |
| } |
| } |
| /* |
| * Returns the stringsize of a given string. |
| */ |
| int font_getstringsize(const unsigned char *str, int *w, int *h, int fontnumber) |
| { |
| struct font* pf = font_get(fontnumber); |
| unsigned short ch; |
| int width = 0; |
| |
| for (str = utf8decode(str, &ch); ch != 0 ; str = utf8decode(str, &ch)) |
| { |
| |
| /* get proportional width and glyph bits*/ |
| width += font_get_width(pf,ch); |
| } |
| if ( w ) |
| *w = width; |
| if ( h ) |
| *h = pf->height; |
| return width; |
| } |
| |
| /* |
| * Reads an entry into cache entry |
| */ |
| static void |
| load_cache_entry(struct font_cache_entry* p, void* callback_data) |
| { |
| struct font* pf = callback_data; |
| unsigned short char_code = p->_char_code; |
| unsigned char tmp[2]; |
| |
| if (file_width_offset) |
| { |
| int width_offset = file_width_offset + char_code; |
| lseek(fnt_file, width_offset, SEEK_SET); |
| read(fnt_file, &(p->width), 1); |
| } |
| else |
| { |
| p->width = pf->maxwidth; |
| } |
| |
| int32_t bitmap_offset = 0; |
| |
| if (file_offset_offset) |
| { |
| int32_t offset = file_offset_offset + char_code * (long_offset ? sizeof(int32_t) : sizeof(short)); |
| lseek(fnt_file, offset, SEEK_SET); |
| read (fnt_file, tmp, 2); |
| bitmap_offset = tmp[0] | (tmp[1] << 8); |
| if (long_offset) { |
| read (fnt_file, tmp, 2); |
| bitmap_offset |= (tmp[0] << 16) | (tmp[1] << 24); |
| } |
| } |
| else |
| { |
| bitmap_offset = ((pf->height + 7) / 8) * p->width * char_code; |
| } |
| |
| int32_t file_offset = FONT_HEADER_SIZE + bitmap_offset; |
| lseek(fnt_file, file_offset, SEEK_SET); |
| |
| int src_bytes = p->width * ((pf->height + 7) / 8); |
| read(fnt_file, p->bitmap, src_bytes); |
| } |
| |
| /* |
| * Converts cbuf into a font cache |
| */ |
| static void cache_create(int maxwidth, int height) |
| { |
| /* maximum size of rotated bitmap */ |
| int bitmap_size = maxwidth * ((height + 7) / 8); |
| |
| /* Initialise cache */ |
| font_cache_create(&font_cache_ui, mbuf, MAX_FONT_SIZE, bitmap_size); |
| } |
| |
| /* |
| * Returns width of character |
| */ |
| int font_get_width(struct font* pf, unsigned short char_code) |
| { |
| /* check input range*/ |
| if (char_code < pf->firstchar || char_code >= pf->firstchar+pf->size) |
| char_code = pf->defaultchar; |
| char_code -= pf->firstchar; |
| |
| return (fnt_file >= 0 && pf != &sysfont)? |
| font_cache_get(&font_cache_ui,char_code,load_cache_entry,pf)->width: |
| pf->width? pf->width[char_code]: pf->maxwidth; |
| } |
| |
| const unsigned char* font_get_bits(struct font* pf, unsigned short char_code) |
| { |
| const unsigned char* bits; |
| |
| /* check input range*/ |
| if (char_code < pf->firstchar || char_code >= pf->firstchar+pf->size) |
| char_code = pf->defaultchar; |
| char_code -= pf->firstchar; |
| |
| if (fnt_file >= 0 && pf != &sysfont) |
| { |
| bits = |
| (unsigned char*)font_cache_get(&font_cache_ui,char_code,load_cache_entry,pf)->bitmap; |
| } |
| else |
| { |
| bits = pf->bits + (pf->offset? |
| pf->offset[char_code]: |
| (((pf->height + 7) / 8) * pf->maxwidth * char_code)); |
| } |
| |
| return bits; |
| } |
| |
| static void glyph_file_write(void* data) |
| { |
| struct font_cache_entry* p = data; |
| struct font* pf = &font_ui; |
| unsigned short ch; |
| unsigned char tmp[2]; |
| |
| ch = p->_char_code + pf->firstchar; |
| |
| if (ch != 0xffff && glyph_file >= 0) { |
| tmp[0] = ch >> 8; |
| tmp[1] = ch & 0xff; |
| if (write(glyph_file, tmp, 2) != 2) { |
| close(glyph_file); |
| glyph_file = -1; |
| } |
| } |
| return; |
| } |
| |
| /* save the char codes of the loaded glyphs to a file */ |
| void glyph_cache_save(void) |
| { |
| |
| if (fnt_file >= 0) { |
| |
| glyph_file = creat(GLYPH_CACHE_FILE); |
| |
| if (glyph_file < 0) return; |
| |
| lru_traverse(&font_cache_ui._lru, glyph_file_write); |
| |
| if (glyph_file >= 0) |
| close(glyph_file); |
| } |
| return; |
| } |
| |
| static void glyph_cache_load(void) |
| { |
| if (fnt_file >= 0) { |
| |
| int fd; |
| unsigned char tmp[2]; |
| unsigned short ch; |
| struct font* pf = &font_ui; |
| |
| fd = open(GLYPH_CACHE_FILE, O_RDONLY|O_BINARY); |
| |
| if (fd >= 0) { |
| |
| while (read(fd, tmp, 2) == 2) { |
| ch = (tmp[0] << 8) | tmp[1]; |
| font_get_bits(pf, ch); |
| } |
| |
| close(fd); |
| } else { |
| /* load latin1 chars into cache */ |
| ch = 256; |
| while (ch-- > 32) |
| font_get_bits(pf, ch); |
| } |
| } |
| return; |
| } |
| |
| #endif /* HAVE_LCD_BITMAP */ |
| |
| /* ----------------------------------------------------------------- |
| * vim: et sw=4 ts=8 sts=4 tw=78 |
| */ |