A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (c) 2002 by Greg Haerr <greg@censoft.com>
11 *
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.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21/*
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.
26 */
27#include <stdio.h>
28#include <string.h>
29#include <stdlib.h>
30#include <stdint.h>
31#include <stddef.h>
32
33#include "config.h"
34#include "system.h"
35#include "kernel.h"
36#include "lcd.h"
37#include "string-extra.h"
38#include "font.h"
39#include "file.h"
40#include "core_alloc.h"
41#include "debug.h"
42#include "panic.h"
43#include "rbunicode.h"
44#include "diacritic.h"
45#include "rbpaths.h"
46
47/* Define LOGF_ENABLE to enable logf output in this file */
48//#define LOGF_ENABLE
49#include "logf.h"
50
51#define MAX_FONTSIZE_FOR_16_BIT_OFFSETS 0xFFDB
52
53#define FONT_EXT "fnt"
54#define GLYPH_CACHE_EXT "gc"
55
56#ifdef UNICODE32
57#define FC_HEADER_VAL 0x01000020
58#else
59#define FC_HEADER_VAL 0x01000010
60#endif
61
62/* max static loadable font buffer size */
63#ifndef MAX_FONT_SIZE
64#if LCD_HEIGHT > 64
65#if MEMORYSIZE > 2
66#define MAX_FONT_SIZE 60000
67#else
68#define MAX_FONT_SIZE 10000
69#endif
70#else
71#define MAX_FONT_SIZE 4000
72#endif
73#endif
74#define GLYPHS_TO_CACHE 256
75
76#if MEMORYSIZE < 4
77#define FONT_HARD_LIMIT
78#endif
79
80#ifndef FONT_HEADER_SIZE
81#define FONT_HEADER_SIZE 36
82#endif
83
84#ifndef BOOTLOADER
85/* Font cache includes */
86#include "font_cache.h"
87#include "lru.h"
88#endif
89
90#ifndef O_BINARY
91#define O_BINARY 0
92#endif
93
94/* Define this to try loading /.rockbox/.glyphcache *
95 * when a font specific file fails. This requires the *
96 * user to copy and rename a font glyph cache file */
97//#define TRY_DEFAULT_GLYPHCACHE
98
99/* compiled-in font */
100extern struct font sysfont;
101
102#if !defined(BOOTLOADER) || defined(SONY_NWZ_LINUX) || defined(HIBY_LINUX) || defined(FIIO_M3K_LINUX)
103
104struct buflib_alloc_data {
105 struct font font; /* must be the first member! */
106 char *path; /* font path and filename (allocd at end of buffer) */
107 size_t path_bufsz; /* size of path buffer */
108 int refcount; /* how many times has this font been loaded? */
109 unsigned char buffer[];
110};
111static int buflib_allocations[MAXFONTS];
112
113static int cache_fd;
114static struct font* cache_pf;
115
116static void glyph_cache_save(int font_id);
117
118static int buflibmove_callback(int handle, void* current, void* new)
119{
120 (void)handle;
121 struct buflib_alloc_data *alloc = (struct buflib_alloc_data*)current;
122 ptrdiff_t diff = new - current;
123
124#define UPDATE(x) if (x) { x = PTR_ADD(x, diff); }
125
126 UPDATE(alloc->font.bits);
127 UPDATE(alloc->font.offset);
128 UPDATE(alloc->font.width);
129
130 UPDATE(alloc->font.buffer_start);
131 UPDATE(alloc->font.buffer_end);
132 UPDATE(alloc->font.buffer_position);
133 UPDATE(alloc->path);
134
135 UPDATE(alloc->font.cache._index);
136 UPDATE(alloc->font.cache._lru._base);
137 logf("%s %s", __func__, alloc->path);
138 return BUFLIB_CB_OK;
139}
140static void lock_font_handle(int handle, bool lock)
141{
142 if ( handle < 0 )
143 return;
144
145 if (lock)
146 core_pin(handle);
147 else
148 core_unpin(handle);
149}
150
151void font_lock(int font_id, bool lock)
152{
153 if( font_id < 0 || font_id >= MAXFONTS )
154 return;
155 if( buflib_allocations[font_id] > 0 )
156 lock_font_handle(buflib_allocations[font_id], lock);
157}
158
159static struct buflib_callbacks buflibops = {buflibmove_callback, NULL, NULL };
160
161static inline unsigned char *buffer_from_handle(int handle)
162{
163 struct buflib_alloc_data *alloc = core_get_data(handle);
164 unsigned char* buffer = alloc->buffer;
165 return buffer;
166}
167
168/* Font cache structures */
169static void cache_create(struct font* pf);
170static NO_INLINE void glyph_cache_load(const char *font_path, struct font *pf);
171/* End Font cache structures */
172
173void font_init(void)
174{
175 int i = 0;
176 cache_fd = -1;
177 while (i<MAXFONTS)
178 buflib_allocations[i++] = -1;
179}
180
181/* Check if we have x bytes left in the file buffer */
182#define HAVEBYTES(x) (pf->buffer_position + (x) <= pf->buffer_end)
183
184/* Helper functions to read big-endian unaligned short or long from
185 the file buffer. Bounds-checking must be done in the calling
186 function.
187 */
188
189static short readshort(struct font *pf)
190{
191 uint16_t s;
192
193 s = *pf->buffer_position++ & 0xff;
194 s |= (*pf->buffer_position++ << 8);
195 return s;
196}
197
198static int32_t readlong(struct font *pf)
199{
200 uint32_t l;
201
202 l = *pf->buffer_position++ & 0xff;
203 l |= *pf->buffer_position++ << 8;
204 l |= ((uint32_t)(*pf->buffer_position++)) << 16;
205 l |= ((uint32_t)(*pf->buffer_position++)) << 24;
206 return l;
207}
208
209static int glyph_bytes( struct font *pf, int width )
210{
211 int ret;
212 if (pf->depth)
213 ret = ( pf->height * width + 1 ) / 2;
214 else
215 ret = width * ((pf->height + 7) / 8);
216 return (ret + 1) & ~1;
217}
218
219/* Load memory font */
220static struct font* font_load_in_memory(struct font* pf,
221 int32_t nwidth,
222 int32_t noffset )
223{
224 int i;
225 /* variable font data*/
226 pf->buffer_position = pf->buffer_start + 36;
227 pf->bits = (unsigned char *)pf->buffer_position;
228 pf->buffer_position += pf->bits_size*sizeof(unsigned char);
229
230 if (pf->bits_size < MAX_FONTSIZE_FOR_16_BIT_OFFSETS)
231 {
232 /* pad to 16-bit boundary */
233 pf->buffer_position = (unsigned char *)(((intptr_t)pf->buffer_position + 1) & ~1);
234 }
235 else
236 {
237 /* pad to 32-bit boundary*/
238 pf->buffer_position = (unsigned char *)(((intptr_t)pf->buffer_position + 3) & ~3);
239 }
240
241 if (noffset)
242 {
243 if (pf->bits_size < MAX_FONTSIZE_FOR_16_BIT_OFFSETS)
244 {
245 pf->long_offset = 0;
246 pf->offset = (uint16_t*)pf->buffer_position;
247
248 /* Check we have sufficient buffer */
249 if (!HAVEBYTES(noffset * sizeof(uint16_t)))
250 return NULL;
251
252 for (i=0; i<noffset; ++i)
253 {
254 ((uint16_t*)(pf->offset))[i] = (uint16_t)readshort(pf);
255 }
256 }
257 else
258 {
259 pf->long_offset = 1;
260 pf->offset = (uint16_t*)pf->buffer_position;
261
262 /* Check we have sufficient buffer */
263 if (!HAVEBYTES(noffset * sizeof(int32_t)))
264 return NULL;
265
266 for (i=0; i<noffset; ++i)
267 {
268 ((uint32_t*)(pf->offset))[i] = (uint32_t)readlong(pf);
269 }
270 }
271 }
272 else
273 pf->offset = NULL;
274
275 if (nwidth) {
276 pf->width = (unsigned char *)pf->buffer_position;
277 pf->buffer_position += nwidth*sizeof(unsigned char);
278 }
279 else
280 pf->width = NULL;
281
282 if (pf->buffer_position > pf->buffer_end)
283 return NULL;
284
285 return pf; /* success!*/
286}
287
288/* Load cached font */
289static struct font* font_load_cached(struct font* pf,
290 int32_t nwidth,
291 int32_t noffset)
292{
293 /* We are now at the bitmap data, this is fixed at 36.. */
294 pf->width = NULL;
295 pf->bits = NULL;
296
297 /* Calculate offset to offset data */
298 pf->buffer_position += pf->bits_size * sizeof(unsigned char);
299
300 if (pf->bits_size < MAX_FONTSIZE_FOR_16_BIT_OFFSETS)
301 {
302 pf->long_offset = 0;
303 /* pad to 16-bit boundary */
304 pf->buffer_position = (unsigned char *)(((intptr_t)pf->buffer_position + 1) & ~1);
305 }
306 else
307 {
308 pf->long_offset = 1;
309 /* pad to 32-bit boundary*/
310 pf->buffer_position = (unsigned char *)(((intptr_t)pf->buffer_position + 3) & ~3);
311 }
312
313 if (noffset)
314 pf->file_offset_offset = (uint32_t)(pf->buffer_position - pf->buffer_start);
315 else
316 pf->file_offset_offset = 0;
317
318 /* Calculate offset to widths data */
319 if (pf->bits_size < MAX_FONTSIZE_FOR_16_BIT_OFFSETS)
320 pf->buffer_position += noffset * sizeof(uint16_t);
321 else
322 pf->buffer_position += noffset * sizeof(uint32_t);
323
324 if (nwidth)
325 pf->file_width_offset = (uint32_t)(pf->buffer_position - pf->buffer_start);
326 else
327 pf->file_width_offset = 0;
328
329 /* Create the cache */
330 cache_create(pf);
331
332 return pf;
333}
334
335bool font_filename_matches_loaded_id(int font_id, const char *filename)
336{
337 if ( font_id >= 0 && font_id < MAXFONTS )
338 {
339 int handle = buflib_allocations[font_id];
340 if (handle > 0)
341 {
342 struct buflib_alloc_data *data = core_get_data(handle);
343 logf("%s id: [%d], %s", __func__, font_id, data->path);
344 return strcmp(data->path, filename) == 0;
345 }
346 }
347 return false;
348}
349
350static int find_font_index(const char* path)
351{
352 for(int index = 0; index < MAXFONTS; index++)
353 {
354 if(font_filename_matches_loaded_id(index, path))
355 {
356 logf("%s Found id: [%d], %s", __func__, index, path);
357 return index;
358 }
359 }
360 logf("%s %s Not found using id: [%d], FONT_SYSFIXED",
361 __func__, path, FONT_SYSFIXED);
362 return FONT_SYSFIXED;
363}
364
365static size_t font_glyphs_to_bufsize(struct font *pf, int glyphs)
366{
367 size_t bufsize;
368
369 /* LRU bytes per glyph */
370 bufsize = LRU_SLOT_OVERHEAD + sizeof(struct font_cache_entry) +
371 sizeof(unsigned short);
372 /* Image bytes per glyph */
373 bufsize += glyph_bytes(pf, pf->maxwidth);
374 bufsize *= glyphs;
375
376 return bufsize;
377}
378
379static struct font* font_load_header(int fd, struct font *pheader,
380 struct font *pf,
381 uint32_t *nwidth, uint32_t *noffset)
382{
383 /* Load the header. Readshort() and readlong() *
384 * update buffer_position address as they read */
385 pheader->buffer_start = pheader->buffer_position = (char *)pheader;
386 pheader->buffer_size = FONT_HEADER_SIZE;
387 pheader->buffer_end = pheader->buffer_start + pheader->buffer_size;
388
389 if (read(fd, pheader, FONT_HEADER_SIZE) != FONT_HEADER_SIZE)
390 return NULL;
391
392 /* read magic and version #*/
393 if (memcmp(pheader->buffer_position, VERSION, 4) != 0)
394 return NULL;
395
396 pheader->buffer_position += 4;
397
398 /* font info*/
399 pf->maxwidth = readshort(pheader);
400 pf->height = readshort(pheader);
401 pf->ascent = readshort(pheader);
402 pf->depth = readshort(pheader);
403 pf->firstchar = readlong(pheader);
404 pf->defaultchar = readlong(pheader);
405 pf->size = readlong(pheader);
406
407 /* get variable font data sizes*/
408 /* # words of bitmap_t*/
409 pf->bits_size = readlong(pheader);
410 *noffset = readlong(pheader);
411 *nwidth = readlong(pheader);
412
413 return pf;
414}
415
416/* load a font with room for glyphs, limited to bufsize if not zero */
417int font_load_ex( const char *path, size_t buf_size, int glyphs )
418{
419 /* needed to handle the font properly after it's loaded */
420 size_t path_len = strlen(path);
421 if ( path_len >= MAX_PATH )
422 return -1;
423
424 //printf("\nfont_load_ex(%s, %d, %d)\n", path, buf_size, glyphs);
425 int fd = open(path, O_RDONLY|O_BINARY);
426 if ( fd < 0 )
427 return -1;
428
429#ifdef UNICODE32
430 if (glyphs && glyphs < 3)
431 glyphs = 3; /* Guarantee we'll always have at least 2 after alignment */
432#else
433 if (glyphs && glyphs < 2)
434 glyphs = 2; /* Guarantee we'll always have at least 1 after alignment */
435#endif
436
437 /* load font struct f with file header */
438 int file_size = filesize( fd );
439 struct font header;
440 struct font f;
441
442 uint32_t nwidth, noffset;
443 if ( !font_load_header( fd, &header, &f, &nwidth, &noffset )
444#if LCD_DEPTH < 16
445 || f.depth
446#endif
447 )
448 {
449 close(fd);
450 return -1;
451 }
452
453 /* examine f and calc buffer size */
454 bool cached = false;
455 size_t bufsize = buf_size;
456 size_t glyph_buf_size = font_glyphs_to_bufsize( &f, glyphs );
457
458 if ( bufsize && glyphs && bufsize > glyph_buf_size)
459 bufsize = glyph_buf_size;
460 else
461 {
462 if ( glyphs )
463 bufsize = glyph_buf_size;
464 else
465 bufsize = MAX_FONT_SIZE;
466 }
467#ifdef FONT_HARD_LIMIT
468 if ( bufsize > MAX_FONT_SIZE )
469 bufsize = MAX_FONT_SIZE;
470#endif
471 if ( bufsize < (size_t) file_size )
472 cached = true;
473 else
474 bufsize = file_size;
475
476 /* check already loaded */
477 int font_id = find_font_index(path);
478
479 if (font_id > FONT_SYSFIXED)
480 {
481 /* already loaded, no need to reload */
482 struct buflib_alloc_data *pd = core_get_data(buflib_allocations[font_id]);
483 if (pd->font.buffer_size < bufsize || pd->path_bufsz < path_len)
484 {
485 int old_refcount, old_id;
486 size_t old_bufsize = pd->font.buffer_size;
487 bool failed = false;
488 /* reload the font:
489 * 1) save of refcont and id
490 * 2) force unload (set refcount to 1 to make sure it get unloaded)
491 * 3) reload with the larger buffer
492 * 4) restore the id and refcount
493 */
494 old_id = font_id;
495 old_refcount = pd->refcount;
496 pd->refcount = 1;
497 font_unload(font_id);
498 font_id = font_load_ex(path, bufsize, glyphs);
499 if (font_id < 0)
500 {
501 failed = true;
502 font_id = font_load_ex(path, old_bufsize, 0);
503 /* we couldn't even get the old size, this shouldn't happen */
504 if ( font_id < 0 )
505 return -1;
506 }
507 if (old_id != font_id)
508 {
509 buflib_allocations[old_id] = buflib_allocations[font_id];
510 buflib_allocations[font_id] = -1;
511 font_id = old_id;
512 }
513 pd = core_get_data(buflib_allocations[font_id]);
514 pd->refcount = old_refcount;
515 if(failed)
516 /* return error because we didn't satisfy the new buffer size */
517 return -1;
518 }
519 pd->refcount++;
520 //printf("reusing handle %d for %s (count: %d)\n", font_id, path, pd->refcount);
521 close(fd);
522 return font_id;
523 }
524
525 int open_slot = -1;
526
527 for (font_id = FONT_FIRSTUSERFONT; font_id < MAXFONTS; font_id++)
528 {
529 if (buflib_allocations[ font_id ] < 0)
530 {
531 open_slot = font_id;
532 break;
533 }
534 }
535 if ( open_slot == -1 )
536 return -1;
537 font_id = open_slot;
538 size_t path_bufsz = MAX(path_len + 1, 64); /* enough size for common case */
539 /* allocate mem */
540 int handle = core_alloc_ex(
541 bufsize + path_bufsz + sizeof( struct buflib_alloc_data ),
542 &buflibops );
543 if ( handle <= 0 )
544 {
545 return -1;
546 }
547 struct buflib_alloc_data *pdata;
548
549 pdata = core_get_data_pinned(handle);
550 pdata->refcount = 1;
551 pdata->path = pdata->buffer + bufsize;
552 /* save load path so we can recognize this font later */
553 memcpy(pdata->path, path, path_len+1);
554
555 /* load and init */
556 struct font *pf = &pdata->font;
557 memcpy(pf, &f, sizeof( struct font) );
558
559 pf->fd = fd;
560 pf->fd_width = pf->fd_offset = -1;
561 pf->handle = handle;
562 pf->disabled = false;
563
564 pf->buffer_start = buffer_from_handle( pf->handle );
565 pf->buffer_position = pf->buffer_start + FONT_HEADER_SIZE;
566 pf->buffer_size = bufsize;
567 pf->buffer_end = pf->buffer_start + bufsize;
568
569 if ( cached )
570 {
571 if ( ! font_load_cached( pf, nwidth, noffset ) )
572 {
573 core_free( handle );
574 return -1;
575 }
576
577 /* trick to get a small cache for each file section *
578 * during glyph_cache_load() */
579 pf->fd_width = open( path, O_RDONLY|O_BINARY );
580 pf->fd_offset = open( path, O_RDONLY|O_BINARY );
581
582 glyph_cache_load( path, pf );
583
584 /* cached font: pf->fd stays open until the font is unloaded */
585 close( pf->fd_width );
586 pf->fd_width = -1;
587 close( pf->fd_offset );
588 pf->fd_offset = -1;
589 }
590 else
591 {
592 lseek( fd, 0, SEEK_SET);
593 read(fd, pf->buffer_start, pf->buffer_size);
594
595 close( fd );
596 pf->fd = -1;
597
598 if ( ! font_load_in_memory( pf, nwidth, noffset ) )
599 {
600 core_free( handle );
601 return -1;
602 }
603 }
604 buflib_allocations[font_id] = handle;
605 //printf("%s -> [%d] -> %d\n", path, font_id, *handle);
606 core_put_data_pinned(pdata);
607 logf("%s id: [%d], %s", __func__, font_id, path);
608 return font_id; /* success!*/
609}
610
611int font_load(const char *path)
612{
613 return font_load_ex(path, MAX_FONT_SIZE, GLYPHS_TO_CACHE);
614}
615
616void font_unload(int font_id)
617{
618 if ( font_id < 0 || font_id >= MAXFONTS )
619 return;
620 int handle = buflib_allocations[font_id];
621 if ( handle < 0 )
622 return;
623 struct buflib_alloc_data *pdata = core_get_data(handle);
624 struct font* pf = &pdata->font;
625 pdata->refcount--;
626 if (pdata->refcount < 1)
627 {
628 logf("%s %s", __func__, pdata->path);
629 if (pf && pf->fd >= 0)
630 {
631 glyph_cache_save(font_id);
632 close(pf->fd);
633 }
634 core_free(handle);
635 buflib_allocations[font_id] = -1;
636
637 }
638}
639
640void font_unload_all(void)
641{
642 int i;
643 for (i=0; i<MAXFONTS; i++)
644 {
645 if (buflib_allocations[i] > 0)
646 {
647 struct buflib_alloc_data *alloc = core_get_data(buflib_allocations[i]);
648 alloc->refcount = 1; /* force unload */
649 font_unload(i);
650 }
651 }
652}
653
654static void font_disable(int font_id)
655{
656 if ( font_id < 0 || font_id >= MAXFONTS )
657 return;
658 int handle = buflib_allocations[font_id];
659 if ( handle < 0 )
660 return;
661 struct buflib_alloc_data *pdata = core_get_data(handle);
662 struct font *pf = &pdata->font;
663
664 if (pf->fd >= 0)
665 {
666 /* save the cache, but it keep it in-RAM so that cache lookups
667 * can still succeed on the same font */
668 glyph_cache_save(font_id);
669 close(pf->fd);
670 pf->fd = -1;
671 pf->disabled = true;
672 }
673}
674
675void font_disable_all(void)
676{
677 for(int i = 0; i < MAXFONTS; i++)
678 font_disable(i);
679}
680
681static void font_enable(int font_id)
682{
683 if ( font_id < 0 || font_id >= MAXFONTS )
684 return;
685 int handle = buflib_allocations[font_id];
686 if ( handle < 0 )
687 return;
688 struct buflib_alloc_data *pdata = core_get_data_pinned(handle);
689 struct font *pf = &pdata->font;
690
691 if (pf->disabled && pf->fd < 0)
692 {
693 pf->fd = open(pdata->path, O_RDONLY);
694 pf->disabled = false;
695 }
696 core_put_data_pinned(pdata);
697}
698
699void font_enable_all(void)
700{
701 for(int i = 0; i < MAXFONTS; i++)
702 font_enable(i);
703}
704
705
706/*
707 * Return a pointer to an incore font structure.
708 * If the requested font isn't loaded/compiled-in,
709 * decrement the font number and try again.
710 */
711struct font* font_get(int font)
712{
713 struct font* pf;
714 if (font == FONT_UI)
715 font = MAXFONTS-1;
716 if (font <= FONT_SYSFIXED || font >= MAXFONTS)
717 return &sysfont;
718
719 while (1) {
720 if (buflib_allocations[font] > 0)
721 {
722 struct buflib_alloc_data *alloc = core_get_data(buflib_allocations[font]);
723 pf = &alloc->font;
724 if (pf && pf->height)
725 return pf;
726 }
727 if (--font < 0)
728 return &sysfont;
729 }
730}
731
732/*
733 * Reads an entry into cache entry
734 */
735static void
736load_cache_entry(struct font_cache_entry* p, void* callback_data)
737{
738 struct font* pf = callback_data;
739
740 ucschar_t char_code = p->_char_code;
741 int fd;
742
743 lock_font_handle(pf->handle, true);
744 if (pf->file_width_offset)
745 {
746 int width_offset = pf->file_width_offset + char_code;
747 /* load via different fd to get this file section cached */
748 if(pf->fd_width >=0 )
749 fd = pf->fd_width;
750 else
751 fd = pf->fd;
752 lseek(fd, width_offset, SEEK_SET);
753 read(fd, &(p->width), 1);
754 }
755 else
756 {
757 p->width = pf->maxwidth;
758 }
759
760 int32_t bitmap_offset = 0;
761
762 if (pf->file_offset_offset)
763 {
764 int32_t offset = pf->file_offset_offset + char_code * (pf->long_offset ? sizeof(int32_t) : sizeof(int16_t));
765 /* load via different fd to get this file section cached */
766 if(pf->fd_offset >=0 )
767 fd = pf->fd_offset;
768 else
769 fd = pf->fd;
770 lseek(fd, offset, SEEK_SET);
771 unsigned char tmp[2];
772 if (read (fd, tmp, 2) == 2)
773 {
774 bitmap_offset = tmp[0] | (tmp[1] << 8);
775 if (pf->long_offset) {
776 if (read (fd, tmp, 2) == 2)
777 bitmap_offset |= (tmp[0] << 16) | (tmp[1] << 24);
778 }
779 }
780 }
781 else
782 {
783 bitmap_offset = char_code * glyph_bytes(pf, p->width);
784 }
785
786 int32_t file_offset = FONT_HEADER_SIZE + bitmap_offset;
787 lseek(pf->fd, file_offset, SEEK_SET);
788 int src_bytes = glyph_bytes(pf, p->width);
789 read(pf->fd, p->bitmap, src_bytes);
790
791 lock_font_handle(pf->handle, false);
792}
793
794/*
795 * Converts cbuf into a font cache
796 */
797static void cache_create(struct font* pf)
798{
799 /* maximum size of rotated bitmap */
800 int bitmap_size = glyph_bytes(pf, pf->maxwidth);
801 /* reserve one blank glyph that is guaranteed to be available, even
802 * when the font file is closed during USB */
803 unsigned char *cache_buf = pf->buffer_start + bitmap_size;
804 size_t cache_size = pf->buffer_size - bitmap_size;
805 ALIGN_BUFFER(cache_buf, cache_size, sizeof(ucschar_t));
806 memset(pf->buffer_start, 0, bitmap_size);
807 /* Initialise cache */
808 font_cache_create(&pf->cache, cache_buf, cache_size, bitmap_size);
809}
810
811/*
812 * Returns width of character
813 */
814int font_get_width(struct font* pf, ucschar_t char_code)
815{
816 int width;
817 struct font_cache_entry *e;
818
819 /* check input range*/
820 if (char_code < pf->firstchar || char_code >= pf->firstchar+pf->size)
821 char_code = pf->defaultchar;
822 char_code -= pf->firstchar;
823
824 if (pf->fd >= 0 && pf != &sysfont)
825 width = font_cache_get(&pf->cache,char_code,false,load_cache_entry,pf)->width;
826 else if (pf->disabled &&
827 (e = font_cache_get(&pf->cache,char_code,true,load_cache_entry,pf)))
828 width = e->width; /* falls back to pf->maxwidth if !e */
829 else if (pf->width)
830 width = pf->width[char_code];
831 else
832 width = pf->maxwidth;
833
834 return width;
835}
836
837const unsigned char* font_get_bits(struct font* pf, ucschar_t char_code)
838{
839 const unsigned char* bits;
840
841 /* check input range*/
842 if (char_code < pf->firstchar || char_code >= pf->firstchar+pf->size)
843 char_code = pf->defaultchar;
844 char_code -= pf->firstchar;
845
846 if (pf->fd >= 0 && pf != &sysfont)
847 {
848 bits =
849 (unsigned char*)font_cache_get(&pf->cache, char_code,
850 false, load_cache_entry, pf)->bitmap;
851 }
852 else if (pf->disabled)
853 {
854 /* the font handle is closed, but the cache is intact. Attempt
855 * a lookup, which is very likely to succeed. Return a placeholder
856 * glyph on miss (again, this is very unlikely */
857 struct font_cache_entry *e = font_cache_get(&pf->cache, char_code,
858 true, NULL, NULL);
859 if (LIKELY(e))
860 bits = (unsigned char *) e->bitmap;
861 else
862 {
863 /* Could attempt to find a suitable fallback glyph from the same
864 * font. For now just return blank space which is
865 * reserved by cache_create() at buffer_start */
866 bits = pf->buffer_start;
867 }
868 }
869 else
870 {
871 /* This font is entirely in RAM */
872 bits = pf->bits;
873 if (pf->offset)
874 {
875 if (pf->bits_size < MAX_FONTSIZE_FOR_16_BIT_OFFSETS)
876 bits += ((uint16_t*)(pf->offset))[char_code];
877 else
878 bits += ((uint32_t*)(pf->offset))[char_code];
879 }
880 else
881 bits += char_code * glyph_bytes(pf, pf->maxwidth);
882 }
883
884 return bits;
885}
886
887static void font_path_to_glyph_path( const char *font_path, char *glyph_path)
888{
889 /* take full file name, cut extension, and add .glyphcache */
890 strmemccpy(glyph_path, font_path, MAX_PATH);
891 int dotidx = strlen(glyph_path) - sizeof(FONT_EXT);
892 strmemccpy(glyph_path + dotidx, "." GLYPH_CACHE_EXT, MAX_PATH - dotidx);
893 logf("%s %s", __func__, glyph_path);
894}
895
896/* call with NULL to flush */
897static void glyph_file_write(void* data)
898{
899 struct font_cache_entry* p = data;
900 struct font* pf = cache_pf;
901 ucschar_t ch;
902 static int buffer_pos = 0;
903#define WRITE_BUFFER 256
904 static unsigned char buffer[WRITE_BUFFER];
905
906 /* flush buffer & reset */
907 if ( data == NULL || buffer_pos >= WRITE_BUFFER)
908 {
909 write(cache_fd, buffer, buffer_pos);
910 buffer_pos = 0;
911 if ( data == NULL )
912 return;
913 }
914 if ( p->_char_code == 0xffff )
915 return;
916
917 ch = p->_char_code + pf->firstchar;
918#ifdef UNICODE32
919 buffer[buffer_pos] = (ch >> 24) & 0xff;
920 buffer[buffer_pos+1] = (ch >> 16) & 0xff;
921 buffer[buffer_pos+2] = (ch >> 8) & 0xff;
922 buffer[buffer_pos+3] = ch & 0xff;
923 buffer_pos += 4;
924#else
925 buffer[buffer_pos] = (ch >> 8) & 0xff;
926 buffer[buffer_pos+1] = ch & 0xff;
927 buffer_pos += 2;
928#endif
929 return;
930}
931
932/* save the char codes of the loaded glyphs to a file */
933static void glyph_cache_save(int font_id)
934{
935 int fd;
936
937 if( font_id < 0 )
938 return;
939 int handle = buflib_allocations[font_id];
940 if ( handle < 0 )
941 return;
942
943 struct buflib_alloc_data *pdata = core_get_data_pinned(handle);
944 struct font *pf = &pdata->font;
945
946 if(pf && pf->fd >= 0)
947 {
948 char filename[MAX_PATH];
949 font_path_to_glyph_path(pdata->path, filename);
950 fd = open(filename, O_WRONLY|O_CREAT|O_TRUNC, 0666);
951 if (fd >= 0)
952 {
953 uint32_t header = FC_HEADER_VAL;
954 write(fd, &header, sizeof(header));
955 cache_pf = pf;
956 cache_fd = fd;
957 lru_traverse(&cache_pf->cache._lru, glyph_file_write);
958 glyph_file_write(NULL);
959 if (cache_fd >= 0)
960 {
961 close(cache_fd);
962 cache_fd = -1;
963 }
964 }
965 }
966 core_put_data_pinned(pdata);
967 return;
968}
969
970
971static int ucscharcmp(const void *a, const void *b)
972{
973 return ((int)(*(ucschar_t*)a - *(ucschar_t*)b));
974}
975
976static NO_INLINE void glyph_cache_load(const char *font_path, struct font *pf)
977{
978#define MAX_SORT 256
979 if (pf->fd >= 0) {
980 int i, size, fd;
981 unsigned char tmp[sizeof(ucschar_t)];
982 ucschar_t ch;
983 ucschar_t glyphs[MAX_SORT];
984 ucschar_t glyphs_lru_order[MAX_SORT];
985 unsigned int glyph_file_skip=0, glyph_file_size=0;
986
987 int sort_size = pf->cache._capacity;
988 if ( sort_size > MAX_SORT )
989 sort_size = MAX_SORT;
990
991 char filename[MAX_PATH];
992 font_path_to_glyph_path(font_path, filename);
993
994 fd = open(filename, O_RDONLY|O_BINARY);
995#ifdef TRY_DEFAULT_GLYPHCACHE
996 /* if font specific file fails, try default */
997 if (fd < 0)
998 fd = open(GLYPH_CACHE_FILE, O_RDONLY|O_BINARY);
999#endif
1000 if (fd >= 0) {
1001 /* Header */
1002 uint32_t hdr = 0;
1003 read(fd, &hdr, sizeof(hdr));
1004 if (hdr != FC_HEADER_VAL)
1005 goto latin;
1006 /* only read what fits */
1007 glyph_file_size = filesize( fd );
1008 if (glyph_file_size < sizeof(uint32_t))
1009 goto latin;
1010 glyph_file_size -= sizeof(uint32_t);
1011 if ( glyph_file_size > (int)sizeof(ucschar_t)*pf->cache._capacity ) {
1012 glyph_file_skip = glyph_file_size - sizeof(ucschar_t)*pf->cache._capacity;
1013 lseek( fd, glyph_file_skip + sizeof(uint32_t), SEEK_SET );
1014 }
1015 while(1) {
1016 for ( size = 0;
1017 read( fd, tmp, sizeof(tmp) ) == sizeof(tmp) && size < sort_size;
1018 size++ )
1019 {
1020#ifdef UNICODE32
1021 glyphs[size] = (tmp[0] << 24) | (tmp[1] << 16) | (tmp[2] << 8) | tmp[3];
1022#else
1023 glyphs[size] = (tmp[0] << 8) | tmp[1];
1024#endif
1025 glyphs_lru_order[size] = glyphs[size];
1026 }
1027
1028 /* sort glyphs array to make sector cache happy */
1029 qsort((void *)glyphs, size, sizeof(ucschar_t),
1030 ucscharcmp );
1031
1032 /* load font bitmaps */
1033 for( i = 0; i < size ; i++ )
1034 font_get_bits(pf, glyphs[i]);
1035
1036 /* redo to fix lru order */
1037 for ( i = 0; i < size ; i++)
1038 font_get_bits(pf, glyphs_lru_order[i]);
1039
1040 if ( size < sort_size )
1041 break;
1042 }
1043
1044 close(fd);
1045 } else {
1046 latin:
1047 /* load latin1 chars into cache */
1048 for ( ch = 32 ; ch < 256 && ch < pf->cache._capacity + 32; ch++ )
1049 font_get_bits(pf, ch);
1050 }
1051 }
1052 return;
1053}
1054#else /* BOOTLOADER */
1055
1056void font_init(void)
1057{
1058}
1059
1060void font_lock(int font_id, bool lock)
1061{
1062 (void)font_id;
1063 (void)lock;
1064}
1065
1066/*
1067 * Bootloader only supports the built-in sysfont.
1068 */
1069struct font* font_get(int font)
1070{
1071 (void)font;
1072 return &sysfont;
1073}
1074
1075/*
1076 * Returns width of character
1077 */
1078int font_get_width(struct font* pf, ucschar_t char_code)
1079{
1080 /* check input range*/
1081 if (char_code < pf->firstchar || char_code >= pf->firstchar+pf->size)
1082 char_code = pf->defaultchar;
1083 char_code -= pf->firstchar;
1084
1085 return pf->width? pf->width[char_code]: pf->maxwidth;
1086}
1087
1088const unsigned char* font_get_bits(struct font* pf, ucschar_t char_code)
1089{
1090 const unsigned char* bits;
1091
1092 /* check input range*/
1093 if (char_code < pf->firstchar || char_code >= pf->firstchar+pf->size)
1094 char_code = pf->defaultchar;
1095 char_code -= pf->firstchar;
1096
1097 /* assume small font with uint16_t offsets*/
1098 bits = pf->bits + (pf->offset?
1099 ((uint16_t*)(pf->offset))[char_code]:
1100 (((pf->height + 7) / 8) * pf->maxwidth * char_code));
1101
1102 return bits;
1103}
1104
1105#endif /* BOOTLOADER */
1106
1107/*
1108 * Returns the stringsize of a given NULL terminated string
1109 * stops after maxbytes or NULL (\0) whichever occurs first.
1110 * maxbytes = -1 ignores maxbytes and relies on NULL terminator (\0)
1111 * to terminate the string
1112 */
1113int font_getstringnsize(const unsigned char *str, size_t maxbytes, int *w, int *h, int fontnum)
1114{
1115 struct font* pf = font_get(fontnum);
1116 font_lock( fontnum, true );
1117 ucschar_t ch;
1118 int width = 0;
1119 size_t b = maxbytes - 1;
1120
1121 for (str = utf8decode(str, &ch); ch != 0 && b < maxbytes; str = utf8decode(str, &ch), b--)
1122 {
1123 if (IS_DIACRITIC(ch))
1124 continue;
1125
1126 /* get proportional width and glyph bits*/
1127 width += font_get_width(pf,ch);
1128 }
1129 if ( w )
1130 *w = width;
1131 if ( h )
1132 *h = pf->height;
1133 font_lock( fontnum, false );
1134 return width;
1135}
1136
1137/*
1138 * Returns the stringsize of a given NULL terminated string.
1139 */
1140int font_getstringsize(const unsigned char *str, int *w, int *h, int fontnumber)
1141{
1142 return font_getstringnsize(str, -1, w, h, fontnumber);
1143}
1144
1145/* -----------------------------------------------------------------
1146 * vim: et sw=4 ts=8 sts=4 tw=78
1147 */