Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 47 additions & 1 deletion internal_filesystem/lib/mpos/ui/font_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,9 +280,55 @@ def _create_emoji_font(cls, size):
cls._ensure_emoji_maps()

try:
return lv.imgfont_create(size, cls._imgfont_path_cb, None)
font = lv.imgfont_create(size, cls._imgfont_path_cb, None)
except Exception:
return None
if font is None:
return None

# Push the same codepoint accept/exclude ranges that _get_emoji_src
# checks down into the C-level imgfont. Once set, LVGL stops calling
# our Python _imgfont_path_cb for codepoints that are guaranteed to
# have no emoji glyph (ASCII, CJK, PUA, etc.) — turning the per-glyph
# cost from "Python call + return" into a pair of int compares in C.
# This is the actual fix for the scrolling slowdown: composed-font
# text where most glyphs are non-emoji now stays in C end-to-end.
try:
cp_min, cp_max = cls._emoji_codepoint_bounds()
lv.imgfont_set_range(font, cp_min, cp_max, 0xE000, 0xF8FF)
except AttributeError:
# Older LVGL build without lv_imgfont_set_range — that's OK, the
# fast path is just a nice-to-have. Behaviour falls back to the
# pre-patch composed-font path (correct, just slower).
cls._debug("imgfont_set_range unavailable — emoji filter not applied")
except Exception as err:
cls._debug("imgfont_set_range failed: " + repr(err))

return font

@classmethod
def _emoji_codepoint_bounds(cls):
"""Smallest / largest codepoint present across all loaded emoji maps.
Recomputed on demand and cached — the maps are immutable after init.
Falls back to a safe wide range if no emojis are loaded yet, so we
never narrow the filter in a way that would hide future glyphs."""
bounds = cls.__dict__.get("_emoji_cp_bounds")
if bounds is not None:
return bounds
lo = None
hi = None
for emap in cls._emoji_maps.values():
for cp in emap:
if lo is None or cp < lo: lo = cp
if hi is None or cp > hi: hi = cp
if lo is None:
# No emojis loaded — accept everything so behaviour matches the
# unpatched code path. Caller should re-create the font once
# maps are populated.
return (0, 0xFFFFFFFF)
bounds = (lo, hi)
cls._emoji_cp_bounds = bounds
return bounds

@classmethod
def _ensure_emoji_maps(cls):
Expand Down
2 changes: 1 addition & 1 deletion lvgl_micropython
14 changes: 14 additions & 0 deletions scripts/build_mpos.sh
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,20 @@ pushd "$codebasedir"/lvgl_micropython/lib/micropython
patch -p1 --forward < ../../esp32_uart_repl_runtime.patch || true
popd

# Fast emoji rendering: bake a codepoint range filter into lv_imgfont so
# non-emoji glyphs bail out in C without invoking the MicroPython path_cb.
# Pre-existence check so MPOS still builds against older pinned
# lvgl_micropython SHAs that don't ship this patch yet (FontManager.py
# degrades gracefully via try/except AttributeError when the setter is
# absent — see internal_filesystem/lib/mpos/ui/font_manager.py).
imgfont_patch="$codebasedir"/lvgl_micropython/imgfont_set_range.patch
if [ -f "$imgfont_patch" ]; then
echo "Applying lvgl_micropython imgfont_set_range patch..."
pushd "$codebasedir"/lvgl_micropython/lib/lvgl
patch -p1 --forward < "$imgfont_patch" || true
popd
fi

echo "Minifying and inlining HTML..."
pushd "$codebasedir"/webrepl/
python3 inline_minify_webrepl.py
Expand Down
Loading