Factor out some drawing code.
[kugel-rb.git] / firmware / font.c
blob52c6ffae6a2b6d0fdb6bc07544791dde2fa05fc7
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (c) 2002 by Greg Haerr <greg@censoft.com>
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
20 ****************************************************************************/
22 * Rockbox startup font initialization
23 * This file specifies which fonts get compiled-in and
24 * loaded at startup, as well as their mapping into
25 * the FONT_SYSFIXED, FONT_UI and FONT_MP3 ids.
27 #include "config.h"
29 #include <stdio.h>
30 #include <string.h>
31 #include "inttypes.h"
32 #include "lcd.h"
33 #include "font.h"
34 #include "file.h"
35 #include "debug.h"
36 #include "panic.h"
37 #include "rbunicode.h"
38 #include "diacritic.h"
40 #define MAX_FONTSIZE_FOR_16_BIT_OFFSETS 0xFFDB
42 /* max static loadable font buffer size */
43 #ifndef MAX_FONT_SIZE
44 #if LCD_HEIGHT > 64
45 #if MEM > 2
46 #define MAX_FONT_SIZE 60000
47 #else
48 #define MAX_FONT_SIZE 10000
49 #endif
50 #else
51 #define MAX_FONT_SIZE 4000
52 #endif
53 #endif
55 #ifndef FONT_HEADER_SIZE
56 #define FONT_HEADER_SIZE 36
57 #endif
59 #define GLYPH_CACHE_FILE ROCKBOX_DIR"/.glyphcache"
61 #ifndef BOOTLOADER
62 /* Font cache includes */
63 #include "font_cache.h"
64 #include "lru.h"
65 #endif
67 #ifndef O_BINARY
68 #define O_BINARY 0
69 #endif
71 /* compiled-in font */
72 extern struct font sysfont;
74 #ifndef BOOTLOADER
76 /* structure filled in by font_load */
77 static struct font font_ui;
78 /* static buffer allocation structures */
79 static unsigned char main_buf[MAX_FONT_SIZE];
80 #ifdef HAVE_REMOTE_LCD
81 #define REMOTE_FONT_SIZE 10000
82 static struct font remote_font_ui;
83 static unsigned char remote_buf[REMOTE_FONT_SIZE];
84 #endif
86 /* system font table, in order of FONT_xxx definition */
87 static struct font* sysfonts[MAXFONTS] = { &sysfont, &font_ui, NULL};
90 /* Font cache structures */
91 static void cache_create(struct font* pf, int maxwidth, int height);
92 static void glyph_cache_load(struct font* pf);
93 /* End Font cache structures */
95 void font_init(void)
97 int i = SYSTEMFONTCOUNT;
98 while (i<MAXFONTS)
99 sysfonts[i++] = NULL;
100 font_reset(NULL);
103 /* Check if we have x bytes left in the file buffer */
104 #define HAVEBYTES(x) (pf->buffer_position + (x) <= pf->buffer_end)
106 /* Helper functions to read big-endian unaligned short or long from
107 the file buffer. Bounds-checking must be done in the calling
108 function.
111 static short readshort(struct font *pf)
113 unsigned short s;
115 s = *pf->buffer_position++ & 0xff;
116 s |= (*pf->buffer_position++ << 8);
117 return s;
120 static int32_t readlong(struct font *pf)
122 uint32_t l;
124 l = *pf->buffer_position++ & 0xff;
125 l |= *pf->buffer_position++ << 8;
126 l |= ((uint32_t)(*pf->buffer_position++)) << 16;
127 l |= ((uint32_t)(*pf->buffer_position++)) << 24;
128 return l;
131 void font_reset(struct font *pf)
133 unsigned char* buffer = NULL;
134 size_t buf_size = 0;
135 if (pf == NULL)
136 pf = &font_ui;
137 else
139 buffer = pf->buffer_start;
140 buf_size = pf->buffer_size;
142 memset(pf, 0, sizeof(struct font));
143 pf->fd = -1;
144 if (buffer)
146 pf->buffer_start = buffer;
147 pf->buffer_size = buf_size;
151 static struct font* font_load_header(struct font *pf)
153 /* Check we have enough data */
154 if (!HAVEBYTES(28))
155 return NULL;
157 /* read magic and version #*/
158 if (memcmp(pf->buffer_position, VERSION, 4) != 0)
159 return NULL;
161 pf->buffer_position += 4;
163 /* font info*/
164 pf->maxwidth = readshort(pf);
165 pf->height = readshort(pf);
166 pf->ascent = readshort(pf);
167 pf->buffer_position += 2; /* Skip padding */
168 pf->firstchar = readlong(pf);
169 pf->defaultchar = readlong(pf);
170 pf->size = readlong(pf);
172 /* get variable font data sizes*/
173 /* # words of bitmap_t*/
174 pf->bits_size = readlong(pf);
176 return pf;
178 /* Load memory font */
179 static struct font* font_load_in_memory(struct font* pf)
181 int32_t i, noffset, nwidth;
183 if (!HAVEBYTES(4))
184 return NULL;
186 /* # longs of offset*/
187 noffset = readlong(pf);
189 /* # bytes of width*/
190 nwidth = readlong(pf);
192 /* variable font data*/
193 pf->bits = (unsigned char *)pf->buffer_position;
194 pf->buffer_position += pf->bits_size*sizeof(unsigned char);
196 if (pf->bits_size < MAX_FONTSIZE_FOR_16_BIT_OFFSETS)
198 /* pad to 16-bit boundary */
199 pf->buffer_position = (unsigned char *)(((intptr_t)pf->buffer_position + 1) & ~1);
201 else
203 /* pad to 32-bit boundary*/
204 pf->buffer_position = (unsigned char *)(((intptr_t)pf->buffer_position + 3) & ~3);
207 if (noffset)
209 if (pf->bits_size < MAX_FONTSIZE_FOR_16_BIT_OFFSETS)
211 pf->long_offset = 0;
212 pf->offset = (uint16_t*)pf->buffer_position;
214 /* Check we have sufficient buffer */
215 if (!HAVEBYTES(noffset * sizeof(uint16_t)))
216 return NULL;
218 for (i=0; i<noffset; ++i)
220 ((uint16_t*)(pf->offset))[i] = (uint16_t)readshort(pf);
223 else
225 pf->long_offset = 1;
226 pf->offset = (uint16_t*)pf->buffer_position;
228 /* Check we have sufficient buffer */
229 if (!HAVEBYTES(noffset * sizeof(int32_t)))
230 return NULL;
232 for (i=0; i<noffset; ++i)
234 ((uint32_t*)(pf->offset))[i] = (uint32_t)readlong(pf);
238 else
239 pf->offset = NULL;
241 if (nwidth) {
242 pf->width = (unsigned char *)pf->buffer_position;
243 pf->buffer_position += nwidth*sizeof(unsigned char);
245 else
246 pf->width = NULL;
248 if (pf->buffer_position > pf->buffer_end)
249 return NULL;
251 return pf; /* success!*/
254 /* Load cached font */
255 static struct font* font_load_cached(struct font* pf)
257 uint32_t noffset, nwidth;
258 unsigned char* oldfileptr = pf->buffer_position;
260 if (!HAVEBYTES(2 * sizeof(int32_t)))
261 return NULL;
263 /* # longs of offset*/
264 noffset = readlong(pf);
266 /* # bytes of width*/
267 nwidth = readlong(pf);
269 /* We are now at the bitmap data, this is fixed at 36.. */
270 pf->bits = NULL;
272 /* Calculate offset to offset data */
273 pf->buffer_position += pf->bits_size * sizeof(unsigned char);
275 if (pf->bits_size < MAX_FONTSIZE_FOR_16_BIT_OFFSETS)
277 pf->long_offset = 0;
278 /* pad to 16-bit boundary */
279 pf->buffer_position = (unsigned char *)(((intptr_t)pf->buffer_position + 1) & ~1);
281 else
283 pf->long_offset = 1;
284 /* pad to 32-bit boundary*/
285 pf->buffer_position = (unsigned char *)(((intptr_t)pf->buffer_position + 3) & ~3);
288 if (noffset)
289 pf->file_offset_offset = (uint32_t)(pf->buffer_position - pf->buffer_start);
290 else
291 pf->file_offset_offset = 0;
293 /* Calculate offset to widths data */
294 if (pf->bits_size < MAX_FONTSIZE_FOR_16_BIT_OFFSETS)
295 pf->buffer_position += noffset * sizeof(uint16_t);
296 else
297 pf->buffer_position += noffset * sizeof(uint32_t);
299 if (nwidth)
300 pf->file_width_offset = (uint32_t)(pf->buffer_position - pf->buffer_start);
301 else
302 pf->file_width_offset = 0;
304 pf->buffer_position = oldfileptr;
306 /* Create the cache */
307 cache_create(pf, pf->maxwidth, pf->height);
309 return pf;
312 static bool internal_load_font(struct font* pf, const char *path,
313 char *buf, size_t buf_size)
315 int size;
317 /* save loaded glyphs */
318 glyph_cache_save(pf);
319 /* Close font file handle */
320 if (pf->fd >= 0)
321 close(pf->fd);
323 font_reset(pf);
325 /* open and read entire font file*/
326 pf->fd = open(path, O_RDONLY|O_BINARY);
328 if (pf->fd < 0) {
329 DEBUGF("Can't open font: %s\n", path);
330 return false;
333 /* Check file size */
334 size = filesize(pf->fd);
335 pf->buffer_start = buf;
336 pf->buffer_size = buf_size;
338 pf->buffer_position = buf;
340 if (size > pf->buffer_size)
342 read(pf->fd, pf->buffer_position, FONT_HEADER_SIZE);
343 pf->buffer_end = pf->buffer_position + FONT_HEADER_SIZE;
345 if (!font_load_header(pf))
347 DEBUGF("Failed font header load");
348 return false;
351 if (!font_load_cached(pf))
353 DEBUGF("Failed font cache load");
354 return false;
357 glyph_cache_load(pf);
359 else
361 read(pf->fd, pf->buffer_position, pf->buffer_size);
362 pf->buffer_end = pf->buffer_position + size;
363 close(pf->fd);
364 pf->fd = -1;
366 if (!font_load_header(pf))
368 DEBUGF("Failed font header load");
369 return false;
372 if (!font_load_in_memory(pf))
374 DEBUGF("Failed mem load");
375 return false;
378 return true;
381 #ifdef HAVE_REMOTE_LCD
382 /* Load a font into the special remote ui font slot */
383 int font_load_remoteui(const char* path)
385 struct font* pf = &remote_font_ui;
386 if (!path)
388 if (sysfonts[FONT_UI_REMOTE] && sysfonts[FONT_UI_REMOTE] != sysfonts[FONT_UI])
389 font_unload(FONT_UI_REMOTE);
390 sysfonts[FONT_UI_REMOTE] = NULL;
391 return FONT_UI;
393 if (!internal_load_font(pf, path, remote_buf, REMOTE_FONT_SIZE))
395 sysfonts[FONT_UI_REMOTE] = NULL;
396 return -1;
399 sysfonts[FONT_UI_REMOTE] = pf;
400 return FONT_UI_REMOTE;
402 #endif
404 /* read and load font into incore font structure,
405 * returns the font number on success, -1 on failure */
406 int font_load(struct font* pf, const char *path)
408 int font_id = -1;
409 char *buffer;
410 size_t buffer_size;
411 if (pf == NULL)
413 pf = &font_ui;
414 font_id = FONT_UI;
416 else
418 for (font_id = SYSTEMFONTCOUNT; font_id < MAXFONTS; font_id++)
420 if (sysfonts[font_id] == NULL)
421 break;
423 if (font_id == MAXFONTS)
424 return -1; /* too many fonts */
427 if (font_id == FONT_UI)
429 /* currently, font loading replaces earlier font allocation*/
430 buffer = (unsigned char *)(((intptr_t)main_buf + 3) & ~3);
431 buffer_size = MAX_FONT_SIZE;
433 else
435 buffer = pf->buffer_start;
436 buffer_size = pf->buffer_size;
439 if (!internal_load_font(pf, path, buffer, buffer_size))
440 return -1;
442 sysfonts[font_id] = pf;
443 return font_id; /* success!*/
446 void font_unload(int font_id)
448 struct font* pf = sysfonts[font_id];
449 if (font_id >= SYSTEMFONTCOUNT && pf)
451 if (pf->fd >= 0)
452 close(pf->fd);
453 sysfonts[font_id] = NULL;
458 * Return a pointer to an incore font structure.
459 * If the requested font isn't loaded/compiled-in,
460 * decrement the font number and try again.
462 struct font* font_get(int font)
464 struct font* pf;
466 while (1) {
467 pf = sysfonts[font];
468 if (pf && pf->height)
469 return pf;
470 if (--font < 0)
471 panicf("No font!");
476 * Reads an entry into cache entry
478 static void
479 load_cache_entry(struct font_cache_entry* p, void* callback_data)
481 struct font* pf = callback_data;
482 unsigned short char_code = p->_char_code;
483 unsigned char tmp[2];
485 if (pf->file_width_offset)
487 int width_offset = pf->file_width_offset + char_code;
488 lseek(pf->fd, width_offset, SEEK_SET);
489 read(pf->fd, &(p->width), 1);
491 else
493 p->width = pf->maxwidth;
496 int32_t bitmap_offset = 0;
498 if (pf->file_offset_offset)
500 int32_t offset = pf->file_offset_offset + char_code * (pf->long_offset ? sizeof(int32_t) : sizeof(int16_t));
501 lseek(pf->fd, offset, SEEK_SET);
502 read (pf->fd, tmp, 2);
503 bitmap_offset = tmp[0] | (tmp[1] << 8);
504 if (pf->long_offset) {
505 read (pf->fd, tmp, 2);
506 bitmap_offset |= (tmp[0] << 16) | (tmp[1] << 24);
509 else
511 bitmap_offset = ((pf->height + 7) / 8) * p->width * char_code;
514 int32_t file_offset = FONT_HEADER_SIZE + bitmap_offset;
515 lseek(pf->fd, file_offset, SEEK_SET);
517 int src_bytes = p->width * ((pf->height + 7) / 8);
518 read(pf->fd, p->bitmap, src_bytes);
522 * Converts cbuf into a font cache
524 static void cache_create(struct font* pf, int maxwidth, int height)
526 /* maximum size of rotated bitmap */
527 int bitmap_size = maxwidth * ((height + 7) / 8);
529 /* Initialise cache */
530 font_cache_create(&pf->cache, pf->buffer_start, pf->buffer_size, bitmap_size);
534 * Returns width of character
536 int font_get_width(struct font* pf, unsigned short char_code)
538 /* check input range*/
539 if (char_code < pf->firstchar || char_code >= pf->firstchar+pf->size)
540 char_code = pf->defaultchar;
541 char_code -= pf->firstchar;
543 return (pf->fd >= 0 && pf != &sysfont)?
544 font_cache_get(&pf->cache,char_code,load_cache_entry,pf)->width:
545 pf->width? pf->width[char_code]: pf->maxwidth;
548 const unsigned char* font_get_bits(struct font* pf, unsigned short char_code)
550 const unsigned char* bits;
552 /* check input range*/
553 if (char_code < pf->firstchar || char_code >= pf->firstchar+pf->size)
554 char_code = pf->defaultchar;
555 char_code -= pf->firstchar;
557 if (pf->fd >= 0 && pf != &sysfont)
559 bits =
560 (unsigned char*)font_cache_get(&pf->cache,char_code,load_cache_entry,pf)->bitmap;
562 else
564 bits = pf->bits;
565 if (pf->offset)
567 if (pf->bits_size < MAX_FONTSIZE_FOR_16_BIT_OFFSETS)
568 bits += ((uint16_t*)(pf->offset))[char_code];
569 else
570 bits += ((uint32_t*)(pf->offset))[char_code];
572 else
573 bits += ((pf->height + 7) / 8) * pf->maxwidth * char_code;
576 return bits;
578 static int cache_fd;
579 static void glyph_file_write(void* data)
581 struct font_cache_entry* p = data;
582 struct font* pf = &font_ui;
583 unsigned short ch;
584 unsigned char tmp[2];
586 ch = p->_char_code + pf->firstchar;
588 if (ch != 0xffff && cache_fd >= 0) {
589 tmp[0] = ch >> 8;
590 tmp[1] = ch & 0xff;
591 if (write(cache_fd, tmp, 2) != 2) {
592 close(cache_fd);
593 cache_fd = -1;
596 return;
599 /* save the char codes of the loaded glyphs to a file */
600 void glyph_cache_save(struct font* pf)
602 if (!pf)
603 pf = &font_ui;
604 if (pf->fd >= 0 && pf == &font_ui)
606 #ifdef WPSEDITOR
607 cache_fd = open(GLYPH_CACHE_FILE, O_WRONLY|O_CREAT|O_TRUNC);
608 #else
609 cache_fd = creat(GLYPH_CACHE_FILE);
610 #endif
611 if (cache_fd < 0) return;
613 lru_traverse(&pf->cache._lru, glyph_file_write);
615 if (cache_fd < 0)
617 close(cache_fd);
618 cache_fd = -1;
621 return;
624 static void glyph_cache_load(struct font* pf)
626 if (pf->fd >= 0) {
627 int fd;
628 unsigned char tmp[2];
629 unsigned short ch;
631 fd = open(GLYPH_CACHE_FILE, O_RDONLY|O_BINARY);
633 if (fd >= 0) {
635 while (read(fd, tmp, 2) == 2) {
636 ch = (tmp[0] << 8) | tmp[1];
637 font_get_bits(pf, ch);
640 close(fd);
641 } else {
642 /* load latin1 chars into cache */
643 ch = 256;
644 while (ch-- > 32)
645 font_get_bits(pf, ch);
648 return;
650 #else /* BOOTLOADER */
652 void font_init(void)
657 * Bootloader only supports the built-in sysfont.
659 struct font* font_get(int font)
661 (void)font;
662 return &sysfont;
666 * Returns width of character
668 int font_get_width(struct font* pf, unsigned short char_code)
670 /* check input range*/
671 if (char_code < pf->firstchar || char_code >= pf->firstchar+pf->size)
672 char_code = pf->defaultchar;
673 char_code -= pf->firstchar;
675 return pf->width? pf->width[char_code]: pf->maxwidth;
678 const unsigned char* font_get_bits(struct font* pf, unsigned short char_code)
680 const unsigned char* bits;
682 /* check input range*/
683 if (char_code < pf->firstchar || char_code >= pf->firstchar+pf->size)
684 char_code = pf->defaultchar;
685 char_code -= pf->firstchar;
687 /* assume small font with uint16_t offsets*/
688 bits = pf->bits + (pf->offset?
689 ((uint16_t*)(pf->offset))[char_code]:
690 (((pf->height + 7) / 8) * pf->maxwidth * char_code));
692 return bits;
695 #endif /* BOOTLOADER */
698 * Returns the stringsize of a given string.
700 int font_getstringsize(const unsigned char *str, int *w, int *h, int fontnumber)
702 struct font* pf = font_get(fontnumber);
703 unsigned short ch;
704 int width = 0;
706 for (str = utf8decode(str, &ch); ch != 0 ; str = utf8decode(str, &ch))
708 if (is_diacritic(ch, NULL))
709 continue;
711 /* get proportional width and glyph bits*/
712 width += font_get_width(pf,ch);
714 if ( w )
715 *w = width;
716 if ( h )
717 *h = pf->height;
718 return width;
721 /* -----------------------------------------------------------------
722 * vim: et sw=4 ts=8 sts=4 tw=78