Skip to content

feat(lua): add lvgl.bitmap object - #4

Draft
thomaskistler wants to merge 10 commits into
mainfrom
thomaskistler/lvgl-bitmap-widget
Draft

thomaskistler wants to merge 10 commits into
mainfrom
thomaskistler/lvgl-bitmap-widget

Conversation

@thomaskistler

@thomaskistler thomaskistler commented Nov 1, 2026 •

Copy link
Copy Markdown
Owner

Fixes: N/A (feature; no linked issue). Alternative to EdgeTX#7947, which adds a bitmap source to lvgl.image.

Summary of changes:

Adds lvgl.bitmap([parent, ]params), a separate Lua LVGL object (also lvgl.BITMAP for lvgl.build) that wraps StaticBitmap. lvgl.image is not modified.

local tile = lvgl.bitmap({ file = "/IMAGES/tile.jpg", x = 0, y = 0, w = 256, h = 256 })
tile:set({ file = "/IMAGES/other.jpg" })

Motivation

lvgl.image decodes through LVGL's shared image cache, which has 8 entries (LV_IMG_CACHE_DEF_SIZE). A Lua widget that shows more images than that, for example a map made of 256x256 JPEG tiles, keeps evicting images that are still visible and decodes them again on redraw. lvgl.bitmap decodes once when its file changes and the object owns the pixels, so a widget can keep one object per image and decide for itself which ones stay decoded.

Details

  • file is a string or a function, like lvgl.image. The file is only decoded when it changes. A function is only called while the object is visible. Setting a string replaces a previous function.
  • The image is scaled to fit the frame when it is decoded and centered. Changing w/h reloads it. Hidden objects keep their pixels.
  • StaticBitmap::setSource(filename) now chooses the format from the file like BitmapBuffer::loadBitmap: RGB565 for opaque images, ARGB4444 for images with alpha. setSource(filename, fmt) forces a format, and the constructor takes the same optional format. Existing callers (model select, file preview, model bitmap widget) pass BMP_ARGB4444 explicitly, so their rendering does not change.
  • Previously StaticBitmap always used ARGB4444, which keeps 4 bits per channel and stores 3 bytes per pixel. For an opaque JPEG tile, an ad-hoc host measurement gave 192 KB and 31.1 dB against the 8-bit decode, versus 128 KB and 39.9 dB for RGB565. With the format chosen from the file, opaque images are byte-identical to what Bitmap.open produces.
  • The pixel memory is not counted against the Lua bitmap memory limit (LUA_MEM_EXTRA_MAX).
  • A new file is only applied after all the parameters of an update have been accepted, together with a size change. A rejected set() leaves the source alone, and changing the file and the size in one set() decodes once. Other parameters of a rejected set() behave as for every other object.
  • A file that cannot be loaded, or does not fit in memory, leaves the object blank. It is not retried until the file or frame size changes.
  • The frame must have a real size (or come from a size function) and cover at most four times the display area, otherwise creation raises an error instead of allocating an arbitrarily large image. Frames larger than the display are allowed, e.g. inside a scrollable container.
  • Lifetime: the pixels belong to the object's window and are freed when the window closes, when the source changes, or immediately with set({file=''}). Updates are ignored once the window has been closed.
  • The creation functions (lvgl.label(parent, ...) etc. and lvgl.build) now run in a protected call. Before, an error left the temporary parent set, so objects created afterwards were attached to the wrong parent, and a half-built object kept its window. This is a separate commit (fix(lua): undo a failed LVGL object creation) and is independent of lvgl.bitmap.
  • StaticBitmap::clear() now only deletes child windows. Window::clear() used to delete the image canvas while BitmapBuffer still pointed at it, and BitmapBuffer::resizeToLVGL() now reports allocation failure instead of leaving data in the wrong format.
  • ETX_BITMAP is inserted after ETX_QRCODE, so the numeric values of later lvgl.* type constants shift by one.
  • Documentation is in the lvgl.bitmap luadoc block. It is marked as introduced in 3.0.0.

Validation

Built and tested the native TX16S target on macOS:

  • cmake -DCMAKE_BUILD_TYPE=Debug -DPCB=X10 -DPCBREV=TX16S .. in build/, then cmake --build . --target native-configure and cmake --build native --target gtests-radio
  • native/gtests-radio: 276 tests passed, including 29 LuaLvglBitmap and 5 LuaLvglObjectErrors tests LuaLvglBitmap tests

Tested on a TX16S Mk2 with a v2.12.5 backport of these two commits (branch thomaskistler/lvgl-bitmap-2-12-test, tx16s Release build). A test widget panned a 4x4 set of 256x256 JPEG tiles, with up to 13 on screen at once: with lvgl.image dragging was slow, since the 8-entry LVGL cache evicts tiles that are still visible, and with lvgl.bitmap it was fast. This was a subjective comparison, with no decode counts or frame timing measured on the radio.

Not done: an ARM build of main, a Companion build, and measuring free heap on the radio.

@thomaskistler
thomaskistler force-pushed the thomaskistler/lvgl-bitmap-widget branch 2 times, most recently from 2d10a0d to b1cf713 Compare November 1, 2026 19:23
philmoz and others added 8 commits November 3, 2026 11:00
…sh (EdgeTX#7980)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…eTX#7976)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…pp (EdgeTX#7983)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
… drive (EdgeTX#7982)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Add the bitmap object and Lua registration on upstream main without shared Lua/LVGL hardening. Keep RGB565 and alpha conversion, source/frame decode coalescing, protected bitmap-only setter staging, image-canvas preservation, and source/scaling bounds.

Make update virtual for bitmap-specific setters. Retain the existing constructor, callback and manager behavior and cover the feature with bitmap-focused regressions.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@thomaskistler
thomaskistler force-pushed the thomaskistler/lvgl-bitmap-widget branch from 8a43230 to 2c63158 Compare November 3, 2026 06:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants