mirror of
https://github.com/slint-ui/slint.git
synced 2025-08-04 10:50:00 +00:00
Make it possible to use slint-esp with displays that expect little endian ordered RGB565 pixels (#5344)
We can't automatically determine what the display supports, so we need to make this an opt-in - just like it is with LVGL.
This commit is contained in:
parent
9f6db89770
commit
1378d6e3bc
5 changed files with 148 additions and 28 deletions
|
@ -84,8 +84,13 @@ extern "C" void app_main(void)
|
|||
static std::vector<slint::platform::Rgb565Pixel> buffer(BSP_LCD_H_RES * BSP_LCD_V_RES);
|
||||
|
||||
/* Initialize Slint's ESP platform support*/
|
||||
slint_esp_init(slint::PhysicalSize({ BSP_LCD_H_RES, BSP_LCD_V_RES }), panel_handle,
|
||||
touch_handle, buffer);
|
||||
slint_esp_init(SlintPlatformConfiguration {
|
||||
.size = slint::PhysicalSize({ BSP_LCD_H_RES, BSP_LCD_V_RES }),
|
||||
.panel = panel_handle,
|
||||
.touch = touch_handle,
|
||||
.buffer1 = buffer,
|
||||
.color_swap_16 = true });
|
||||
|
||||
/* Instantiate the UI */
|
||||
auto ui = AppWindow::create();
|
||||
/* Show it on the screen and run the event loop */
|
||||
|
@ -168,6 +173,13 @@ using [Windows Subsystem for Linux](https://learn.microsoft.com/en-us/windows/ws
|
|||
One reason could be that you don't have enough ram for the heap or the stack.
|
||||
Make sure that the stack is big enough (~8KiB), and that all the RAM was made available for the heap allocator.
|
||||
|
||||
### Wrong colors shown
|
||||
|
||||
If colors look inverted on your display, it may be an incompatibility between how RGB565 colors are ordered in little-endian
|
||||
and your display expecting a different byte order. Typically, esp32 devices are little ending and display controllers often
|
||||
expect big-endian or `esp_lcd` configures them accordingly. Therefore, by default Slint converts pixels to big-endian.
|
||||
If your display controller expects little endian, set the `color_swap_16` field in `SlintPlatformConfiguration` to `false`.
|
||||
|
||||
## License
|
||||
|
||||
You can use Slint under ***any*** of the following licenses, at your choice:
|
||||
|
|
|
@ -7,10 +7,58 @@
|
|||
#include "esp_lcd_touch.h"
|
||||
#include "esp_lcd_types.h"
|
||||
|
||||
/**
|
||||
* This data structure configures the Slint platform for use with ESP-IDF, in particular
|
||||
* the esp_lcd component (
|
||||
* https://docs.espressif.com/projects/esp-idf/en/stable/esp32/api-reference/peripherals/lcd.html )
|
||||
* for touch input and on-screen rendering.
|
||||
*
|
||||
* Slint supports three different ways of rendering:
|
||||
*
|
||||
* * Single-buffering: Allocate one frame-buffer at a location of your choosing in RAM, and
|
||||
* set the `buffer1` field.
|
||||
* * Double-buffering: Call `esp_lcd_rgb_panel_get_frame_buffer` to obtain two frame buffers
|
||||
* allocated by the `esp_lcd` driver and set `buffer1` and `buffer2`.
|
||||
* * Line-by-line rendering: Set neither `buffer1` nor `buffer2` to instruct Slint to allocate
|
||||
* a buffer (with MALLOC_CAP_INTERNAL) big enough to hold one line,
|
||||
* render into it, and send it to the display. This is an experimental
|
||||
* feature that requires enabling the `SLINT_FEATURE_EXPERIMENTAL` CMake
|
||||
* option.
|
||||
*
|
||||
* Use single-buffering if you can allocate a buffer in a memory region that allows the esp_lcd
|
||||
* driver to efficiently transfer to the display. Use double-buffering if your driver supports
|
||||
* calling `esp_lcd_rgb_panel_get_frame_buffer` and the buffers can be accessed directly by the
|
||||
* display controller. Use line-by-line rendering if you don't have sufficient memory or rendering
|
||||
* to internal memory (MALLOC_CAP_INTERNAL) and flushing to the display is faster than rendering
|
||||
* into memory buffers that may be slower to access for the CPU.
|
||||
*/
|
||||
struct SlintPlatformConfiguration
|
||||
{
|
||||
/// The size of the screen in pixels.
|
||||
slint::PhysicalSize size;
|
||||
/// The handle to the display as previously initialized by `bsp_display_new` or
|
||||
/// `esp_lcd_panel_init`.
|
||||
esp_lcd_panel_handle_t panel;
|
||||
/// The touch screen handle, if the device is equipped with a touch screen.
|
||||
std::optional<esp_lcd_touch_handle_t> touch;
|
||||
/// The buffer Slint will render into. It must have have the size of at least one frame. Slint
|
||||
/// calls esp_lcd_panel_draw_bitmap to flush the buffer to the screen.
|
||||
std::optional<std::span<slint::platform::Rgb565Pixel>> buffer1 = {};
|
||||
/// If specified, this is a second buffer that will be used for double-buffering. Use this if
|
||||
/// your LCD panel supports double buffering: Call `esp_lcd_rgb_panel_get_frame_buffer` to
|
||||
/// obtain two buffers and set `buffer` and `buffer2` in this data structure.
|
||||
std::optional<std::span<slint::platform::Rgb565Pixel>> buffer2 = {};
|
||||
slint::platform::SoftwareRenderer::RenderingRotation rotation =
|
||||
slint::platform::SoftwareRenderer::RenderingRotation::NoRotation;
|
||||
// Swap the 2 bytes of RGB 565 pixels before sending to the display. Use this
|
||||
// if your CPU is little endian but the display expects big-endian.
|
||||
bool color_swap_16 = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the Slint platform for ESP-IDF
|
||||
*
|
||||
* This must be called before any other call to the slint library.
|
||||
* This must be called before any other call to the Slint library.
|
||||
*
|
||||
* - `size` is the size of the screen
|
||||
* - `panel` is a handle to the display.
|
||||
|
@ -20,6 +68,9 @@
|
|||
* - `buffer2`, if specified, is a second buffer to be used with double buffering,
|
||||
* both buffer1 and buffer2 should then be obtained with `esp_lcd_rgb_panel_get_frame_buffer`
|
||||
* - `rotation` applies a transformation while rendering in the buffer
|
||||
*
|
||||
* Note: For compatibility, this function overload selects RGB16 byte swapping if single-buffering
|
||||
* is selected as rendering method.
|
||||
*/
|
||||
void slint_esp_init(slint::PhysicalSize size, esp_lcd_panel_handle_t panel,
|
||||
std::optional<esp_lcd_touch_handle_t> touch,
|
||||
|
@ -38,3 +89,10 @@ void slint_esp_init(slint::PhysicalSize size, esp_lcd_panel_handle_t panel,
|
|||
std::optional<esp_lcd_touch_handle_t> touch,
|
||||
slint::platform::SoftwareRenderer::RenderingRotation rotation = {});
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Initialize the Slint platform for ESP-IDF.
|
||||
*
|
||||
* This must be called before any other call to the Slint library.
|
||||
*/
|
||||
void slint_esp_init(const SlintPlatformConfiguration &config);
|
||||
|
|
|
@ -20,18 +20,22 @@ using RepaintBufferType = slint::platform::SoftwareRenderer::RepaintBufferType;
|
|||
|
||||
struct EspPlatform : public slint::platform::Platform
|
||||
{
|
||||
EspPlatform(slint::PhysicalSize size, esp_lcd_panel_handle_t panel,
|
||||
std::optional<esp_lcd_touch_handle_t> touch,
|
||||
std::optional<std::span<slint::platform::Rgb565Pixel>> buffer1,
|
||||
std::optional<std::span<slint::platform::Rgb565Pixel>> buffer2 = {},
|
||||
slint::platform::SoftwareRenderer::RenderingRotation rotation = {})
|
||||
: size(size),
|
||||
panel_handle(panel),
|
||||
touch_handle(touch),
|
||||
buffer1(buffer1),
|
||||
buffer2(buffer2),
|
||||
rotation(rotation)
|
||||
EspPlatform(const SlintPlatformConfiguration &config)
|
||||
: size(config.size),
|
||||
panel_handle(config.panel),
|
||||
touch_handle(config.touch),
|
||||
buffer1(config.buffer1),
|
||||
buffer2(config.buffer2),
|
||||
color_swap_16(config.color_swap_16),
|
||||
rotation(config.rotation)
|
||||
{
|
||||
#if !defined(SLINT_FEATURE_EXPERIMENTAL)
|
||||
if (!buffer1 && !buffer2) {
|
||||
ESP_LOGI(TAG,
|
||||
"FATAL: Line-by-line rendering requested for use with slint-esp is "
|
||||
"experimental and not enabled in this build.");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
std::unique_ptr<slint::platform::WindowAdapter> create_window_adapter() override;
|
||||
|
@ -47,6 +51,7 @@ private:
|
|||
std::optional<esp_lcd_touch_handle_t> touch_handle;
|
||||
std::optional<std::span<slint::platform::Rgb565Pixel>> buffer1;
|
||||
std::optional<std::span<slint::platform::Rgb565Pixel>> buffer2;
|
||||
bool color_swap_16;
|
||||
slint::platform::SoftwareRenderer::RenderingRotation rotation;
|
||||
class EspWindowAdapter *m_window = nullptr;
|
||||
|
||||
|
@ -210,6 +215,20 @@ void EspPlatform::run_event_loop()
|
|||
if (buffer1) {
|
||||
auto region = m_window->m_renderer.render(buffer1.value(),
|
||||
rotated ? size.height : size.width);
|
||||
|
||||
if (color_swap_16) {
|
||||
for (auto [o, s] : region.rectangles()) {
|
||||
for (int y = o.y; y < o.y + s.height; y++) {
|
||||
for (int x = o.x; x < o.x + s.width; x++) {
|
||||
// Swap endianess to big endian
|
||||
auto px = reinterpret_cast<uint16_t *>(
|
||||
&buffer1.value()[y * size.width + x]);
|
||||
*px = (*px << 8) | (*px >> 8);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer2) {
|
||||
auto s = region.bounding_box_size();
|
||||
if (s.width > 0 && s.height > 0) {
|
||||
|
@ -229,12 +248,6 @@ void EspPlatform::run_event_loop()
|
|||
} else {
|
||||
for (auto [o, s] : region.rectangles()) {
|
||||
for (int y = o.y; y < o.y + s.height; y++) {
|
||||
for (int x = o.x; x < o.x + s.width; x++) {
|
||||
// Swap endianess to big endian
|
||||
auto px = reinterpret_cast<uint16_t *>(
|
||||
&buffer1.value()[y * size.width + x]);
|
||||
*px = (*px << 8) | (*px >> 8);
|
||||
}
|
||||
esp_lcd_panel_draw_bitmap(panel_handle, o.x, y, o.x + s.width,
|
||||
y + 1,
|
||||
buffer1->data() + y * size.width + o.x);
|
||||
|
@ -256,6 +269,13 @@ void EspPlatform::run_event_loop()
|
|||
std::span<slint::platform::Rgb565Pixel> view { lb.get(),
|
||||
line_end - line_start };
|
||||
render_fn(view);
|
||||
if (color_swap_16) {
|
||||
// Swap endianess to big endian
|
||||
std::for_each(view.begin(), view.end(), [](auto &rgbpix) {
|
||||
auto px = reinterpret_cast<uint16_t *>(&rgbpix);
|
||||
*px = (*px << 8) | (*px >> 8);
|
||||
});
|
||||
}
|
||||
esp_lcd_panel_draw_bitmap(panel_handle, line_start, line_y, line_end,
|
||||
line_y + 1, lb.get());
|
||||
});
|
||||
|
@ -305,8 +325,19 @@ void slint_esp_init(slint::PhysicalSize size, esp_lcd_panel_handle_t panel,
|
|||
std::optional<std::span<slint::platform::Rgb565Pixel>> buffer2,
|
||||
slint::platform::SoftwareRenderer::RenderingRotation rotation)
|
||||
{
|
||||
slint::platform::set_platform(
|
||||
std::make_unique<EspPlatform>(size, panel, touch, buffer1, buffer2, rotation));
|
||||
|
||||
SlintPlatformConfiguration config {
|
||||
.size = size,
|
||||
.panel = panel,
|
||||
.touch = touch,
|
||||
.buffer1 = buffer1,
|
||||
.buffer2 = buffer2,
|
||||
.rotation = rotation,
|
||||
// For compatibility with earlier versions of Slint, we compute the value of
|
||||
// color_swap_16 the way it was implemented in Slint (slint-esp) <= 1.6.0:
|
||||
.color_swap_16 = buffer2.has_value()
|
||||
};
|
||||
slint_esp_init(config);
|
||||
}
|
||||
|
||||
#ifdef SLINT_FEATURE_EXPERIMENTAL
|
||||
|
@ -314,7 +345,18 @@ void slint_esp_init(slint::PhysicalSize size, esp_lcd_panel_handle_t panel,
|
|||
std::optional<esp_lcd_touch_handle_t> touch,
|
||||
slint::platform::SoftwareRenderer::RenderingRotation rotation)
|
||||
{
|
||||
slint::platform::set_platform(std::make_unique<EspPlatform>(size, panel, touch, std::nullopt,
|
||||
std::nullopt, rotation));
|
||||
SlintPlatformConfiguration config { .size = size,
|
||||
.panel = panel,
|
||||
.touch = touch,
|
||||
.buffer1 = std::nullopt,
|
||||
.buffer2 = std::nullopt,
|
||||
.rotation = rotation,
|
||||
.color_swap_16 = false };
|
||||
slint_esp_init(config);
|
||||
}
|
||||
#endif
|
||||
|
||||
void slint_esp_init(const SlintPlatformConfiguration &config)
|
||||
{
|
||||
slint::platform::set_platform(std::make_unique<EspPlatform>(config));
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue