This saves a second or two from bootup; AFAICT this *mostly* reclaims the dynamic fonts boot time regression.custom
parent
cbcf1bea61
commit
88ac96242f
@ -1,49 +1,23 @@ |
|||||||
#include "luavgl.h" |
#include "luavgl.h" |
||||||
#include "private.h" |
#include "private.h" |
||||||
|
|
||||||
static char *to_lower(char *str) |
|
||||||
{ |
|
||||||
for (char *s = str; *s; ++s) |
|
||||||
*s = *s >= 'A' && *s <= 'Z' ? *s | 0x60 : *s; |
|
||||||
return str; |
|
||||||
} |
|
||||||
|
|
||||||
static char *luavgl_strchr(const char *s, char c) |
|
||||||
{ |
|
||||||
while (*s) { |
|
||||||
if (c == *s) { |
|
||||||
return (char *)s; |
|
||||||
} |
|
||||||
s++; |
|
||||||
} |
|
||||||
return NULL; |
|
||||||
} |
|
||||||
|
|
||||||
/**
|
|
||||||
* Dynamic font family fallback is not supported. |
|
||||||
* The fallback only happen when font creation fails and continue to try next |
|
||||||
* one. Fallback logic in lvgl is supposed to be system wide. |
|
||||||
* |
|
||||||
* lvgl.Font("MiSansW medium, montserrat", 24, "normal") |
|
||||||
*/ |
|
||||||
static int luavgl_font_create(lua_State *L) |
static int luavgl_font_create(lua_State *L) |
||||||
{ |
{ |
||||||
|
|
||||||
if (!lua_isstring(L, 1)) { |
if (!lua_isstring(L, 1)) { |
||||||
return luaL_argerror(L, 1, "expect string"); |
return luaL_argerror(L, 1, "expect string"); |
||||||
} |
} |
||||||
const char *name = lua_tostring(L, 1); |
if (!lua_isfunction(L, 2)) { |
||||||
const lv_font_t *font = NULL; |
return luaL_argerror(L, 1, "expect function"); |
||||||
|
} |
||||||
|
|
||||||
luavgl_ctx_t *ctx = luavgl_context(L); |
luavgl_ctx_t *ctx = luavgl_context(L); |
||||||
if (ctx->make_font) { |
if (!ctx->make_font) { |
||||||
font = ctx->make_font(name); |
return luaL_error(L, "cannot create font"); |
||||||
} |
} |
||||||
|
|
||||||
if (font) { |
const char *name = lua_tostring(L, 1); |
||||||
lua_pushlightuserdata(L, (void *)font); |
int cb_ref = luaL_ref(L, LUA_REGISTRYINDEX); |
||||||
return 1; |
ctx->make_font(L, name, cb_ref); |
||||||
} |
|
||||||
|
|
||||||
return luaL_error(L, "cannot create font"); |
return 0; |
||||||
} |
} |
||||||
|
@ -1,6 +1,36 @@ |
|||||||
local lvgl = require("lvgl") |
local lvgl = require("lvgl") |
||||||
|
|
||||||
return { |
local fonts = {} |
||||||
fusion_12 = lvgl.Font("//lua/fonts/fusion12"), |
local fonts_priv = { |
||||||
fusion_10 = lvgl.Font("//lua/fonts/fusion10"), |
has_invoked_cb = false, |
||||||
|
cb = nil, |
||||||
} |
} |
||||||
|
|
||||||
|
function fonts_priv.has_loaded_all() |
||||||
|
return fonts.fusion_12 and fonts.fusion_10 |
||||||
|
end |
||||||
|
|
||||||
|
function fonts_priv.invoke_cb() |
||||||
|
if fonts_priv.has_invoked_cb or not fonts_priv.cb then return end |
||||||
|
if not fonts_priv.has_loaded_all() then return end |
||||||
|
fonts_priv.has_invoked_cb = true |
||||||
|
fonts_priv.cb() |
||||||
|
end |
||||||
|
|
||||||
|
lvgl.Font("//lua/fonts/fusion12", function(font) |
||||||
|
fonts.fusion_12 = font |
||||||
|
fonts_priv.invoke_cb() |
||||||
|
end) |
||||||
|
|
||||||
|
lvgl.Font("//lua/fonts/fusion10", function(font) |
||||||
|
fonts.fusion_10 = font |
||||||
|
fonts_priv.invoke_cb() |
||||||
|
end) |
||||||
|
|
||||||
|
function fonts.on_loaded(cb) |
||||||
|
fonts_priv.cb = cb |
||||||
|
fonts_priv.has_invoked_cb = false |
||||||
|
fonts_priv.invoke_cb() |
||||||
|
end |
||||||
|
|
||||||
|
return fonts |
||||||
|
@ -0,0 +1,115 @@ |
|||||||
|
/*
|
||||||
|
* Copyright 2024 jacqueline <me@jacqueline.id.au> |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: GPL-3.0-only |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "lua_font.hpp" |
||||||
|
|
||||||
|
#include <cstdint> |
||||||
|
#include <string> |
||||||
|
|
||||||
|
#include "lauxlib.h" |
||||||
|
#include "lua.h" |
||||||
|
#include "lvgl.h" |
||||||
|
|
||||||
|
#include "events/event_queue.hpp" |
||||||
|
#include "lua/bridge.hpp" |
||||||
|
#include "lua/lua_registry.hpp" |
||||||
|
#include "lua/lua_thread.hpp" |
||||||
|
|
||||||
|
namespace lua { |
||||||
|
|
||||||
|
[[maybe_unused]] static constexpr char kTag[] = "lua_font"; |
||||||
|
|
||||||
|
/* Reads the given file completely into PSRAM. */ |
||||||
|
static auto readFont(std::string path) -> std::span<uint8_t> { |
||||||
|
// This following is a bit C-brained. Sorry.
|
||||||
|
FILE* f = fopen(path.c_str(), "r"); |
||||||
|
if (!f) { |
||||||
|
return {}; |
||||||
|
} |
||||||
|
|
||||||
|
uint8_t* data = NULL; |
||||||
|
long len = 0; |
||||||
|
|
||||||
|
if (fseek(f, 0, SEEK_END)) { |
||||||
|
goto fail; |
||||||
|
} |
||||||
|
|
||||||
|
len = ftell(f); |
||||||
|
if (len <= 0) { |
||||||
|
goto fail; |
||||||
|
} |
||||||
|
|
||||||
|
if (fseek(f, 0, SEEK_SET)) { |
||||||
|
len = 0; |
||||||
|
goto fail; |
||||||
|
} |
||||||
|
|
||||||
|
data = reinterpret_cast<uint8_t*>(heap_caps_malloc(len, MALLOC_CAP_SPIRAM)); |
||||||
|
if (!data) { |
||||||
|
len = 0; |
||||||
|
goto fail; |
||||||
|
} |
||||||
|
|
||||||
|
if (fread(data, 1, len, f) < len) { |
||||||
|
heap_caps_free(data); |
||||||
|
len = 0; |
||||||
|
} |
||||||
|
|
||||||
|
fail: |
||||||
|
fclose(f); |
||||||
|
|
||||||
|
return {data, static_cast<size_t>(len)}; |
||||||
|
} |
||||||
|
|
||||||
|
static auto parseFont(std::span<uint8_t> data) -> lv_font_t* { |
||||||
|
if (data.empty()) { |
||||||
|
return nullptr; |
||||||
|
} |
||||||
|
|
||||||
|
lv_font_t* font = lv_binfont_create_from_buffer(data.data(), data.size()); |
||||||
|
heap_caps_free(data.data()); |
||||||
|
|
||||||
|
return font; |
||||||
|
} |
||||||
|
|
||||||
|
auto loadFont(lua_State* L, const char* path, int cb_ref) -> void { |
||||||
|
// 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 path_str = path; |
||||||
|
if (path_str.starts_with("//")) { |
||||||
|
path++; |
||||||
|
path_str = path; |
||||||
|
} |
||||||
|
|
||||||
|
// Do the file read from the current thread, since the path might be for a
|
||||||
|
// file in flash, and we can't read from flash in a background task.
|
||||||
|
auto font_data = readFont(path_str); |
||||||
|
|
||||||
|
Bridge* bridge = Bridge::Get(L); |
||||||
|
bridge->services().bg_worker().Dispatch<void>([=]() { |
||||||
|
// Do the parsing now that we're in the background.
|
||||||
|
lv_font_t* font = parseFont(font_data); |
||||||
|
|
||||||
|
// Hop back to the UI task to invoke the Lua callback.
|
||||||
|
events::Ui().RunOnTask([=] { |
||||||
|
// Retrieve the callback by ref, and release the ref.
|
||||||
|
lua_rawgeti(L, LUA_REGISTRYINDEX, cb_ref); |
||||||
|
luaL_unref(L, LUA_REGISTRYINDEX, cb_ref); |
||||||
|
|
||||||
|
// We always invoke the callback, but we don't always have a result.
|
||||||
|
if (font) { |
||||||
|
lua_pushlightuserdata(L, (void*)font); |
||||||
|
} else { |
||||||
|
lua_pushnil(L); |
||||||
|
} |
||||||
|
|
||||||
|
CallProtected(L, 1, 0); |
||||||
|
}); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
} // namespace lua
|
@ -0,0 +1,15 @@ |
|||||||
|
/*
|
||||||
|
* Copyright 2024 jacqueline <me@jacqueline.id.au> |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: GPL-3.0-only |
||||||
|
*/ |
||||||
|
|
||||||
|
#pragma once |
||||||
|
|
||||||
|
#include "lua.hpp" |
||||||
|
|
||||||
|
namespace lua { |
||||||
|
|
||||||
|
auto loadFont(lua_State* L, const char* name, int cb_ref) -> void; |
||||||
|
|
||||||
|
} |
Loading…
Reference in new issue