diff --git a/api/cpp/docs/mcu/esp-idf/troubleshoot.md b/api/cpp/docs/mcu/esp-idf/troubleshoot.md index fe1da62041d..8410e8543c8 100644 --- a/api/cpp/docs/mcu/esp-idf/troubleshoot.md +++ b/api/cpp/docs/mcu/esp-idf/troubleshoot.md @@ -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 diff --git a/api/cpp/docs/mcu/esp_idf.md b/api/cpp/docs/mcu/esp_idf.md index 8b534a6e150..4ab8f7c52a4 100644 --- a/api/cpp/docs/mcu/esp_idf.md +++ b/api/cpp/docs/mcu/esp_idf.md @@ -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(); diff --git a/api/cpp/esp-idf/slint/include/slint-esp.h b/api/cpp/esp-idf/slint/include/slint-esp.h index e49b60d2709..837d66d86ea 100644 --- a/api/cpp/esp-idf/slint/include/slint-esp.h +++ b/api/cpp/esp-idf/slint/include/slint-esp.h @@ -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 struct SlintPlatformConfiguration { /// The size of the screen in pixels. @@ -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> buffer1 = {}; + std::optional> 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> buffer2 = {}; + std::optional> 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 +SlintPlatformConfiguration(Args...) -> SlintPlatformConfiguration<>; + /** * Initialize the Slint platform for ESP-IDF * @@ -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 &config); +void slint_esp_init(const SlintPlatformConfiguration &config); diff --git a/api/cpp/esp-idf/slint/src/slint-esp.cpp b/api/cpp/esp-idf/slint/src/slint-esp.cpp index 42cef519302..8386da03a71 100644 --- a/api/cpp/esp-idf/slint/src/slint-esp.cpp +++ b/api/cpp/esp-idf/slint/src/slint-esp.cpp @@ -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 struct EspPlatform : public slint::platform::Platform { - EspPlatform(const SlintPlatformConfiguration &config) + EspPlatform(const SlintPlatformConfiguration &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) @@ -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> buffer1; - std::optional> buffer2; - bool color_swap_16; + std::optional> buffer1; + std::optional> buffer2; + bool byte_swap; slint::platform::SoftwareRenderer::RenderingRotation rotation; class EspWindowAdapter *m_window = nullptr; @@ -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 EspPlatform::create_window_adapter() +template +std::unique_ptr EspPlatform::create_window_adapter() { if (m_window != nullptr) { ESP_LOGI(TAG, "FATAL: create_window_adapter called multiple times"); @@ -96,7 +98,8 @@ std::unique_ptr EspPlatform::create_window_adapt return window; } -std::chrono::milliseconds EspPlatform::duration_since_start() +template +std::chrono::milliseconds EspPlatform::duration_since_start() { auto ticks = xTaskGetTickCount(); return std::chrono::milliseconds(pdTICKS_TO_MS(ticks)); @@ -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(pixel); + *px = (*px << 8) | (*px >> 8); +} +void byte_swap_color(slint::Rgb8Pixel *pixel) +{ + std::swap(pixel->r, pixel->b); +} +} + +template +void EspPlatform::run_event_loop() { task = xTaskGetCurrentTaskHandle(); @@ -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( - &buffer1.value()[y * size.width + x]); - *px = (*px << 8) | (*px >> 8); + byte_swap_color(&buffer1.value()[y * size.width + x]); } } } @@ -270,12 +284,10 @@ void EspPlatform::run_event_loop() std::span 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(&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()); @@ -300,7 +312,8 @@ void EspPlatform::run_event_loop() vTaskDelete(NULL); } -void EspPlatform::quit_event_loop() +template +void EspPlatform::quit_event_loop() { { const std::unique_lock lock(queue_mutex); @@ -309,7 +322,8 @@ void EspPlatform::quit_event_loop() vTaskNotifyGiveFromISR(task, nullptr); } -void EspPlatform::run_in_event_loop(slint::platform::Platform::Task event) +template +void EspPlatform::run_in_event_loop(slint::platform::Platform::Task event) { { const std::unique_lock lock(queue_mutex); @@ -318,7 +332,8 @@ void EspPlatform::run_in_event_loop(slint::platform::Platform::Task event) vTaskNotifyGiveFromISR(task, nullptr); } -TaskHandle_t EspPlatform::task = {}; +template +TaskHandle_t EspPlatform::task = {}; void slint_esp_init(slint::PhysicalSize size, esp_lcd_panel_handle_t panel, std::optional touch, @@ -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); } @@ -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 &config) +{ + slint::platform::set_platform( + std::make_unique>(config)); +} + +void slint_esp_init(const SlintPlatformConfiguration &config) { - slint::platform::set_platform(std::make_unique(config)); + slint::platform::set_platform(std::make_unique>(config)); } diff --git a/demos/printerdemo_mcu/esp-idf/main/main.cpp b/demos/printerdemo_mcu/esp-idf/main/main.cpp index f5223faa92f..515f25fdc86 100644 --- a/demos/printerdemo_mcu/esp-idf/main/main.cpp +++ b/demos/printerdemo_mcu/esp-idf/main/main.cpp @@ -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()); diff --git a/examples/carousel/esp-idf/main.cpp b/examples/carousel/esp-idf/main.cpp index d03382a4a16..903d7207857 100644 --- a/examples/carousel/esp-idf/main.cpp +++ b/examples/carousel/esp-idf/main.cpp @@ -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();