Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

esp-idf: Add support for RGB888 displays #7301

Merged
merged 6 commits into from
Jan 13, 2025
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
2 changes: 1 addition & 1 deletion api/cpp/docs/mcu/esp-idf/troubleshoot.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Make sure that the stack is big enough (~8KiB), and that all the RAM was made av
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`.
If your display controller expects little endian, set the `byte_swap` field in `SlintPlatformConfiguration` to `false`.

## Errors about multiple symbol definitions when linking

Expand Down
2 changes: 1 addition & 1 deletion api/cpp/docs/mcu/esp_idf.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ extern "C" void app_main(void)
.panel_handle = panel_handle,
.touch_handle = touch_handle,
.buffer1 = buffer,
.color_swap_16 = true });
.byte_swap = true });

/* Instantiate the UI */
auto ui = AppWindow::create();
Expand Down
23 changes: 17 additions & 6 deletions api/cpp/esp-idf/slint/include/slint-esp.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,11 @@
* 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.
*
* The data structure is a template where the pixel type is configurable. The default is for
* RGB565 displays, but you can also use `slint::Rgb8Pixel` if you're targeting an RGB888 display.
*/
template<typename PixelType = slint::platform::Rgb565Pixel>
tronical marked this conversation as resolved.
Show resolved Hide resolved
struct SlintPlatformConfiguration
{
/// The size of the screen in pixels.
Expand All @@ -44,18 +48,24 @@ struct SlintPlatformConfiguration
esp_lcd_touch_handle_t touch_handle = nullptr;
/// 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 = {};
std::optional<std::span<PixelType>> 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 = {};
std::optional<std::span<PixelType>> 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;
/// Swap the 2 bytes of RGB 565 pixels before sending to the display, or turn 24-bit RGB into
/// BGR. Use this if your CPU is little endian but the display expects big-endian.
union {
[[deprecated("Renamed to byte_swap")]] bool color_swap_16;
bool byte_swap = false;
};
};

template<typename... Args>
SlintPlatformConfiguration(Args...) -> SlintPlatformConfiguration<>;

/**
* Initialize the Slint platform for ESP-IDF
*
Expand Down Expand Up @@ -92,4 +102,5 @@ void slint_esp_init(slint::PhysicalSize size, esp_lcd_panel_handle_t panel,
*
* This must be called before any other call to the Slint library.
*/
void slint_esp_init(const SlintPlatformConfiguration &config);
void slint_esp_init(const SlintPlatformConfiguration<slint::platform::Rgb565Pixel> &config);
void slint_esp_init(const SlintPlatformConfiguration<slint::Rgb8Pixel> &config);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should there be documentation for the new function?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean, I can copy it, but it seems pointless for what's an overload. But when we generate docs in the future, we need to make sure that it shows up as one function or two with the same docs.

111 changes: 66 additions & 45 deletions api/cpp/esp-idf/slint/src/slint-esp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,35 @@ static const char *TAG = "slint_platform";

using RepaintBufferType = slint::platform::SoftwareRenderer::RepaintBufferType;

class EspWindowAdapter : public slint::platform::WindowAdapter
{
public:
slint::platform::SoftwareRenderer m_renderer;
bool needs_redraw = true;
const slint::PhysicalSize m_size;

explicit EspWindowAdapter(RepaintBufferType buffer_type, slint::PhysicalSize size)
: m_renderer(buffer_type), m_size(size)
{
}

slint::platform::AbstractRenderer &renderer() override { return m_renderer; }

slint::PhysicalSize size() override { return m_size; }

void request_redraw() override { needs_redraw = true; }
};

template<typename PixelType>
struct EspPlatform : public slint::platform::Platform
{
EspPlatform(const SlintPlatformConfiguration &config)
EspPlatform(const SlintPlatformConfiguration<PixelType> &config)
: size(config.size),
panel_handle(config.panel_handle),
touch_handle(config.touch_handle),
buffer1(config.buffer1),
buffer2(config.buffer2),
color_swap_16(config.color_swap_16),
byte_swap(config.byte_swap),
rotation(config.rotation)
{
#if !defined(SLINT_FEATURE_EXPERIMENTAL)
Expand All @@ -49,9 +69,9 @@ struct EspPlatform : public slint::platform::Platform
slint::PhysicalSize size;
esp_lcd_panel_handle_t panel_handle;
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;
std::optional<std::span<PixelType>> buffer1;
std::optional<std::span<PixelType>> buffer2;
bool byte_swap;
slint::platform::SoftwareRenderer::RenderingRotation rotation;
class EspWindowAdapter *m_window = nullptr;

Expand All @@ -62,26 +82,8 @@ struct EspPlatform : public slint::platform::Platform
bool quit = false; // protected by queue_mutex
};

class EspWindowAdapter : public slint::platform::WindowAdapter
{
public:
slint::platform::SoftwareRenderer m_renderer;
bool needs_redraw = true;
const slint::PhysicalSize m_size;

explicit EspWindowAdapter(RepaintBufferType buffer_type, slint::PhysicalSize size)
: m_renderer(buffer_type), m_size(size)
{
}

slint::platform::AbstractRenderer &renderer() override { return m_renderer; }

slint::PhysicalSize size() override { return m_size; }

void request_redraw() override { needs_redraw = true; }
};

std::unique_ptr<slint::platform::WindowAdapter> EspPlatform::create_window_adapter()
template<typename PixelType>
std::unique_ptr<slint::platform::WindowAdapter> EspPlatform<PixelType>::create_window_adapter()
{
if (m_window != nullptr) {
ESP_LOGI(TAG, "FATAL: create_window_adapter called multiple times");
Expand All @@ -96,7 +98,8 @@ std::unique_ptr<slint::platform::WindowAdapter> EspPlatform::create_window_adapt
return window;
}

std::chrono::milliseconds EspPlatform::duration_since_start()
template<typename PixelType>
std::chrono::milliseconds EspPlatform<PixelType>::duration_since_start()
{
auto ticks = xTaskGetTickCount();
return std::chrono::milliseconds(pdTICKS_TO_MS(ticks));
Expand All @@ -117,7 +120,21 @@ extern "C" bool on_vsync_event(esp_lcd_panel_handle_t panel,
}
#endif

void EspPlatform::run_event_loop()
namespace {
void byte_swap_color(slint::platform::Rgb565Pixel *pixel)
{
// Swap endianness to big endian
auto px = reinterpret_cast<uint16_t *>(pixel);
*px = (*px << 8) | (*px >> 8);
}
void byte_swap_color(slint::Rgb8Pixel *pixel)
{
std::swap(pixel->r, pixel->b);
}
}

template<typename PixelType>
void EspPlatform<PixelType>::run_event_loop()
{
task = xTaskGetCurrentTaskHandle();

Expand Down Expand Up @@ -222,14 +239,11 @@ void EspPlatform::run_event_loop()
auto region = m_window->m_renderer.render(buffer1.value(),
rotated ? size.height : size.width);

if (color_swap_16) {
if (byte_swap) {
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 endianness to big endian
auto px = reinterpret_cast<uint16_t *>(
&buffer1.value()[y * size.width + x]);
*px = (*px << 8) | (*px >> 8);
byte_swap_color(&buffer1.value()[y * size.width + x]);
}
}
}
Expand Down Expand Up @@ -270,12 +284,10 @@ void EspPlatform::run_event_loop()
std::span<slint::platform::Rgb565Pixel> view { lb.get(),
line_end - line_start };
render_fn(view);
if (color_swap_16) {
if (byte_swap) {
// Swap endianness to big endian
std::for_each(view.begin(), view.end(), [](auto &rgbpix) {
auto px = reinterpret_cast<uint16_t *>(&rgbpix);
*px = (*px << 8) | (*px >> 8);
});
std::for_each(view.begin(), view.end(),
[](auto &rgbpix) { byte_swap_color(&rgbpix); });
}
esp_lcd_panel_draw_bitmap(panel_handle, line_start, line_y, line_end,
line_y + 1, lb.get());
Expand All @@ -300,7 +312,8 @@ void EspPlatform::run_event_loop()
vTaskDelete(NULL);
}

void EspPlatform::quit_event_loop()
template<typename PixelType>
void EspPlatform<PixelType>::quit_event_loop()
{
{
const std::unique_lock lock(queue_mutex);
Expand All @@ -309,7 +322,8 @@ void EspPlatform::quit_event_loop()
vTaskNotifyGiveFromISR(task, nullptr);
}

void EspPlatform::run_in_event_loop(slint::platform::Platform::Task event)
template<typename PixelType>
void EspPlatform<PixelType>::run_in_event_loop(slint::platform::Platform::Task event)
{
{
const std::unique_lock lock(queue_mutex);
Expand All @@ -318,7 +332,8 @@ void EspPlatform::run_in_event_loop(slint::platform::Platform::Task event)
vTaskNotifyGiveFromISR(task, nullptr);
}

TaskHandle_t EspPlatform::task = {};
template<typename PixelType>
TaskHandle_t EspPlatform<PixelType>::task = {};

void slint_esp_init(slint::PhysicalSize size, esp_lcd_panel_handle_t panel,
std::optional<esp_lcd_touch_handle_t> touch,
Expand All @@ -333,8 +348,8 @@ void slint_esp_init(slint::PhysicalSize size, esp_lcd_panel_handle_t panel,
.buffer1 = buffer1,
.buffer2 = buffer2,
// 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()
// byte_swap the way it was implemented in Slint (slint-esp) <= 1.6.0:
.byte_swap = buffer2.has_value()
};
slint_esp_init(config);
}
Expand All @@ -350,12 +365,18 @@ void slint_esp_init(slint::PhysicalSize size, esp_lcd_panel_handle_t panel,
.buffer1 = std::nullopt,
.buffer2 = std::nullopt,
.rotation = rotation,
.color_swap_16 = false };
.color_swap = false };
slint_esp_init(config);
}
#endif

void slint_esp_init(const SlintPlatformConfiguration &config)
void slint_esp_init(const SlintPlatformConfiguration<slint::platform::Rgb565Pixel> &config)
{
slint::platform::set_platform(
std::make_unique<EspPlatform<slint::platform::Rgb565Pixel>>(config));
}

void slint_esp_init(const SlintPlatformConfiguration<slint::Rgb8Pixel> &config)
{
slint::platform::set_platform(std::make_unique<EspPlatform>(config));
slint::platform::set_platform(std::make_unique<EspPlatform<slint::Rgb8Pixel>>(config));
}
2 changes: 1 addition & 1 deletion demos/printerdemo_mcu/esp-idf/main/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ extern "C" void app_main(void)
.panel_handle = panel_handle,
.touch_handle = touch_handle,
.buffer1 = buffer,
.color_swap_16 = true });
.byte_swap = true });

auto printer_demo = MainWindow::create();
printer_demo->set_ink_levels(std::make_shared<InkLevelModel>());
Expand Down
2 changes: 1 addition & 1 deletion examples/carousel/esp-idf/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ extern "C" void app_main(void)
.panel_handle = panel_handle,
.touch_handle = touch_handle,
.buffer1 = buffer,
.color_swap_16 = true });
.byte_swap = true });

auto carousel_demo = MainWindow::create();

Expand Down
Loading