Load fonts dynamically from /lua

This unfortunately slows boot time by a bit, but I think we should be able to reclaim that time eventually.
custom
jacqueline 10 months ago
parent 5086ab96ea
commit 7f2d56e29a
  1. 285
      lib/luavgl/src/font.c
  2. 3
      lib/luavgl/src/luavgl.h
  3. 2
      lib/lvgl/lv_conf.h
  4. 4
      lua/font.lua
  5. 4
      luals-stubs/lvgl.lua
  6. 4
      src/graphics/CMakeLists.txt
  7. 54952
      src/graphics/font_fusion_10.c
  8. 84570
      src/graphics/font_fusion_12.c
  9. 77
      src/tangara/lua/bridge.cpp
  10. 4
      tools/fonts/mkfonts.sh

@ -1,174 +1,6 @@
#include "luavgl.h"
#include "private.h"
#define FONT_DEFAULT_SIZE 12
#define _ARRAY_LEN(a) (sizeof(a) / sizeof(a[0]))
#define NAMED_WEIGHT_MAX_CHARS 16
/**
* Follow css style, specify the name by name family, name size,
* name weight. Font weight can be numeric value or 'bold'. Alls strings
* are converted to lower-case before matching with local supported name.
*
* weight named weight
* 100 thin
* 200 extra light
* 300 light
* 400 normal (*default)
* 500 medium
* 600 semi bold
* 700 bold
* 800 extra bold
* 900 ultra bold
*
* Only normal weight is supported to builtin name.
*
* Font returned to lua is a userdata, it's used by object style, or style
* directly. If name is no longer ref'ed by any object or style, it's
* gc'ed.
*/
/**
* lvgl.Font("montserrat, unscii", 8, bold)
* lvgl.Font("montserrat", 8)
* lvgl.Font("montserrat")
*
* return nil if failed
*/
typedef enum {
FONT_WEIGHT_THIN = 100,
FONT_WEIGHT_EXTRA_LIGHT = 200,
FONT_WEIGHT_LIGHT = 300,
FONT_WEIGHT_NORMAL = 400,
FONT_WEIGHT_MEDIUM = 500,
FONT_WEIGHT_SEMI_BOLD = 600,
FONT_WEIGHT_BOLD = 700,
FONT_WEIGHT_EXTRA_BOLD = 800,
FONT_WEIGHT_ULTRA_BOLD = 900,
} font_weight_t;
static const struct {
char *name;
int value;
} g_named_weight[] = {
{"thin", 100},
{"extra light", 200},
{"light", 300},
{"normal", 400},
{"medium", 500},
{"semi bold", 600},
{"bold", 700},
{"extra bold", 800},
{"ultra bold", 900},
};
static const struct {
int size;
const lv_font_t *font;
} g_builtin_montserrat[] = {
#if LV_FONT_MONTSERRAT_8
{8, &lv_font_montserrat_8 },
#endif
#if LV_FONT_MONTSERRAT_10
{10, &lv_font_montserrat_10},
#endif
#if LV_FONT_MONTSERRAT_12
{12, &lv_font_montserrat_12},
#endif
#if LV_FONT_MONTSERRAT_14
{14, &lv_font_montserrat_14},
#endif
#if LV_FONT_MONTSERRAT_16
{16, &lv_font_montserrat_16},
#endif
#if LV_FONT_MONTSERRAT_18
{18, &lv_font_montserrat_18},
#endif
#if LV_FONT_MONTSERRAT_20
{20, &lv_font_montserrat_20},
#endif
#if LV_FONT_MONTSERRAT_22
{22, &lv_font_montserrat_22},
#endif
#if LV_FONT_MONTSERRAT_24
{24, &lv_font_montserrat_24},
#endif
#if LV_FONT_MONTSERRAT_26
{26, &lv_font_montserrat_26},
#endif
#if LV_FONT_MONTSERRAT_28
{28, &lv_font_montserrat_28},
#endif
#if LV_FONT_MONTSERRAT_30
{30, &lv_font_montserrat_30},
#endif
#if LV_FONT_MONTSERRAT_32
{32, &lv_font_montserrat_32},
#endif
#if LV_FONT_MONTSERRAT_34
{34, &lv_font_montserrat_34},
#endif
#if LV_FONT_MONTSERRAT_36
{36, &lv_font_montserrat_36},
#endif
#if LV_FONT_MONTSERRAT_38
{38, &lv_font_montserrat_38},
#endif
#if LV_FONT_MONTSERRAT_40
{40, &lv_font_montserrat_40},
#endif
#if LV_FONT_MONTSERRAT_42
{42, &lv_font_montserrat_42},
#endif
#if LV_FONT_MONTSERRAT_44
{44, &lv_font_montserrat_44},
#endif
#if LV_FONT_MONTSERRAT_46
{46, &lv_font_montserrat_46},
#endif
#if LV_FONT_MONTSERRAT_48
{48, &lv_font_montserrat_48},
#endif
};
static int luavgl_get_named_weight(const char *name)
{
if (name == NULL) {
return FONT_DEFAULT_SIZE;
}
for (int i = 0; i < _ARRAY_LEN(g_named_weight); i++) {
if (lv_strcmp(name, g_named_weight[i].name) == 0) {
return g_named_weight[i].value;
}
}
return FONT_DEFAULT_SIZE;
}
static char *to_lower(char *str)
{
for (char *s = str; *s; ++s)
@ -176,55 +8,6 @@ static char *to_lower(char *str)
return str;
}
static const lv_font_t *_luavgl_font_create(lua_State *L, const char *name,
int size, int weight)
{
/* check builtin font firstly. */
if (lv_strcmp(name, "montserrat") == 0) {
if (FONT_WEIGHT_NORMAL != weight)
return NULL;
for (int i = 0; i < _ARRAY_LEN(g_builtin_montserrat); i++) {
if (size == g_builtin_montserrat[i].size) {
return g_builtin_montserrat[i].font;
}
}
} else if (lv_strcmp(name, "unscii") == 0) {
if (FONT_WEIGHT_NORMAL != weight)
return NULL;
#if LV_FONT_UNSCII_8
if (size == 8)
return &lv_font_unscii_8;
#endif
#if LV_FONT_UNSCII_16
if (size == 16)
return &lv_font_unscii_16;
#endif
}
#if LV_FONT_DEJAVU_16_PERSIAN_HEBREW
else if (lv_strcmp(name, "dejavu_persian_hebrew") == 0) {
if (size == 16)
return &lv_font_dejavu_16_persian_hebrew;
}
#endif
#if LV_FONT_SIMSUN_16_CJK
else if (lv_strcmp(name, "dejavu_persian_hebrew") == 0) {
if (size == 16)
return &lv_font_simsun_16_cjk;
}
#endif
/* not built-in font, check extension */
luavgl_ctx_t *ctx = luavgl_context(L);
if (ctx->make_font) {
return ctx->make_font(name, size, weight);
}
return NULL;
}
static char *luavgl_strchr(const char *s, char c)
{
while (*s) {
@ -245,76 +28,18 @@ static char *luavgl_strchr(const char *s, char c)
*/
static int luavgl_font_create(lua_State *L)
{
int weight;
int size;
char *str, *name;
const lv_font_t *font = NULL;
if (!lua_isstring(L, 1)) {
return luaL_argerror(L, 1, "expect string");
}
const char *name = lua_tostring(L, 1);
const lv_font_t *font = NULL;
/* size is optional, default to FONT_DEFAULT_SIZE */
size = lua_tointeger(L, 2);
if (size == 0) {
size = FONT_DEFAULT_SIZE;
}
if (!lua_isnoneornil(L, 3)) {
if (lua_isinteger(L, 3)) {
weight = lua_tointeger(L, 3);
} else {
char *luastr = (char *)lua_tostring(L, 3);
int len = lv_strlen(luastr);
if (len > 128) {
/* not likely to happen */
return luaL_argerror(L, 3, "too long");
}
char s[NAMED_WEIGHT_MAX_CHARS];
if (len + 1 > NAMED_WEIGHT_MAX_CHARS) {
return luaL_argerror(L, 3, "too long");
}
lv_strcpy(s, luastr);
weight = luavgl_get_named_weight(to_lower(s));
}
} else {
weight = FONT_WEIGHT_NORMAL;
}
str = lv_strdup(lua_tostring(L, 1));
if (str == NULL) {
return luaL_error(L, "no memory");
}
name = to_lower(str);
while (*name) {
if (*name == ' ') {
name++;
continue;
}
char *end = luavgl_strchr(name, ',');
if (end != NULL) {
*end = '\0';
} else {
end = name + lv_strlen(name);
}
char *trim = end - 1;
while (*trim == ' ') {
*trim-- = '\0'; /* trailing space. */
}
font = _luavgl_font_create(L, name, size, weight);
if (font) {
break;
}
name = end + 1; /* next */
luavgl_ctx_t *ctx = luavgl_context(L);
if (ctx->make_font) {
font = ctx->make_font(name);
}
lv_free(str);
if (font) {
lua_pushlightuserdata(L, (void *)font);
return 1;

@ -12,8 +12,7 @@
extern "C" {
#endif
typedef const lv_font_t *(*make_font_cb)(const char *name, int size,
int weight);
typedef const lv_font_t *(*make_font_cb)(const char *);
typedef void (*delete_font_cb)(const lv_font_t *);
typedef int (*luavgl_pcall_t)(lua_State *L, int nargs, int nresults);

@ -47,7 +47,7 @@
#if LV_USE_STDLIB_MALLOC == LV_STDLIB_BUILTIN
/*Size of the memory available for `lv_malloc()` in bytes (>= 2kB)*/
#define LV_MEM_SIZE (256U * 1024U) /*[bytes]*/
#define LV_MEM_SIZE (1024U * 1024U) /*[bytes]*/
/*Size of the memory expand for `lv_malloc()` in bytes*/
#define LV_MEM_POOL_EXPAND_SIZE 0

@ -1,6 +1,6 @@
local lvgl = require("lvgl")
return {
fusion_12 = lvgl.Font("fusion", 12, "normal"),
fusion_10 = lvgl.Font("fusion", 10, "normal"),
fusion_12 = lvgl.Font("//lua/fonts/fusion12"),
fusion_10 = lvgl.Font("//lua/fonts/fusion10"),
}

@ -1246,7 +1246,7 @@ function timer:ready()
end
--[[
Font is a light userdata that can be uset to set style text_font.
Font is a light userdata that can be used to set style text_font.
]]
--- @class Font
---
@ -1368,7 +1368,7 @@ end
--- @field arc_img_src? integer
--- @field text_color? integer | string
--- @field text_opa? integer
--- @field text_font? Font | BuiltinFont
--- @field text_font? Font
--- @field text_letter_space? integer
--- @field text_line_space/ integer
--- @field text_decor? integer

@ -2,6 +2,4 @@
#
# SPDX-License-Identifier: GPL-3.0-only
idf_component_register(
SRCS "font_fusion_10.c" "font_fusion_12.c" "splash.c"
REQUIRES "lvgl")
idf_component_register(SRCS "splash.c" REQUIRES "lvgl")

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

@ -5,6 +5,9 @@
*/
#include "lua/bridge.hpp"
#include <stdint.h>
#include <stdio.h>
#include <sys/unistd.h>
#include <cstdint>
#include <memory>
@ -12,7 +15,9 @@
#include "database/database.hpp"
#include "database/index.hpp"
#include "esp_heap_caps.h"
#include "esp_log.h"
#include "font/lv_binfont_loader.h"
#include "lauxlib.h"
#include "lua.h"
#include "lua.hpp"
@ -30,6 +35,7 @@
#include "events/event_queue.hpp"
#include "lua/property.hpp"
#include "misc/lv_fs.h"
#include "system_fsm/service_locator.hpp"
#include "ui/ui_events.hpp"
@ -38,29 +44,74 @@ int luaopen_linenoise(lua_State* L);
int luaopen_term_core(lua_State* L);
}
LV_FONT_DECLARE(font_fusion_12);
LV_FONT_DECLARE(font_fusion_10);
namespace lua {
[[maybe_unused]] static constexpr char kTag[] = "lua_bridge";
static constexpr char kBridgeKey[] = "bridge";
static auto make_font_cb(const char* name, int size, int weight)
-> const lv_font_t* {
if (std::string{"fusion"} == name) {
if (size == 12) {
return &font_fusion_12;
}
if (size == 10) {
return &font_fusion_10;
static auto make_font_cb(const char* name) -> const lv_font_t* {
// Most Lua file paths start with "//" in order to deal with LVGL's Windows-y
// approach to paths. Try to handle such paths correctly so that paths in Lua
// code look a bit more consistent.
{
std::string name_str = name;
if (name_str.starts_with("//")) {
name++;
}
}
return NULL;
// This following is a bit C-brained. Sorry.
ESP_LOGI(kTag, "load font '%s'", name);
FILE* f = fopen(name, "r");
if (!f) {
return NULL;
}
uint8_t* data = NULL;
long len = 0;
lv_font_t* font = NULL;
if (fseek(f, 0, SEEK_END)) {
goto fail_with_file;
}
len = ftell(f);
if (len <= 0) {
goto fail_with_file;
}
if (fseek(f, 0, SEEK_SET)) {
goto fail_with_file;
}
data = reinterpret_cast<uint8_t*>(heap_caps_malloc(len, MALLOC_CAP_SPIRAM));
if (!data) {
goto fail_with_buffer;
}
if (fread(data, 1, len, f) < len) {
goto fail_with_buffer;
}
// We can finally start parsing the font!
font = lv_binfont_create_from_buffer(data, len);
fail_with_buffer:
// LVGL copies the font data out of the buffer, so we don't need to big raw
// data alloc after this function returns.
heap_caps_free(data);
fail_with_file:
fclose(f);
return font;
}
static auto delete_font_cb(const lv_font_t* font) -> void {}
static auto delete_font_cb(const lv_font_t* font) -> void {
// FIXME: luavgl never actually calls this?
}
auto Bridge::Get(lua_State* state) -> Bridge* {
lua_pushstring(state, kBridgeKey);

@ -8,7 +8,7 @@ fusion_12() {
-r 0x20-0x7F,0xA0-0xFF \
-r 0x3000-0x303f,0x3040-0x309F,0x30A0-0x30FF,0xFF00-0xFFEF,0x4E00-0x9FAF \
--size 12 \
--bpp 1 --format lvgl -o font_fusion_12.c
--bpp 1 --format bin -o fusion12
echo "finished fusion_12"
}
@ -21,7 +21,7 @@ fusion_10() {
-r 0x3000-0x303f,0x3040-0x309F,0x30A0-0x30FF \
-r 0xFF00-0xFFEF,0x4E00-0x9FAF \
--size 10 \
--bpp 1 --format lvgl -o font_fusion_10.c
--bpp 1 --format bin -o fusion10
echo "finished fusion_10"
}

Loading…
Cancel
Save