diff --git a/doc/releases/release-notes-4.1.rst b/doc/releases/release-notes-4.1.rst index 2e734c7f8197b..40b3dfedfed5a 100644 --- a/doc/releases/release-notes-4.1.rst +++ b/doc/releases/release-notes-4.1.rst @@ -97,6 +97,7 @@ Drivers and Sensors signal handling (:github:`81250`) * Added ``frame_incomplete`` handling to SDL display driver (:dtcompatible:`zephyr,sdl-dc`) (:github:`81250`) + * Added transparency support to SDL display driver (:dtcompatible:`zephyr,sdl-dc`) (:github:`81184`) * Ethernet @@ -292,6 +293,9 @@ LVGL Tests and Samples ***************** +* Fixed incorrect alpha values in :zephyr_file:`samples/drivers/display`. (:github:`81184`) +* Added :zephyr_file:`samples/modules/lvgl/screen_transparency`. (:github:`81184`) + Issue Related Items ******************* diff --git a/drivers/display/Kconfig.sdl b/drivers/display/Kconfig.sdl index bfa1f07c48e3b..d1ed25bf53750 100644 --- a/drivers/display/Kconfig.sdl +++ b/drivers/display/Kconfig.sdl @@ -59,4 +59,22 @@ config SDL_DISPLAY_MONO_MSB_FIRST If selected, set the MSB to represent the first pixel. This applies when the pixel format is MONO01/MONO10. +config SDL_DISPLAY_TRANSPARENCY_GRID_CELL_SIZE + int "Transparency grid cell size" + default 8 + help + The size of the checkerboard pattern squares, in pixels. + +config SDL_DISPLAY_TRANSPARENCY_GRID_CELL_COLOR_1 + hex "Transparency grid cell color 1" + default 0xcccccc + help + The color of the odd cells in the transparency grid. + +config SDL_DISPLAY_TRANSPARENCY_GRID_CELL_COLOR_2 + hex "Transparency grid cell color 2" + default 0xbbbbbb + help + The color of the even cells in the transparency grid. + endif # SDL_DISPLAY diff --git a/drivers/display/display_sdl.c b/drivers/display/display_sdl.c index 2c88e16bd7514..125c5624ef02e 100644 --- a/drivers/display/display_sdl.c +++ b/drivers/display/display_sdl.c @@ -33,6 +33,7 @@ struct sdl_display_data { void *mutex; void *texture; void *read_texture; + void *background_texture; bool display_on; enum display_pixel_format current_pixel_format; uint8_t *buf; @@ -80,7 +81,10 @@ static int sdl_display_init(const struct device *dev) int rc = sdl_display_init_bottom(config->height, config->width, sdl_display_zoom_pct, use_accelerator, &disp_data->window, &disp_data->renderer, &disp_data->mutex, &disp_data->texture, - &disp_data->read_texture); + &disp_data->read_texture, &disp_data->background_texture, + CONFIG_SDL_DISPLAY_TRANSPARENCY_GRID_CELL_COLOR_1, + CONFIG_SDL_DISPLAY_TRANSPARENCY_GRID_CELL_COLOR_2, + CONFIG_SDL_DISPLAY_TRANSPARENCY_GRID_CELL_SIZE); if (rc != 0) { LOG_ERR("Failed to create SDL display"); @@ -119,7 +123,7 @@ static void sdl_display_write_rgb888(uint8_t *disp_buf, pixel = *byte_ptr << 16; pixel |= *(byte_ptr + 1) << 8; pixel |= *(byte_ptr + 2); - *((uint32_t *)disp_buf) = pixel; + *((uint32_t *)disp_buf) = pixel | 0xFF000000; disp_buf += 4; } } @@ -145,7 +149,7 @@ static void sdl_display_write_rgb565(uint8_t *disp_buf, pixel = (((rgb565 >> 11) & 0x1F) * 255 / 31) << 16; pixel |= (((rgb565 >> 5) & 0x3F) * 255 / 63) << 8; pixel |= (rgb565 & 0x1F) * 255 / 31; - *((uint32_t *)disp_buf) = pixel; + *((uint32_t *)disp_buf) = pixel | 0xFF000000; disp_buf += 4; } } @@ -169,7 +173,7 @@ static void sdl_display_write_bgr565(uint8_t *disp_buf, pixel = (((*pix_ptr >> 11) & 0x1F) * 255 / 31) << 16; pixel |= (((*pix_ptr >> 5) & 0x3F) * 255 / 63) << 8; pixel |= (*pix_ptr & 0x1F) * 255 / 31; - *((uint32_t *)disp_buf) = pixel; + *((uint32_t *)disp_buf) = pixel | 0xFF000000; disp_buf += 4; } } @@ -207,9 +211,9 @@ static void sdl_display_write_mono(uint8_t *disp_buf, if ((*byte_ptr & mono_pixel_order(h_idx)) != 0U) { pixel = one_color; } else { - pixel = (~one_color) & 0x00FFFFFF; + pixel = ~one_color; } - *((uint32_t *)disp_buf) = pixel; + *((uint32_t *)disp_buf) = pixel | 0xFF000000; disp_buf += (desc->width * 4U); } disp_buf = disp_buf_start; @@ -261,7 +265,8 @@ static int sdl_display_write(const struct device *dev, const uint16_t x, } sdl_display_write_bottom(desc->height, desc->width, x, y, disp_data->renderer, - disp_data->mutex, disp_data->texture, disp_data->buf, + disp_data->mutex, disp_data->texture, + disp_data->background_texture, disp_data->buf, disp_data->display_on, desc->frame_incomplete); return 0; @@ -431,7 +436,8 @@ static int sdl_display_blanking_off(const struct device *dev) disp_data->display_on = true; - sdl_display_blanking_off_bottom(disp_data->renderer, disp_data->texture); + sdl_display_blanking_off_bottom(disp_data->renderer, disp_data->texture, + disp_data->background_texture); return 0; } @@ -491,7 +497,8 @@ static int sdl_display_set_pixel_format(const struct device *dev, static void sdl_display_cleanup(struct sdl_display_data *disp_data) { sdl_display_cleanup_bottom(&disp_data->window, &disp_data->renderer, &disp_data->mutex, - &disp_data->texture, &disp_data->read_texture); + &disp_data->texture, &disp_data->read_texture, + &disp_data->background_texture); } static const struct display_driver_api sdl_display_api = { diff --git a/drivers/display/display_sdl_bottom.c b/drivers/display/display_sdl_bottom.c index e0fbd4a5fbb5f..e7b259cda095b 100644 --- a/drivers/display/display_sdl_bottom.c +++ b/drivers/display/display_sdl_bottom.c @@ -15,7 +15,9 @@ int sdl_display_init_bottom(uint16_t height, uint16_t width, uint16_t zoom_pct, bool use_accelerator, void **window, void **renderer, void **mutex, - void **texture, void **read_texture) + void **texture, void **read_texture, void **background_texture, + uint32_t transparency_grid_color1, uint32_t transparency_grid_color2, + uint16_t transparency_grid_cell_size) { *window = SDL_CreateWindow("Zephyr Display", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width * zoom_pct / 100, @@ -51,6 +53,7 @@ int sdl_display_init_bottom(uint16_t height, uint16_t width, uint16_t zoom_pct, nsi_print_warning("Failed to create SDL texture: %s", SDL_GetError()); return -1; } + SDL_SetTextureBlendMode(*texture, SDL_BLENDMODE_BLEND); *read_texture = SDL_CreateTexture(*renderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_TARGET, width, height); @@ -59,8 +62,41 @@ int sdl_display_init_bottom(uint16_t height, uint16_t width, uint16_t zoom_pct, return -1; } + *background_texture = SDL_CreateTexture(*renderer, SDL_PIXELFORMAT_ARGB8888, + SDL_TEXTUREACCESS_STREAMING, width, height); + if (*background_texture == NULL) { + nsi_print_warning("Failed to create SDL texture: %s", SDL_GetError()); + return -1; + } + + void *background_data; + int background_pitch; + int err; + + err = SDL_LockTexture(*background_texture, NULL, &background_data, &background_pitch); + if (err != 0) { + nsi_print_warning("Failed to lock background texture: %d", err); + return -1; + } + for (int y = 0; y < height; y++) { + uint32_t *row = (uint32_t *)((uint8_t *)background_data + background_pitch * y); + + for (int x = 0; x < width; x++) { + bool x_cell_even = ((x / transparency_grid_cell_size) % 2) == 0; + bool y_cell_even = ((y / transparency_grid_cell_size) % 2) == 0; + + if (x_cell_even == y_cell_even) { + row[x] = transparency_grid_color1 | 0xff000000; + } else { + row[x] = transparency_grid_color2 | 0xff000000; + } + } + } + SDL_UnlockTexture(*background_texture); + SDL_SetRenderDrawColor(*renderer, 0, 0, 0, 0xFF); SDL_RenderClear(*renderer); + SDL_RenderCopy(*renderer, *background_texture, NULL, NULL); SDL_RenderPresent(*renderer); return 0; @@ -68,7 +104,8 @@ int sdl_display_init_bottom(uint16_t height, uint16_t width, uint16_t zoom_pct, void sdl_display_write_bottom(const uint16_t height, const uint16_t width, const uint16_t x, const uint16_t y, void *renderer, void *mutex, void *texture, - uint8_t *buf, bool display_on, bool frame_incomplete) + void *background_texture, uint8_t *buf, bool display_on, + bool frame_incomplete) { SDL_Rect rect; int err; @@ -88,6 +125,7 @@ void sdl_display_write_bottom(const uint16_t height, const uint16_t width, const if (display_on && !frame_incomplete) { SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, background_texture, NULL, NULL); SDL_RenderCopy(renderer, texture, NULL, NULL); SDL_RenderPresent(renderer); } @@ -127,9 +165,10 @@ int sdl_display_read_bottom(const uint16_t height, const uint16_t width, return err; } -void sdl_display_blanking_off_bottom(void *renderer, void *texture) +void sdl_display_blanking_off_bottom(void *renderer, void *texture, void *background_texture) { SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, background_texture, NULL, NULL); SDL_RenderCopy(renderer, texture, NULL, NULL); SDL_RenderPresent(renderer); } @@ -141,8 +180,13 @@ void sdl_display_blanking_on_bottom(void *renderer) } void sdl_display_cleanup_bottom(void **window, void **renderer, void **mutex, void **texture, - void **read_texture) + void **read_texture, void **background_texture) { + if (*background_texture != NULL) { + SDL_DestroyTexture(*background_texture); + *background_texture = NULL; + } + if (*read_texture != NULL) { SDL_DestroyTexture(*read_texture); *read_texture = NULL; diff --git a/drivers/display/display_sdl_bottom.h b/drivers/display/display_sdl_bottom.h index 721cfa2b5a81e..16a6a2ce603f3 100644 --- a/drivers/display/display_sdl_bottom.h +++ b/drivers/display/display_sdl_bottom.h @@ -22,17 +22,20 @@ extern "C" { int sdl_display_init_bottom(uint16_t height, uint16_t width, uint16_t zoom_pct, bool use_accelerator, void **window, void **renderer, void **mutex, - void **texture, void **read_texture); + void **texture, void **read_texture, void **background_texture, + uint32_t transparency_grid_color1, uint32_t transparency_grid_color2, + uint16_t transparency_grid_cell_size); void sdl_display_write_bottom(const uint16_t height, const uint16_t width, const uint16_t x, const uint16_t y, void *renderer, void *mutex, void *texture, - uint8_t *buf, bool display_on, bool frame_incomplete); + void *background_texture, uint8_t *buf, bool display_on, + bool frame_incomplete); int sdl_display_read_bottom(const uint16_t height, const uint16_t width, const uint16_t x, const uint16_t y, void *renderer, void *buf, uint16_t pitch, void *mutex, void *texture, void *read_texture); -void sdl_display_blanking_off_bottom(void *renderer, void *texture); +void sdl_display_blanking_off_bottom(void *renderer, void *texture, void *background_texture); void sdl_display_blanking_on_bottom(void *renderer); void sdl_display_cleanup_bottom(void **window, void **renderer, void **mutex, void **texture, - void **read_texture); + void **read_texture, void **background_texture); #ifdef __cplusplus } diff --git a/samples/drivers/display/src/main.c b/samples/drivers/display/src/main.c index 8cbfb64040ac1..09c8f86acd49a 100644 --- a/samples/drivers/display/src/main.c +++ b/samples/drivers/display/src/main.c @@ -50,16 +50,16 @@ static void fill_buffer_argb8888(enum corner corner, uint8_t grey, uint8_t *buf, switch (corner) { case TOP_LEFT: - color = 0x00FF0000u; + color = 0xFFFF0000u; break; case TOP_RIGHT: - color = 0x0000FF00u; + color = 0xFF00FF00u; break; case BOTTOM_RIGHT: - color = 0x000000FFu; + color = 0xFF0000FFu; break; case BOTTOM_LEFT: - color = grey << 16 | grey << 8 | grey; + color = 0xFF000000u | grey << 16 | grey << 8 | grey; break; } @@ -239,7 +239,7 @@ int main(void) switch (capabilities.current_pixel_format) { case PIXEL_FORMAT_ARGB_8888: - bg_color = 0xFFu; + bg_color = 0x00u; fill_buffer_fnc = fill_buffer_argb8888; buf_size *= 4; break; diff --git a/samples/modules/lvgl/accelerometer_chart/src/main.c b/samples/modules/lvgl/accelerometer_chart/src/main.c index 2aaee9fad0d43..937f4690e79fe 100644 --- a/samples/modules/lvgl/accelerometer_chart/src/main.c +++ b/samples/modules/lvgl/accelerometer_chart/src/main.c @@ -89,7 +89,9 @@ int main(void) display_blanking_off(display_dev); while (1) { - k_msleep(lv_task_handler()); + uint32_t sleep_ms = lv_task_handler(); + + k_msleep(MIN(sleep_ms, INT32_MAX)); } return 0; diff --git a/samples/modules/lvgl/demos/src/main.c b/samples/modules/lvgl/demos/src/main.c index 2e3785d82984a..8aa2ffa3aed85 100644 --- a/samples/modules/lvgl/demos/src/main.c +++ b/samples/modules/lvgl/demos/src/main.c @@ -40,7 +40,9 @@ int main(void) display_blanking_off(display_dev); while (1) { - k_msleep(lv_task_handler()); + uint32_t sleep_ms = lv_task_handler(); + + k_msleep(MIN(sleep_ms, INT32_MAX)); } return 0; diff --git a/samples/modules/lvgl/screen_transparency/CMakeLists.txt b/samples/modules/lvgl/screen_transparency/CMakeLists.txt new file mode 100644 index 0000000000000..eb9fd20c1c157 --- /dev/null +++ b/samples/modules/lvgl/screen_transparency/CMakeLists.txt @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: Apache-2.0 + +cmake_minimum_required(VERSION 3.20.0) + +find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE}) +project(screen_transparency) + +FILE(GLOB app_sources src/*.c) +target_sources(app PRIVATE ${app_sources}) diff --git a/samples/modules/lvgl/screen_transparency/Kconfig b/samples/modules/lvgl/screen_transparency/Kconfig new file mode 100644 index 0000000000000..0229ee90c8965 --- /dev/null +++ b/samples/modules/lvgl/screen_transparency/Kconfig @@ -0,0 +1,4 @@ +# Copyright (c) 2024 Martin Stumpf +# SPDX-License-Identifier: Apache-2.0 + +source "Kconfig.zephyr" diff --git a/samples/modules/lvgl/screen_transparency/README.rst b/samples/modules/lvgl/screen_transparency/README.rst new file mode 100644 index 0000000000000..fdfd80478d23c --- /dev/null +++ b/samples/modules/lvgl/screen_transparency/README.rst @@ -0,0 +1,30 @@ +.. zephyr:code-sample:: lvgl-screen-transparency + :name: LVGL screen transparency + :relevant-api: display_interface + + Rendering to screens with transparency support using LVGL. + +Overview +******** + +A sample application that demonstrates how to use LVGL to render to +screens that support transparency, like OSD overlays. + +Requirements +************ + +* A board with a display that supports ``ARGB8888`` color. + +.. _lvgl_screen_transparency_building_and_running: + +Building and Running +******************** + +The demo can be built as follows: + +.. zephyr-app-commands:: + :zephyr-app: samples/modules/lvgl/screen_transparency + :host-os: unix + :board: native_sim + :goals: run + :compact: diff --git a/samples/modules/lvgl/screen_transparency/prj.conf b/samples/modules/lvgl/screen_transparency/prj.conf new file mode 100644 index 0000000000000..7c857deba03f5 --- /dev/null +++ b/samples/modules/lvgl/screen_transparency/prj.conf @@ -0,0 +1,9 @@ +CONFIG_MAIN_STACK_SIZE=4096 +CONFIG_LOG=y + +CONFIG_LVGL=y +CONFIG_LV_Z_MEM_POOL_SIZE=16384 +CONFIG_LV_COLOR_DEPTH_32=y +CONFIG_LV_COLOR_SCREEN_TRANSP=y + +CONFIG_DISPLAY=y diff --git a/samples/modules/lvgl/screen_transparency/sample.yaml b/samples/modules/lvgl/screen_transparency/sample.yaml new file mode 100644 index 0000000000000..9f4f571868a77 --- /dev/null +++ b/samples/modules/lvgl/screen_transparency/sample.yaml @@ -0,0 +1,18 @@ +sample: + description: Demonstrating Screen Transparency using LVGL + name: LVGL screen transparency +tests: + sample.modules.lvgl.screen_transparency: + filter: dt_chosen_enabled("zephyr,display") + min_flash: 250 + min_ram: 32 + harness: none + tags: + - samples + - display + - gui + - lvgl + modules: + - lvgl + integration_platforms: + - native_sim/native/64 diff --git a/samples/modules/lvgl/screen_transparency/src/main.c b/samples/modules/lvgl/screen_transparency/src/main.c new file mode 100644 index 0000000000000..64494ffdaed18 --- /dev/null +++ b/samples/modules/lvgl/screen_transparency/src/main.c @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2024 Martin Stumpf + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include +#include + +#include +#include +#include +#include + +#include +LOG_MODULE_REGISTER(app, CONFIG_LOG_DEFAULT_LEVEL); + +static void initialize_gui(void) +{ + lv_obj_t *label; + + /* Configure screen and background for transparency */ + lv_obj_set_style_bg_opa(lv_scr_act(), LV_OPA_TRANSP, LV_PART_MAIN); + lv_disp_set_bg_opa(NULL, LV_OPA_TRANSP); + lv_obj_set_style_bg_color(lv_scr_act(), lv_color_hex(0x000000), LV_PART_MAIN); + + /* Create a label, set its text and align it to the center */ + label = lv_label_create(lv_scr_act()); + lv_label_set_text(label, "Hello, world!"); + lv_obj_set_style_text_color(lv_scr_act(), lv_color_hex(0xff00ff), LV_PART_MAIN); + lv_obj_align(label, LV_ALIGN_CENTER, 0, 0); +} + +int main(void) +{ + int err; + const struct device *display_dev; + struct display_capabilities display_caps; + + display_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_display)); + if (!device_is_ready(display_dev)) { + LOG_ERR("Device not ready, aborting test"); + return -ENODEV; + } + + display_get_capabilities(display_dev, &display_caps); + if (!(display_caps.supported_pixel_formats | PIXEL_FORMAT_ARGB_8888)) { + LOG_ERR("Display does not support ARGB8888 mode"); + return -ENOTSUP; + } + + if (PIXEL_FORMAT_ARGB_8888 != display_caps.current_pixel_format) { + err = display_set_pixel_format(display_dev, PIXEL_FORMAT_ARGB_8888); + if (err != 0) { + LOG_ERR("Failed to set ARGB8888 pixel format"); + return err; + } + } + + initialize_gui(); + + lv_task_handler(); + display_blanking_off(display_dev); + + while (1) { + uint32_t sleep_ms = lv_task_handler(); + + k_msleep(MIN(sleep_ms, INT32_MAX)); + } + + return 0; +}