Remove solved issue
[maemo-rb.git] / firmware / font.c
blob45ddef3afe4e70c66586a2fc038c7fc091288a5d
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 <stdlib.h>
32 #include "inttypes.h"
33 #include "lcd.h"
34 #include "system.h"
35 #include "font.h"
36 #include "file.h"
37 #include "debug.h"
38 #include "panic.h"
39 #include "rbunicode.h"
40 #include "diacritic.h"
41 #include "rbpaths.h"
43 #define MAX_FONTSIZE_FOR_16_BIT_OFFSETS 0xFFDB
45 /* max static loadable font buffer size */
46 #ifndef MAX_FONT_SIZE
47 #if LCD_HEIGHT > 64
48 #if MEMORYSIZE > 2
49 #define MAX_FONT_SIZE 60000
50 #else
51 #define MAX_FONT_SIZE 10000
52 #endif
53 #else
54 #define MAX_FONT_SIZE 4000
55 #endif
56 #endif
58 #ifndef FONT_HEADER_SIZE
59 #define FONT_HEADER_SIZE 36
60 #endif
62 #ifndef BOOTLOADER
63 /* Font cache includes */
64 #include "font_cache.h"
65 #include "lru.h"
66 #endif
68 #ifndef O_BINARY
69 #define O_BINARY 0
70 #endif
72 /* compiled-in font */
73 extern struct font sysfont;
75 #ifndef BOOTLOADER
77 /* structure filled in by font_load */
78 static struct font font_ui;
79 /* static buffer allocation structures */
80 static unsigned char main_buf[MAX_FONT_SIZE] CACHEALIGN_ATTR;
81 #ifdef HAVE_REMOTE_LCD
82 #define REMOTE_FONT_SIZE 10000
83 static struct font remote_font_ui;
84 static unsigned char remote_buf[REMOTE_FONT_SIZE] CACHEALIGN_ATTR;
85 #endif
87 /* system font table, in order of FONT_xxx definition */
88 static struct font* sysfonts[MAXFONTS] = { &sysfont, &font_ui, NULL};
91 /* Font cache structures */
92 static void cache_create(struct font* pf, int maxwidth, int height);
93 static void glyph_cache_load(struct font* pf);
94 /* End Font cache structures */
96 void font_init(void)
98 int i = SYSTEMFONTCOUNT;
99 while (i<MAXFONTS)
100 sysfonts[i++] = NULL;
101 font_reset(NULL);
102 #ifdef HAVE_REMOTE_LCD
103 font_reset(&remote_font_ui);
104 #endif
107 /* Check if we have x bytes left in the file buffer */
108 #define HAVEBYTES(x) (pf->buffer_position + (x) <= pf->buffer_end)
110 /* Helper functions to read big-endian unaligned short or long from
111 the file buffer. Bounds-checking must be done in the calling
112 function.
115 static short readshort(struct font *pf)
117 unsigned short s;
119 s = *pf->buffer_position++ & 0xff;
120 s |= (*pf->buffer_position++ << 8);
121 return s;
124 static int32_t readlong(struct font *pf)
126 uint32_t l;
128 l = *pf->buffer_position++ & 0xff;
129 l |= *pf->buffer_position++ << 8;
130 l |= ((uint32_t)(*pf->buffer_position++)) << 16;
131 l |= ((uint32_t)(*pf->buffer_position++)) << 24;
132 return l;
135 void font_reset(struct font *pf)
137 unsigned char* buffer = NULL;
138 size_t buf_size = 0;
139 if (pf == NULL)
140 pf = &font_ui;
141 else
143 buffer = pf->buffer_start;
144 buf_size = pf->buffer_size;
146 memset(pf, 0, sizeof(struct font));
147 pf->fd = -1;
148 if (buffer)
150 pf->buffer_start = buffer;
151 pf->buffer_size = buf_size;
155 static struct font* font_load_header(struct font *pf)
157 /* Check we have enough data */
158 if (!HAVEBYTES(28))
159 return NULL;
161 /* read magic and version #*/
162 if (memcmp(pf->buffer_position, VERSION, 4) != 0)
163 return NULL;
165 pf->buffer_position += 4;
167 /* font info*/
168 pf->maxwidth = readshort(pf);
169 pf->height = readshort(pf);
170 pf->ascent = readshort(pf);
171 pf->buffer_position += 2; /* Skip padding */
172 pf->firstchar = readlong(pf);
173 pf->defaultchar = readlong(pf);
174 pf->size = readlong(pf);
176 /* get variable font data sizes*/
177 /* # words of bitmap_t*/
178 pf->bits_size = readlong(pf);
180 return pf;
182 /* Load memory font */
183 static struct font* font_load_in_memory(struct font* pf)
185 int32_t i, noffset, nwidth;
187 if (!HAVEBYTES(4))
188 return NULL;
190 /* # longs of offset*/
191 noffset = readlong(pf);
193 /* # bytes of width*/
194 nwidth = readlong(pf);
196 /* variable font data*/
197 pf->bits = (unsigned char *)pf->buffer_position;
198 pf->buffer_position += pf->bits_size*sizeof(unsigned char);
200 if (pf->bits_size < MAX_FONTSIZE_FOR_16_BIT_OFFSETS)
202 /* pad to 16-bit boundary */
203 pf->buffer_position = (unsigned char *)(((intptr_t)pf->buffer_position + 1) & ~1);
205 else
207 /* pad to 32-bit boundary*/
208 pf->buffer_position = (unsigned char *)(((intptr_t)pf->buffer_position + 3) & ~3);
211 if (noffset)
213 if (pf->bits_size < MAX_FONTSIZE_FOR_16_BIT_OFFSETS)
215 pf->long_offset = 0;
216 pf->offset = (uint16_t*)pf->buffer_position;
218 /* Check we have sufficient buffer */
219 if (!HAVEBYTES(noffset * sizeof(uint16_t)))
220 return NULL;
222 for (i=0; i<noffset; ++i)
224 ((uint16_t*)(pf->offset))[i] = (uint16_t)readshort(pf);
227 else
229 pf->long_offset = 1;
230 pf->offset = (uint16_t*)pf->buffer_position;
232 /* Check we have sufficient buffer */
233 if (!HAVEBYTES(noffset * sizeof(int32_t)))
234 return NULL;
236 for (i=0; i<noffset; ++i)
238 ((uint32_t*)(pf->offset))[i] = (uint32_t)readlong(pf);
242 else
243 pf->offset = NULL;
245 if (nwidth) {
246 pf->width = (unsigned char *)pf->buffer_position;
247 pf->buffer_position += nwidth*sizeof(unsigned char);
249 else
250 pf->width = NULL;
252 if (pf->buffer_position > pf->buffer_end)
253 return NULL;
255 return pf; /* success!*/
258 /* Load cached font */
259 static struct font* font_load_cached(struct font* pf)
261 uint32_t noffset, nwidth;
262 unsigned char* oldfileptr = pf->buffer_position;
264 if (!HAVEBYTES(2 * sizeof(int32_t)))
265 return NULL;
267 /* # longs of offset*/
268 noffset = readlong(pf);
270 /* # bytes of width*/
271 nwidth = readlong(pf);
273 /* We are now at the bitmap data, this is fixed at 36.. */
274 pf->bits = NULL;
276 /* Calculate offset to offset data */
277 pf->buffer_position += pf->bits_size * sizeof(unsigned char);
279 if (pf->bits_size < MAX_FONTSIZE_FOR_16_BIT_OFFSETS)
281 pf->long_offset = 0;
282 /* pad to 16-bit boundary */
283 pf->buffer_position = (unsigned char *)(((intptr_t)pf->buffer_position + 1) & ~1);
285 else
287 pf->long_offset = 1;
288 /* pad to 32-bit boundary*/
289 pf->buffer_position = (unsigned char *)(((intptr_t)pf->buffer_position + 3) & ~3);
292 if (noffset)
293 pf->file_offset_offset = (uint32_t)(pf->buffer_position - pf->buffer_start);
294 else
295 pf->file_offset_offset = 0;
297 /* Calculate offset to widths data */
298 if (pf->bits_size < MAX_FONTSIZE_FOR_16_BIT_OFFSETS)
299 pf->buffer_position += noffset * sizeof(uint16_t);
300 else
301 pf->buffer_position += noffset * sizeof(uint32_t);
303 if (nwidth)
304 pf->file_width_offset = (uint32_t)(pf->buffer_position - pf->buffer_start);
305 else
306 pf->file_width_offset = 0;
308 pf->buffer_position = oldfileptr;
310 /* Create the cache */
311 cache_create(pf, pf->maxwidth, pf->height);
313 return pf;
316 static bool internal_load_font(struct font* pf, const char *path,
317 char *buf, size_t buf_size)
319 int size;
321 /* save loaded glyphs */
322 glyph_cache_save(pf);
323 /* Close font file handle */
324 if (pf->fd >= 0)
325 close(pf->fd);
327 font_reset(pf);
329 /* open and read entire font file*/
330 pf->fd = open(path, O_RDONLY|O_BINARY);
332 if (pf->fd < 0) {
333 DEBUGF("Can't open font: %s\n", path);
334 return false;
337 /* Check file size */
338 size = filesize(pf->fd);
339 pf->buffer_start = buf;
340 pf->buffer_size = buf_size;
342 pf->buffer_position = buf;
344 if (size > pf->buffer_size)
346 read(pf->fd, pf->buffer_position, FONT_HEADER_SIZE);
347 pf->buffer_end = pf->buffer_position + FONT_HEADER_SIZE;
349 if (!font_load_header(pf))
351 DEBUGF("Failed font header load");
352 close(pf->fd);
353 pf->fd = -1;
354 return false;
357 if (!font_load_cached(pf))
359 DEBUGF("Failed font cache load");
360 close(pf->fd);
361 pf->fd = -1;
362 return false;
365 glyph_cache_load(pf);
367 else
369 read(pf->fd, pf->buffer_position, pf->buffer_size);
370 pf->buffer_end = pf->buffer_position + size;
371 close(pf->fd);
372 pf->fd = -1;
374 if (!font_load_header(pf))
376 DEBUGF("Failed font header load");
377 return false;
380 if (!font_load_in_memory(pf))
382 DEBUGF("Failed mem load");
383 return false;
386 return true;
389 #ifdef HAVE_REMOTE_LCD
390 /* Load a font into the special remote ui font slot */
391 int font_load_remoteui(const char* path)
393 struct font* pf = &remote_font_ui;
394 if (!path)
396 if (sysfonts[FONT_UI_REMOTE] && sysfonts[FONT_UI_REMOTE] != sysfonts[FONT_UI])
397 font_unload(FONT_UI_REMOTE);
398 sysfonts[FONT_UI_REMOTE] = NULL;
399 return FONT_UI;
401 if (!internal_load_font(pf, path, remote_buf, REMOTE_FONT_SIZE))
403 sysfonts[FONT_UI_REMOTE] = NULL;
404 return -1;
407 sysfonts[FONT_UI_REMOTE] = pf;
408 return FONT_UI_REMOTE;
410 #endif
412 /* read and load font into incore font structure,
413 * returns the font number on success, -1 on failure */
414 int font_load(struct font* pf, const char *path)
416 int font_id = -1;
417 char *buffer;
418 size_t buffer_size;
419 if (pf == NULL)
421 pf = &font_ui;
422 font_id = FONT_UI;
424 else
426 for (font_id = SYSTEMFONTCOUNT; font_id < MAXFONTS; font_id++)
428 if (sysfonts[font_id] == NULL)
429 break;
431 if (font_id == MAXFONTS)
432 return -1; /* too many fonts */
435 if (font_id == FONT_UI)
437 /* currently, font loading replaces earlier font allocation*/
438 buffer = (unsigned char *)(((intptr_t)main_buf + 3) & ~3);
439 buffer_size = MAX_FONT_SIZE;
441 else
443 buffer = pf->buffer_start;
444 buffer_size = pf->buffer_size;
447 if (!internal_load_font(pf, path, buffer, buffer_size))
448 return -1;
450 sysfonts[font_id] = pf;
451 return font_id; /* success!*/
454 void font_unload(int font_id)
456 struct font* pf = sysfonts[font_id];
457 if (font_id >= SYSTEMFONTCOUNT && pf)
459 if (pf->fd >= 0)
460 close(pf->fd);
461 sysfonts[font_id] = NULL;
466 * Return a pointer to an incore font structure.
467 * If the requested font isn't loaded/compiled-in,
468 * decrement the font number and try again.
470 struct font* font_get(int font)
472 struct font* pf;
474 while (1) {
475 pf = sysfonts[font];
476 if (pf && pf->height)
477 return pf;
478 if (--font < 0)
479 panicf("No font!");
484 * Reads an entry into cache entry
486 static void
487 load_cache_entry(struct font_cache_entry* p, void* callback_data)
489 struct font* pf = callback_data;
490 unsigned short char_code = p->_char_code;
491 unsigned char tmp[2];
493 if (pf->file_width_offset)
495 int width_offset = pf->file_width_offset + char_code;
496 lseek(pf->fd, width_offset, SEEK_SET);
497 read(pf->fd, &(p->width), 1);
499 else
501 p->width = pf->maxwidth;
504 int32_t bitmap_offset = 0;
506 if (pf->file_offset_offset)
508 int32_t offset = pf->file_offset_offset + char_code * (pf->long_offset ? sizeof(int32_t) : sizeof(int16_t));
509 lseek(pf->fd, offset, SEEK_SET);
510 read (pf->fd, tmp, 2);
511 bitmap_offset = tmp[0] | (tmp[1] << 8);
512 if (pf->long_offset) {
513 read (pf->fd, tmp, 2);
514 bitmap_offset |= (tmp[0] << 16) | (tmp[1] << 24);
517 else
519 bitmap_offset = ((pf->height + 7) / 8) * p->width * char_code;
522 int32_t file_offset = FONT_HEADER_SIZE + bitmap_offset;
523 lseek(pf->fd, file_offset, SEEK_SET);
525 int src_bytes = p->width * ((pf->height + 7) / 8);
526 read(pf->fd, p->bitmap, src_bytes);
530 * Converts cbuf into a font cache
532 static void cache_create(struct font* pf, int maxwidth, int height)
534 /* maximum size of rotated bitmap */
535 int bitmap_size = maxwidth * ((height + 7) / 8);
537 /* Initialise cache */
538 font_cache_create(&pf->cache, pf->buffer_start, pf->buffer_size, bitmap_size);
542 * Returns width of character
544 int font_get_width(struct font* pf, unsigned short char_code)
546 /* check input range*/
547 if (char_code < pf->firstchar || char_code >= pf->firstchar+pf->size)
548 char_code = pf->defaultchar;
549 char_code -= pf->firstchar;
551 return (pf->fd >= 0 && pf != &sysfont)?
552 font_cache_get(&pf->cache,char_code,load_cache_entry,pf)->width:
553 pf->width? pf->width[char_code]: pf->maxwidth;
556 const unsigned char* font_get_bits(struct font* pf, unsigned short char_code)
558 const unsigned char* bits;
560 /* check input range*/
561 if (char_code < pf->firstchar || char_code >= pf->firstchar+pf->size)
562 char_code = pf->defaultchar;
563 char_code -= pf->firstchar;
565 if (pf->fd >= 0 && pf != &sysfont)
567 bits =
568 (unsigned char*)font_cache_get(&pf->cache,char_code,load_cache_entry,pf)->bitmap;
570 else
572 bits = pf->bits;
573 if (pf->offset)
575 if (pf->bits_size < MAX_FONTSIZE_FOR_16_BIT_OFFSETS)
576 bits += ((uint16_t*)(pf->offset))[char_code];
577 else
578 bits += ((uint32_t*)(pf->offset))[char_code];
580 else
581 bits += ((pf->height + 7) / 8) * pf->maxwidth * char_code;
584 return bits;
586 static int cache_fd;
587 static void glyph_file_write(void* data)
589 struct font_cache_entry* p = data;
590 struct font* pf = &font_ui;
591 unsigned short ch;
592 unsigned char tmp[2];
594 if ( p->_char_code == 0xffff )
595 return;
597 ch = p->_char_code + pf->firstchar;
599 if ( cache_fd >= 0) {
600 tmp[0] = ch >> 8;
601 tmp[1] = ch & 0xff;
602 if (write(cache_fd, tmp, 2) != 2) {
603 close(cache_fd);
604 cache_fd = -1;
607 return;
610 /* save the char codes of the loaded glyphs to a file */
611 void glyph_cache_save(struct font* pf)
613 if (!pf)
614 pf = &font_ui;
615 if (pf->fd >= 0 && pf == &font_ui)
617 cache_fd = open(GLYPH_CACHE_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0666);
618 if (cache_fd < 0)
619 return;
621 lru_traverse(&pf->cache._lru, glyph_file_write);
623 if (cache_fd >= 0)
625 close(cache_fd);
626 cache_fd = -1;
629 return;
632 int font_glyphs_to_bufsize(const char *path, int glyphs)
634 struct font f;
635 int bufsize;
636 char buf[FONT_HEADER_SIZE];
638 f.buffer_start = buf;
639 f.buffer_size = sizeof(buf);
640 f.buffer_position = buf;
642 f.fd = open(path, O_RDONLY|O_BINARY);
643 if(f.fd < 0)
644 return 0;
646 read(f.fd, f.buffer_position, FONT_HEADER_SIZE);
647 f.buffer_end = f.buffer_position + FONT_HEADER_SIZE;
649 if( !font_load_header(&f) )
651 close(f.fd);
652 return 0;
654 close(f.fd);
656 bufsize = LRU_SLOT_OVERHEAD + sizeof(struct font_cache_entry) +
657 sizeof( unsigned short);
658 bufsize += f.maxwidth * ((f.height + 7) / 8);
659 bufsize *= glyphs;
660 if ( bufsize < FONT_HEADER_SIZE )
661 bufsize = FONT_HEADER_SIZE;
662 return bufsize;
665 static int ushortcmp(const void *a, const void *b)
667 return ((int)(*(unsigned short*)a - *(unsigned short*)b));
669 static void glyph_cache_load(struct font* pf)
672 #define MAX_SORT 256
673 if (pf->fd >= 0) {
674 int fd;
675 int i, size;
676 unsigned char tmp[2];
677 unsigned short ch;
678 unsigned short glyphs[MAX_SORT];
679 unsigned short glyphs_lru_order[MAX_SORT];
680 int glyph_file_skip=0, glyph_file_size=0;
682 int sort_size = pf->cache._capacity;
683 if ( sort_size > MAX_SORT )
684 sort_size = MAX_SORT;
686 fd = open(GLYPH_CACHE_FILE, O_RDONLY|O_BINARY);
687 if (fd >= 0) {
689 /* only read what fits */
690 glyph_file_size = filesize( fd );
691 if ( glyph_file_size > 2*pf->cache._capacity ) {
692 glyph_file_skip = glyph_file_size - 2*pf->cache._capacity;
693 lseek( fd, glyph_file_skip, SEEK_SET );
696 while(1) {
698 for ( size = 0;
699 read( fd, tmp, 2 ) == 2 && size < sort_size;
700 size++ )
702 glyphs[size] = (tmp[0] << 8) | tmp[1];
703 glyphs_lru_order[size] = glyphs[size];
706 /* sort glyphs array to make sector cache happy */
707 qsort((void *)glyphs, size, sizeof(unsigned short),
708 ushortcmp );
710 /* load font bitmaps */
711 i = 0;
712 font_get_bits(pf, glyphs[i]);
713 for ( i = 1; i < size ; i++) {
714 if ( glyphs[i] != glyphs[i-1] )
715 font_get_bits(pf, glyphs[i]);
718 /* redo to fix lru order */
719 for ( i = 0; i < size ; i++)
720 font_get_bits(pf, glyphs_lru_order[i]);
722 if ( size < sort_size )
723 break;
726 close(fd);
727 } else {
728 /* load latin1 chars into cache */
729 for ( ch = 32 ; ch < 256 && ch < pf->cache._capacity + 32; ch++ )
730 font_get_bits(pf, ch);
733 return;
735 #else /* BOOTLOADER */
737 void font_init(void)
742 * Bootloader only supports the built-in sysfont.
744 struct font* font_get(int font)
746 (void)font;
747 return &sysfont;
751 * Returns width of character
753 int font_get_width(struct font* pf, unsigned short char_code)
755 /* check input range*/
756 if (char_code < pf->firstchar || char_code >= pf->firstchar+pf->size)
757 char_code = pf->defaultchar;
758 char_code -= pf->firstchar;
760 return pf->width? pf->width[char_code]: pf->maxwidth;
763 const unsigned char* font_get_bits(struct font* pf, unsigned short char_code)
765 const unsigned char* bits;
767 /* check input range*/
768 if (char_code < pf->firstchar || char_code >= pf->firstchar+pf->size)
769 char_code = pf->defaultchar;
770 char_code -= pf->firstchar;
772 /* assume small font with uint16_t offsets*/
773 bits = pf->bits + (pf->offset?
774 ((uint16_t*)(pf->offset))[char_code]:
775 (((pf->height + 7) / 8) * pf->maxwidth * char_code));
777 return bits;
780 #endif /* BOOTLOADER */
783 * Returns the stringsize of a given string.
785 int font_getstringsize(const unsigned char *str, int *w, int *h, int fontnumber)
787 struct font* pf = font_get(fontnumber);
788 unsigned short ch;
789 int width = 0;
791 for (str = utf8decode(str, &ch); ch != 0 ; str = utf8decode(str, &ch))
793 if (is_diacritic(ch, NULL))
794 continue;
796 /* get proportional width and glyph bits*/
797 width += font_get_width(pf,ch);
799 if ( w )
800 *w = width;
801 if ( h )
802 *h = pf->height;
803 return width;
806 /* -----------------------------------------------------------------
807 * vim: et sw=4 ts=8 sts=4 tw=78