A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 1147 lines 32 kB view raw
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 */