mirror of
https://github.com/ByteWelder/Tactility.git
synced 2026-04-25 04:25:05 +00:00
Update LVGL (master 9.2+) and related libraries (#130)
- LVGL 9.2+ (master commit) - esp_lvgl_port (master with my PR hotfix changes) - espressif/esp_lcd_touch_gt911 1.1.1~2
This commit is contained in:
parent
e4b8519f67
commit
80b69b7f45
@ -1,6 +1,6 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
espressif/esp_lcd_ili9341: "2.0.0"
|
espressif/esp_lcd_ili9341: "2.0.0"
|
||||||
espressif/esp_lcd_touch_cst816s: "1.0.3"
|
espressif/esp_lcd_touch_cst816s: "1.0.3"
|
||||||
espressif/esp_lcd_touch_gt911: "1.1.1"
|
espressif/esp_lcd_touch_gt911: "1.1.1~2"
|
||||||
espressif/esp_lcd_touch: "1.1.2"
|
espressif/esp_lcd_touch: "1.1.2"
|
||||||
idf: '5.3.1'
|
idf: '5.3.1'
|
||||||
|
|||||||
@ -12,7 +12,6 @@
|
|||||||
#define TDECK_TOUCH_I2C_BUS_HANDLE I2C_NUM_0
|
#define TDECK_TOUCH_I2C_BUS_HANDLE I2C_NUM_0
|
||||||
#define TDECK_TOUCH_X_MAX 240
|
#define TDECK_TOUCH_X_MAX 240
|
||||||
#define TDECK_TOUCH_Y_MAX 320
|
#define TDECK_TOUCH_Y_MAX 320
|
||||||
#define TDECK_TOUCH_PIN_INT GPIO_NUM_16
|
|
||||||
|
|
||||||
bool TdeckTouch::start(lv_display_t* display) {
|
bool TdeckTouch::start(lv_display_t* display) {
|
||||||
const esp_lcd_panel_io_i2c_config_t io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG();
|
const esp_lcd_panel_io_i2c_config_t io_config = ESP_LCD_TOUCH_IO_I2C_GT911_CONFIG();
|
||||||
@ -41,7 +40,7 @@ bool TdeckTouch::start(lv_display_t* display) {
|
|||||||
.x_max = TDECK_TOUCH_X_MAX,
|
.x_max = TDECK_TOUCH_X_MAX,
|
||||||
.y_max = TDECK_TOUCH_Y_MAX,
|
.y_max = TDECK_TOUCH_Y_MAX,
|
||||||
.rst_gpio_num = GPIO_NUM_NC,
|
.rst_gpio_num = GPIO_NUM_NC,
|
||||||
.int_gpio_num = TDECK_TOUCH_PIN_INT,
|
.int_gpio_num = GPIO_NUM_NC, // There is no reset pin for touch on the T-Deck, leading to a bug in esp_lvgl_port firing multiple click events when tapping the screen
|
||||||
.levels = {
|
.levels = {
|
||||||
.reset = 0,
|
.reset = 0,
|
||||||
.interrupt = 0,
|
.interrupt = 0,
|
||||||
|
|||||||
@ -29,8 +29,8 @@ bool YellowTouch::start(lv_display_t* display) {
|
|||||||
esp_lcd_touch_config_t config = {
|
esp_lcd_touch_config_t config = {
|
||||||
.x_max = 240,
|
.x_max = 240,
|
||||||
.y_max = 320,
|
.y_max = 320,
|
||||||
.rst_gpio_num = GPIO_NUM_25,
|
.rst_gpio_num = GPIO_NUM_NC, //GPIO_NUM_25,
|
||||||
.int_gpio_num = GPIO_NUM_21,
|
.int_gpio_num = GPIO_NUM_NC, //GPIO_NUM_21,
|
||||||
.levels = {
|
.levels = {
|
||||||
.reset = 0,
|
.reset = 0,
|
||||||
.interrupt = 0,
|
.interrupt = 0,
|
||||||
|
|||||||
@ -56,7 +56,8 @@ if (DEFINED ENV{ESP_IDF_VERSION})
|
|||||||
get_filename_component(
|
get_filename_component(
|
||||||
LVGL_CONFIG_FULL_PATH Libraries/lvgl_conf ABSOLUTE
|
LVGL_CONFIG_FULL_PATH Libraries/lvgl_conf ABSOLUTE
|
||||||
)
|
)
|
||||||
add_compile_definitions(LV_CONF_PATH=${LVGL_CONFIG_FULL_PATH}/lv_conf_kconfig.h)
|
|
||||||
|
add_compile_definitions(LV_CONF_PATH="${LVGL_CONFIG_FULL_PATH}/lv_conf_kconfig.h")
|
||||||
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=esp_panic_handler" APPEND)
|
idf_build_set_property(LINK_OPTIONS "-Wl,--wrap=esp_panic_handler" APPEND)
|
||||||
|
|
||||||
file(READ version.txt TACTILITY_VERSION)
|
file(READ version.txt TACTILITY_VERSION)
|
||||||
@ -108,7 +109,7 @@ if (NOT DEFINED ENV{ESP_IDF_VERSION})
|
|||||||
if (NOT DEFINED ENV{SKIP_SDL})
|
if (NOT DEFINED ENV{SKIP_SDL})
|
||||||
target_include_directories(lvgl PUBLIC ${SDL2_IMAGE_INCLUDE_DIRS})
|
target_include_directories(lvgl PUBLIC ${SDL2_IMAGE_INCLUDE_DIRS})
|
||||||
endif()
|
endif()
|
||||||
target_compile_definitions(lvgl PUBLIC "-DLV_CONF_PATH=${PROJECT_SOURCE_DIR}/Libraries/lvgl_conf/lv_conf_simulator.h")
|
target_compile_definitions(lvgl PUBLIC "-DLV_CONF_PATH=\"${PROJECT_SOURCE_DIR}/Libraries/lvgl_conf/lv_conf_simulator.h\"")
|
||||||
|
|
||||||
# SDL
|
# SDL
|
||||||
# TODO: This is a temporary skipping option for running unit tests
|
# TODO: This is a temporary skipping option for running unit tests
|
||||||
|
|||||||
@ -1,11 +1,16 @@
|
|||||||
# Bugs
|
# Bugs
|
||||||
- I2C Scanner is on M5Stack devices is broken
|
- I2C Scanner is on M5Stack devices is broken
|
||||||
- Fix screenshot app on ESP32: it currently blocks when allocating memory (its cmakelists.txt also needs a fix, see TODO in there)
|
- Fix screenshot app on ESP32: it currently blocks when allocating memory (its cmakelists.txt also needs a fix, see TODO in there)
|
||||||
- In LVGL9 with M5Core2, crash when bottom item is clicked without scrolling first
|
|
||||||
- Commit fix to esp_lvgl_port to have `esp_lvgl_port_disp.c` user driver_data instead of user_data
|
- Commit fix to esp_lvgl_port to have `esp_lvgl_port_disp.c` user driver_data instead of user_data
|
||||||
- WiFi bug: when pressing disconnect while between `WIFI_EVENT_STA_START` and `IP_EVENT_STA_GOT_IP`, then auto-connect becomes activate again.
|
- WiFi bug: when pressing disconnect while between `WIFI_EVENT_STA_START` and `IP_EVENT_STA_GOT_IP`, then auto-connect becomes activate again.
|
||||||
|
- elf_loader, lvgl and TactilityC in TactilitySDK should include their license files
|
||||||
|
- ESP32 (CYD) runs out of memory when:
|
||||||
|
- loading splash (even in Files app)
|
||||||
|
- WiFi is on and navigating back to Desktop
|
||||||
|
Suggested mitigation: When no PSRAM is availabl, use simplified desktop buttons
|
||||||
|
|
||||||
# TODOs
|
# TODOs
|
||||||
|
- Create different partitions files for different ESP flash size targets (N4, N8, N16, N32)
|
||||||
- Rewrite `sdcard` HAL to class
|
- Rewrite `sdcard` HAL to class
|
||||||
- Attach ELF data to wrapper app (as app data) (check that app state is "running"!) so you can run more than 1 external apps at a time.
|
- Attach ELF data to wrapper app (as app data) (check that app state is "running"!) so you can run more than 1 external apps at a time.
|
||||||
We'll need to keep track of all manifest instances, so that the wrapper can look up the relevant manifest for the relevant callbacks.
|
We'll need to keep track of all manifest instances, so that the wrapper can look up the relevant manifest for the relevant callbacks.
|
||||||
@ -23,7 +28,6 @@
|
|||||||
- Show a warning screen when a user plugs in the SD card on a device that only supports mounting at boot.
|
- Show a warning screen when a user plugs in the SD card on a device that only supports mounting at boot.
|
||||||
- Localisation of texts (load in boot app from sd?)
|
- Localisation of texts (load in boot app from sd?)
|
||||||
- Explore LVGL9's FreeRTOS functionality
|
- Explore LVGL9's FreeRTOS functionality
|
||||||
- Explore LVGL9's ILI93414 driver for 2.4" Yellow Board
|
|
||||||
- Replace M5Unified and M5GFX with custom drivers (so we can fix the Core2 SD card mounting bug, and so we regain some firmware space)
|
- Replace M5Unified and M5GFX with custom drivers (so we can fix the Core2 SD card mounting bug, and so we regain some firmware space)
|
||||||
- External app loading: Check version of Tactility and check ESP target hardware, to check for compatibility.
|
- External app loading: Check version of Tactility and check ESP target hardware, to check for compatibility.
|
||||||
- Scanning SD card for external apps and auto-register them (in a temporary register?)
|
- Scanning SD card for external apps and auto-register them (in a temporary register?)
|
||||||
|
|||||||
@ -1,11 +1,96 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## 1.5.0
|
## 2.4.3
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
- Fixed a display context pointer bug
|
||||||
|
- Fixed I2C example for using with LVGL9
|
||||||
|
|
||||||
|
### Features
|
||||||
|
- Support for LV_COLOR_FORMAT_I1 for monochromatic screen
|
||||||
|
|
||||||
|
## 2.4.2
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
- Fixed SW rotation in LVGL9.2
|
||||||
|
- Fixed freeing right buffers when error
|
||||||
|
|
||||||
|
## 2.4.1
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
- Fixed the issue of the DPI callback function not being initialized.
|
||||||
|
|
||||||
|
## 2.4.0
|
||||||
|
|
||||||
|
### Features
|
||||||
|
- Added support for direct mode and full refresh mode in the MIPI-DSI interface.
|
||||||
|
- Optimized avoid-tear feature and LVGL task.
|
||||||
|
|
||||||
|
## 2.3.3
|
||||||
|
|
||||||
|
### Features
|
||||||
|
- Updated RGB screen flush handling in LVGL9.
|
||||||
|
|
||||||
|
## 2.3.2
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
- Fixed rotation type compatibility with LVGL8.
|
||||||
|
|
||||||
|
## 2.3.1
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
- Fixed LVGL version resolution if LVGL is not a managed component
|
||||||
|
- Fixed link error with LVGL v9.2
|
||||||
|
- Fixed event error with LVGL v9.2
|
||||||
|
|
||||||
|
## 2.3.0
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
- Fixed LVGL port for using with LVGL9 OS FreeRTOS enabled
|
||||||
|
- Fixed bad handled touch due to synchronization timer task
|
||||||
|
|
||||||
|
### Features
|
||||||
|
- Added support for SW rotation in LVGL9
|
||||||
|
|
||||||
|
## 2.2.2
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
- Fixed missing callback in IDF4.4.3 and lower for LVGL port
|
||||||
|
|
||||||
|
## 2.2.1
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
- Added missing includes
|
||||||
|
- Fixed watchdog error in some cases in LVGL9
|
||||||
|
|
||||||
|
## 2.2.0
|
||||||
|
|
||||||
|
### Features
|
||||||
|
- Added RGB display support
|
||||||
|
- Added support for direct mode and full refresh mode
|
||||||
|
|
||||||
|
### Breaking changes
|
||||||
|
- Removed MIPI-DSI from display configuration structure - use `lvgl_port_add_disp_dsi` instead
|
||||||
|
|
||||||
|
## 2.1.0
|
||||||
|
|
||||||
|
### Features
|
||||||
|
- Added LVGL sleep feature: The esp_lvgl_port handling can sleep if the display and touch are inactive (only with LVGL9)
|
||||||
|
- Added support for different display color modes (only with LVGL9)
|
||||||
|
- Added script for generating C array images during build (depends on LVGL version)
|
||||||
|
|
||||||
|
### Fixes
|
||||||
|
- Applied initial display rotation from configuration https://github.com/espressif/esp-bsp/pull/278
|
||||||
|
- Added blocking wait for LVGL task stop during esp_lvgl_port de-initialization https://github.com/espressif/esp-bsp/issues/277
|
||||||
|
- Added missing esp_idf_version.h include
|
||||||
|
|
||||||
|
## 2.0.0
|
||||||
|
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
- Divided into files per feature
|
- Divided into files per feature
|
||||||
- Added support for LVGL9
|
- Added support for LVGL9
|
||||||
|
- Added support for MIPI-DSI display
|
||||||
|
|
||||||
## 1.4.0
|
## 1.4.0
|
||||||
|
|
||||||
|
|||||||
@ -1,13 +1,34 @@
|
|||||||
|
include($ENV{IDF_PATH}/tools/cmake/version.cmake) # $ENV{IDF_VERSION} was added after v4.3...
|
||||||
|
|
||||||
#Get LVGL version
|
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_LESS "4.4")
|
||||||
#idf_component_get_property(lvgl_ver lvgl__lvgl COMPONENT_VERSION)
|
return()
|
||||||
#if(lvgl_ver EQUAL "")
|
endif()
|
||||||
# idf_component_get_property(lvgl_ver lvgl COMPONENT_VERSION)
|
|
||||||
#endif()
|
# This component uses a CMake workaround, so we can compile esp_lvgl_port for both LVGL8.x and LVGL9.x
|
||||||
set(lvgl_ver "9.0.0")
|
# At the time of idf_component_register() we don't know which LVGL version is used, so we only register an INTERFACE component (with no sources)
|
||||||
|
# Later, when we know the LVGL version, we create another CMake library called 'lvgl_port_lib' and link it to the 'esp_lvgl_port' INTERFACE component
|
||||||
|
idf_component_register(
|
||||||
|
INCLUDE_DIRS "include"
|
||||||
|
PRIV_INCLUDE_DIRS "priv_include"
|
||||||
|
REQUIRES "esp_lcd")
|
||||||
|
|
||||||
|
# Get LVGL version
|
||||||
|
idf_build_get_property(build_components BUILD_COMPONENTS)
|
||||||
|
if(lvgl IN_LIST build_components)
|
||||||
|
set(lvgl_name lvgl) # Local component
|
||||||
|
set(lvgl_ver $ENV{LVGL_VERSION}) # Get the version from env variable (set from LVGL v9.2)
|
||||||
|
else()
|
||||||
|
set(lvgl_name lvgl__lvgl) # Managed component
|
||||||
|
idf_component_get_property(lvgl_ver ${lvgl_name} COMPONENT_VERSION) # Get the version from esp-idf build system
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if("${lvgl_ver}" STREQUAL "")
|
||||||
|
message("Could not determine LVGL version, assuming v9.x")
|
||||||
|
set(lvgl_ver "9.0.0")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Select folder by LVGL version
|
||||||
message(STATUS "LVGL version: ${lvgl_ver}")
|
message(STATUS "LVGL version: ${lvgl_ver}")
|
||||||
|
|
||||||
#Select folder by LVGL version
|
|
||||||
if(lvgl_ver VERSION_LESS "9.0.0")
|
if(lvgl_ver VERSION_LESS "9.0.0")
|
||||||
message(VERBOSE "Compiling esp_lvgl_port for LVGL8")
|
message(VERBOSE "Compiling esp_lvgl_port for LVGL8")
|
||||||
set(PORT_FOLDER "lvgl8")
|
set(PORT_FOLDER "lvgl8")
|
||||||
@ -16,10 +37,8 @@ else()
|
|||||||
set(PORT_FOLDER "lvgl9")
|
set(PORT_FOLDER "lvgl9")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Add LVGL port extensions
|
||||||
set(PORT_PATH "src/${PORT_FOLDER}")
|
set(PORT_PATH "src/${PORT_FOLDER}")
|
||||||
|
|
||||||
idf_component_register(SRCS "${PORT_PATH}/esp_lvgl_port.c" "${PORT_PATH}/esp_lvgl_port_disp.c" INCLUDE_DIRS "include" REQUIRES "esp_lcd" "lvgl" PRIV_REQUIRES "esp_timer")
|
|
||||||
|
|
||||||
set(ADD_SRCS "")
|
set(ADD_SRCS "")
|
||||||
set(ADD_LIBS "")
|
set(ADD_LIBS "")
|
||||||
|
|
||||||
@ -57,9 +76,43 @@ if("usb_host_hid" IN_LIST build_components)
|
|||||||
list(APPEND ADD_LIBS idf::usb_host_hid)
|
list(APPEND ADD_LIBS idf::usb_host_hid)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(ADD_SRCS)
|
# Include SIMD assembly source code for rendering, only for (9.1.0 <= LVG_version < 9.2.0) and only for esp32 and esp32s3
|
||||||
target_sources(${COMPONENT_LIB} PRIVATE ${ADD_SRCS})
|
if((lvgl_ver VERSION_GREATER_EQUAL "9.1.0") AND (lvgl_ver VERSION_LESS "9.2.0"))
|
||||||
endif()
|
if(CONFIG_IDF_TARGET_ESP32 OR CONFIG_IDF_TARGET_ESP32S3)
|
||||||
if(ADD_LIBS)
|
message(VERBOSE "Compiling SIMD")
|
||||||
target_link_libraries(${COMPONENT_LIB} PRIVATE ${ADD_LIBS})
|
if(CONFIG_IDF_TARGET_ESP32S3)
|
||||||
|
file(GLOB_RECURSE ASM_SRCS ${PORT_PATH}/simd/*_esp32s3.S) # Select only esp32s3 related files
|
||||||
|
else()
|
||||||
|
file(GLOB_RECURSE ASM_SRCS ${PORT_PATH}/simd/*_esp32.S) # Select only esp32 related files
|
||||||
|
endif()
|
||||||
|
list(APPEND ADD_SRCS ${ASM_SRCS})
|
||||||
|
|
||||||
|
# Include component libraries, so lvgl component would see lvgl_port includes
|
||||||
|
idf_component_get_property(lvgl_lib ${lvgl_name} COMPONENT_LIB)
|
||||||
|
target_include_directories(${lvgl_lib} PRIVATE "include")
|
||||||
|
|
||||||
|
# Force link .S files
|
||||||
|
set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u lv_color_blend_to_argb8888_esp")
|
||||||
|
set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u lv_color_blend_to_rgb565_esp")
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
# Here we create the real lvgl_port_lib
|
||||||
|
add_library(lvgl_port_lib STATIC
|
||||||
|
${PORT_PATH}/esp_lvgl_port.c
|
||||||
|
${PORT_PATH}/esp_lvgl_port_disp.c
|
||||||
|
${ADD_SRCS}
|
||||||
|
)
|
||||||
|
target_include_directories(lvgl_port_lib PUBLIC "include")
|
||||||
|
target_include_directories(lvgl_port_lib PRIVATE "priv_include")
|
||||||
|
target_link_libraries(lvgl_port_lib PUBLIC
|
||||||
|
idf::esp_lcd
|
||||||
|
idf::${lvgl_name}
|
||||||
|
)
|
||||||
|
target_link_libraries(lvgl_port_lib PRIVATE
|
||||||
|
idf::esp_timer
|
||||||
|
${ADD_LIBS}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Finally, link the lvgl_port_lib its esp-idf interface library
|
||||||
|
target_link_libraries(${COMPONENT_LIB} INTERFACE lvgl_port_lib)
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
# LVGL ESP Portation
|
# LVGL ESP Portation
|
||||||
|
|
||||||
[](https://components.espressif.com/components/espressif/esp_lvgl_port)
|
[](https://components.espressif.com/components/espressif/esp_lvgl_port)
|
||||||
|

|
||||||
|
|
||||||
This component helps with using LVGL with Espressif's LCD and touch drivers. It can be used with any project with LCD display.
|
This component helps with using LVGL with Espressif's LCD and touch drivers. It can be used with any project with LCD display.
|
||||||
|
|
||||||
@ -8,14 +9,16 @@ This component helps with using LVGL with Espressif's LCD and touch drivers. It
|
|||||||
* Initialization of the LVGL
|
* Initialization of the LVGL
|
||||||
* Create task and timer
|
* Create task and timer
|
||||||
* Handle rotating
|
* Handle rotating
|
||||||
|
* Power saving
|
||||||
* Add/remove display (using [`esp_lcd`](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/lcd.html))
|
* Add/remove display (using [`esp_lcd`](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/lcd.html))
|
||||||
* Add/remove touch input (using [`esp_lcd_touch`](https://github.com/espressif/esp-bsp/tree/master/components/lcd_touch))
|
* Add/remove touch input (using [`esp_lcd_touch`](https://github.com/espressif/esp-bsp/tree/master/components/lcd_touch))
|
||||||
* Add/remove navigation buttons input (using [`button`](https://github.com/espressif/esp-iot-solution/tree/master/components/button))
|
* Add/remove navigation buttons input (using [`button`](https://github.com/espressif/esp-iot-solution/tree/master/components/button))
|
||||||
* Add/remove encoder input (using [`knob`](https://github.com/espressif/esp-iot-solution/tree/master/components/knob))
|
* Add/remove encoder input (using [`knob`](https://github.com/espressif/esp-iot-solution/tree/master/components/knob))
|
||||||
|
* Add/remove USB HID mouse/keyboard input (using [`usb_host_hid`](https://components.espressif.com/components/espressif/usb_host_hid))
|
||||||
|
|
||||||
## LVGL Version
|
## LVGL Version
|
||||||
|
|
||||||
This component supports **LVGL8** and **LVGL9**. By default, it selects the latest LVGL version. If you want to use a specific version (e.g. latest LVGL8), you can easily put into `idf_component.yml` in your project like this:
|
This component supports **LVGL8** and **LVGL9**. By default, it selects the latest LVGL version. If you want to use a specific version (e.g. latest LVGL8), you can easily define this requirement in `idf_component.yml` in your project like this:
|
||||||
|
|
||||||
```
|
```
|
||||||
lvgl/lvgl:
|
lvgl/lvgl:
|
||||||
@ -25,7 +28,7 @@ This component supports **LVGL8** and **LVGL9**. By default, it selects the l
|
|||||||
|
|
||||||
### LVGL Version Compatibility
|
### LVGL Version Compatibility
|
||||||
|
|
||||||
This component is fully compatible with LVGL version 9. All types and functions are used from LVGL9. Some LVGL9 types are not supported in LVGL8 and there are retyping in [`esp_lvgl_port_compatibility.h`](include/esp_lvgl_port_compatibility.h) header file. **Please, be aware, that some draw and object functions are not compatible between LVGL8 and LVGL9.**
|
This component is fully compatible with LVGL version 9. All types and functions are used from LVGL9. Some LVGL9 types are not supported in LVGL8 and there are retyped in [`esp_lvgl_port_compatibility.h`](include/esp_lvgl_port_compatibility.h) header file. **Please, be aware, that some draw and object functions are not compatible between LVGL8 and LVGL9.**
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@ -59,7 +62,8 @@ Add an LCD screen to the LVGL. It can be called multiple times for adding multip
|
|||||||
.hres = DISP_WIDTH,
|
.hres = DISP_WIDTH,
|
||||||
.vres = DISP_HEIGHT,
|
.vres = DISP_HEIGHT,
|
||||||
.monochrome = false,
|
.monochrome = false,
|
||||||
/* Rotation values must be same as used in esp_lcd for initial settings of the screen */
|
.mipi_dsi = false,
|
||||||
|
.color_format = LV_COLOR_FORMAT_RGB565,
|
||||||
.rotation = {
|
.rotation = {
|
||||||
.swap_xy = false,
|
.swap_xy = false,
|
||||||
.mirror_x = false,
|
.mirror_x = false,
|
||||||
@ -78,6 +82,10 @@ Add an LCD screen to the LVGL. It can be called multiple times for adding multip
|
|||||||
lvgl_port_remove_disp(disp_handle);
|
lvgl_port_remove_disp(disp_handle);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> 1. For adding RGB or MIPI-DSI screen, use functions `lvgl_port_add_disp_rgb` or `lvgl_port_add_disp_dsi`.
|
||||||
|
> 2. DMA buffer can be used only when you use color format `LV_COLOR_FORMAT_RGB565`.
|
||||||
|
|
||||||
### Add touch input
|
### Add touch input
|
||||||
|
|
||||||
Add touch input to the LVGL. It can be called more times for adding more touch inputs.
|
Add touch input to the LVGL. It can be called more times for adding more touch inputs.
|
||||||
@ -144,8 +152,8 @@ Add buttons input to the LVGL. It can be called more times for adding more butto
|
|||||||
/* If deinitializing LVGL port, remember to delete all buttons: */
|
/* If deinitializing LVGL port, remember to delete all buttons: */
|
||||||
lvgl_port_remove_navigation_buttons(buttons_handle);
|
lvgl_port_remove_navigation_buttons(buttons_handle);
|
||||||
```
|
```
|
||||||
|
> [!NOTE]
|
||||||
**Note:** When you use navigation buttons for control LVGL objects, these objects must be added to LVGL groups. See [LVGL documentation](https://docs.lvgl.io/master/overview/indev.html?highlight=lv_indev_get_act#keypad-and-encoder) for more info.
|
> When you use navigation buttons for control LVGL objects, these objects must be added to LVGL groups. See [LVGL documentation](https://docs.lvgl.io/master/overview/indev.html?highlight=lv_indev_get_act#keypad-and-encoder) for more info.
|
||||||
|
|
||||||
### Add encoder input
|
### Add encoder input
|
||||||
|
|
||||||
@ -179,8 +187,8 @@ Add encoder input to the LVGL. It can be called more times for adding more encod
|
|||||||
/* If deinitializing LVGL port, remember to delete all encoders: */
|
/* If deinitializing LVGL port, remember to delete all encoders: */
|
||||||
lvgl_port_remove_encoder(encoder_handle);
|
lvgl_port_remove_encoder(encoder_handle);
|
||||||
```
|
```
|
||||||
|
> [!NOTE]
|
||||||
**Note:** When you use encoder for control LVGL objects, these objects must be added to LVGL groups. See [LVGL documentation](https://docs.lvgl.io/master/overview/indev.html?highlight=lv_indev_get_act#keypad-and-encoder) for more info.
|
> When you use encoder for control LVGL objects, these objects must be added to LVGL groups. See [LVGL documentation](https://docs.lvgl.io/master/overview/indev.html?highlight=lv_indev_get_act#keypad-and-encoder) for more info.
|
||||||
|
|
||||||
### Add USB HID keyboard and mouse input
|
### Add USB HID keyboard and mouse input
|
||||||
|
|
||||||
@ -217,7 +225,8 @@ Keyboard special behavior (when objects are in group):
|
|||||||
- **ARROWS** or **HOME** or **END**: Move in text area
|
- **ARROWS** or **HOME** or **END**: Move in text area
|
||||||
- **DEL** or **Backspace**: Remove character in textarea
|
- **DEL** or **Backspace**: Remove character in textarea
|
||||||
|
|
||||||
**Note:** When you use keyboard for control LVGL objects, these objects must be added to LVGL groups. See [LVGL documentation](https://docs.lvgl.io/master/overview/indev.html?highlight=lv_indev_get_act#keypad-and-encoder) for more info.
|
> [!NOTE]
|
||||||
|
> When you use keyboard for control LVGL objects, these objects must be added to LVGL groups. See [LVGL documentation](https://docs.lvgl.io/master/overview/indev.html?highlight=lv_indev_get_act#keypad-and-encoder) for more info.
|
||||||
|
|
||||||
### LVGL API usage
|
### LVGL API usage
|
||||||
|
|
||||||
@ -254,7 +263,11 @@ Display rotation can be changed at runtime.
|
|||||||
lv_disp_set_rotation(disp_handle, LV_DISP_ROT_90);
|
lv_disp_set_rotation(disp_handle, LV_DISP_ROT_90);
|
||||||
```
|
```
|
||||||
|
|
||||||
**Note:** During the hardware rotating, the component call [`esp_lcd`](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/lcd.html) API. When using software rotation, you cannot use neither `direct_mode` nor `full_refresh` in the driver. See [LVGL documentation](https://docs.lvgl.io/8.3/porting/display.html?highlight=sw_rotate) for more info.
|
> [!NOTE]
|
||||||
|
> This feature consume more RAM.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> During the hardware rotating, the component call [`esp_lcd`](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/lcd.html) API. When using software rotation, you cannot use neither `direct_mode` nor `full_refresh` in the driver. See [LVGL documentation](https://docs.lvgl.io/8.3/porting/display.html?highlight=sw_rotate) for more info.
|
||||||
|
|
||||||
### Using PSRAM canvas
|
### Using PSRAM canvas
|
||||||
|
|
||||||
@ -272,6 +285,79 @@ If the SRAM is insufficient, you can use the PSRAM as a canvas and use a small t
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Generating images (C Array)
|
||||||
|
|
||||||
|
Images can be generated during build by adding these lines to end of the main CMakeLists.txt:
|
||||||
|
```
|
||||||
|
# Generate C array for each image
|
||||||
|
lvgl_port_create_c_image("images/logo.png" "images/" "ARGB8888" "NONE")
|
||||||
|
lvgl_port_create_c_image("images/image.png" "images/" "ARGB8888" "NONE")
|
||||||
|
# Add generated images to build
|
||||||
|
lvgl_port_add_images(${COMPONENT_LIB} "images/")
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage of create C image function:
|
||||||
|
```
|
||||||
|
lvgl_port_create_c_image(input_image output_folder color_format compression)
|
||||||
|
```
|
||||||
|
|
||||||
|
Available color formats:
|
||||||
|
L8,I1,I2,I4,I8,A1,A2,A4,A8,ARGB8888,XRGB8888,RGB565,RGB565A8,RGB888,TRUECOLOR,TRUECOLOR_ALPHA,AUTO
|
||||||
|
|
||||||
|
Available compression:
|
||||||
|
NONE,RLE,LZ4
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> Parameters `color_format` and `compression` are used only in LVGL 9.
|
||||||
|
|
||||||
|
## Power Saving
|
||||||
|
|
||||||
|
The LVGL port can be optimized for power saving mode. There are two main features.
|
||||||
|
|
||||||
|
### LVGL task sleep
|
||||||
|
|
||||||
|
For optimization power saving, the LVGL task should sleep, when it does nothing. Set `task_max_sleep_ms` to big value, the LVGL task will wait for events only.
|
||||||
|
|
||||||
|
The LVGL task can sleep till these situations:
|
||||||
|
* LVGL display invalidate
|
||||||
|
* LVGL animation in process
|
||||||
|
* Touch interrupt
|
||||||
|
* Button interrupt
|
||||||
|
* Knob interrupt
|
||||||
|
* USB mouse/keyboard interrupt
|
||||||
|
* Timeout (`task_max_sleep_ms` in configuration structure)
|
||||||
|
* User wake (by function `lvgl_port_task_wake`)
|
||||||
|
|
||||||
|
> [!WARNING]
|
||||||
|
> This feature is available from LVGL 9.
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> Don't forget to set the interrupt pin in LCD touch when you set a big time for sleep in `task_max_sleep_ms`.
|
||||||
|
|
||||||
|
### Stopping the timer
|
||||||
|
|
||||||
|
Timers can still work during light-sleep mode. You can stop LVGL timer before use light-sleep by function:
|
||||||
|
|
||||||
|
```
|
||||||
|
lvgl_port_stop();
|
||||||
|
```
|
||||||
|
|
||||||
|
and resume LVGL timer after wake by function:
|
||||||
|
|
||||||
|
```
|
||||||
|
lvgl_port_resume();
|
||||||
|
```
|
||||||
|
|
||||||
## Performance
|
## Performance
|
||||||
|
|
||||||
Key feature of every graphical application is performance. Recommended settings for improving LCD performance is described in a separate document [here](docs/performance.md).
|
Key feature of every graphical application is performance. Recommended settings for improving LCD performance is described in a separate document [here](docs/performance.md).
|
||||||
|
|
||||||
|
### Performance monitor
|
||||||
|
|
||||||
|
For show performance monitor in LVGL9, please add these lines to sdkconfig.defaults and rebuild all.
|
||||||
|
|
||||||
|
```
|
||||||
|
CONFIG_LV_USE_OBSERVER=y
|
||||||
|
CONFIG_LV_USE_SYSMON=y
|
||||||
|
CONFIG_LV_USE_PERF_MONITOR=y
|
||||||
|
```
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
# LCD & LVGL Performance
|
# LCD & LVGL Performance
|
||||||
|
|
||||||
This document provides steps, how to set up your LCD and LVGL port for the best performance and comparison of different settings. All settings and measurements are valid for Espressif's chips.
|
This document provides steps, how to set up your LCD and LVGL port for the best performance and comparison of different settings. All settings and measurements are valid for Espressif's chips. Examples in [ESP-BSP](https://github.com/espressif/esp-bsp) are ready to use with the best performance.
|
||||||
|
|
||||||
## Performance metrics
|
## Performance metrics
|
||||||
|
|
||||||
@ -67,7 +67,7 @@ The main LVGL task can be processed on the second core of the CPU. It can increa
|
|||||||
|
|
||||||
### Using esp-idf `memcpy` and `memset` instead LVGL's configuration
|
### Using esp-idf `memcpy` and `memset` instead LVGL's configuration
|
||||||
|
|
||||||
Native esp-idf implementation are a little (~1-3 FPS) faster.
|
Native esp-idf implementation are a little (~1-3 FPS) faster (only for LVGL8).
|
||||||
|
|
||||||
* `CONFIG_LV_MEMCPY_MEMSET_STD=y`
|
* `CONFIG_LV_MEMCPY_MEMSET_STD=y`
|
||||||
|
|
||||||
@ -75,10 +75,45 @@ Native esp-idf implementation are a little (~1-3 FPS) faster.
|
|||||||
|
|
||||||
This setting can improve subjective performance during screen transitions (scrolling, etc.).
|
This setting can improve subjective performance during screen transitions (scrolling, etc.).
|
||||||
|
|
||||||
|
LVGL8
|
||||||
* `CONFIG_LV_DISP_DEF_REFR_PERIOD=10`
|
* `CONFIG_LV_DISP_DEF_REFR_PERIOD=10`
|
||||||
|
|
||||||
|
LVGL9
|
||||||
|
* `CONFIG_LV_DEF_REFR_PERIOD=10`
|
||||||
|
|
||||||
## Example FPS improvement vs graphical settings
|
## Example FPS improvement vs graphical settings
|
||||||
|
|
||||||
|
The LVGL9 benchmark demo uses a different algorithm for measuring FPS. In this case, we used the same algorithm for measurement in LVGL8 for comparison.
|
||||||
|
|
||||||
|
### RGB LCD, PSRAM (octal) with GDMA - ESP32-S3-LCD-EV-BOARD
|
||||||
|
|
||||||
|
<img src="https://github.com/espressif/esp-bsp/blob/master/docu/pics/esp32-s3-lcd-ev-board_800x480.png?raw=true" align="right" width="300px" />
|
||||||
|
|
||||||
|
Default settings:
|
||||||
|
* BSP example `display_lvgl_demos`
|
||||||
|
* LCD: 4.3" 800x480
|
||||||
|
* Interface: RGB
|
||||||
|
* LVGL buffer size: 800 x 480
|
||||||
|
* LVGL buffer mode: Direct (avoid-tearing)
|
||||||
|
* LVGL double buffer: NO
|
||||||
|
* Optimization: Debug
|
||||||
|
* CPU frequency: 160 MHz
|
||||||
|
* Flash frequency: 80 MHz
|
||||||
|
* PSRAM frequency: 80 MHz
|
||||||
|
* Flash mode: DIO
|
||||||
|
* LVGL display refresh period: 30 ms
|
||||||
|
|
||||||
|
| Average FPS (LVGL8) | Average FPS (LVGL 9.2) | Changed settings |
|
||||||
|
| :---: | :---: | ---------------- |
|
||||||
|
| 12 | 9 | Default |
|
||||||
|
| 13 | 9 | + Optimization: Performance (`CONFIG_COMPILER_OPTIMIZATION_PERF=y`) |
|
||||||
|
| 14 | 11 | + CPU frequency: 240 MHz (`CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y`) |
|
||||||
|
| 14 | 11 | + Flash mode: QIO (`CONFIG_ESPTOOLPY_FLASHMODE_QIO=y`) |
|
||||||
|
| 15 | 11 | + LVGL fast memory enabled (`CONFIG_LV_ATTRIBUTE_FAST_MEM_USE_IRAM=y`) |
|
||||||
|
| 16 | 11 | + (`CONFIG_LV_DISP_DEF_REFR_PERIOD=10` / `CONFIG_LV_DEF_REFR_PERIOD=10`) |
|
||||||
|
|
||||||
|
### Parallel 8080 LCD (only for LVGL8)
|
||||||
|
|
||||||
<img src="https://github.com/espressif/esp-bsp/blob/master/docu/pics/7inch-Capacitive-Touch-LCD-C_l.jpg?raw=true" align="right" width="300px" />
|
<img src="https://github.com/espressif/esp-bsp/blob/master/docu/pics/7inch-Capacitive-Touch-LCD-C_l.jpg?raw=true" align="right" width="300px" />
|
||||||
|
|
||||||
Default settings:
|
Default settings:
|
||||||
@ -94,7 +129,7 @@ Default settings:
|
|||||||
* Flash mode: DIO
|
* Flash mode: DIO
|
||||||
* LVGL display refresh period: 30 ms
|
* LVGL display refresh period: 30 ms
|
||||||
|
|
||||||
### Internal RAM with DMA
|
#### Internal RAM with DMA
|
||||||
|
|
||||||
| Average FPS | Weighted FPS | Changed settings |
|
| Average FPS | Weighted FPS | Changed settings |
|
||||||
| :---: | :---: | ---------------- |
|
| :---: | :---: | ---------------- |
|
||||||
@ -105,7 +140,7 @@ Default settings:
|
|||||||
| 28 | **60** | + LVGL fast memory enabled (`CONFIG_LV_ATTRIBUTE_FAST_MEM_USE_IRAM=y`) |
|
| 28 | **60** | + LVGL fast memory enabled (`CONFIG_LV_ATTRIBUTE_FAST_MEM_USE_IRAM=y`) |
|
||||||
| 41 | 55 | + (`CONFIG_LV_DISP_DEF_REFR_PERIOD=10`) |
|
| 41 | 55 | + (`CONFIG_LV_DISP_DEF_REFR_PERIOD=10`) |
|
||||||
|
|
||||||
### PSRAM (QUAD) without DMA
|
#### PSRAM (QUAD) without DMA
|
||||||
|
|
||||||
Default changes:
|
Default changes:
|
||||||
* LCD IO clock: 2 MHz
|
* LCD IO clock: 2 MHz
|
||||||
@ -124,32 +159,6 @@ Default changes:
|
|||||||
|
|
||||||
[^1]: This is not working in default and sometimes in fast changes on screen is not working properly.
|
[^1]: This is not working in default and sometimes in fast changes on screen is not working properly.
|
||||||
|
|
||||||
### RGB LCD (without `esp_lvgl_port`), PSRAM (octal) with GDMA - ESP32-S3-LCD-EV-BOARD
|
|
||||||
|
|
||||||
<img src="https://github.com/espressif/esp-bsp/blob/master/docu/pics/esp32-s3-lcd-ev-board_800x480.png?raw=true" align="right" width="300px" />
|
|
||||||
|
|
||||||
Default settings:
|
|
||||||
* BSP example `display_lvgl_demos`
|
|
||||||
* LCD: 4.3" 800x480
|
|
||||||
* Interface: RGB
|
|
||||||
* LVGL buffer size: 800 x 100
|
|
||||||
* LVGL double buffer: NO
|
|
||||||
* Optimization: Debug
|
|
||||||
* CPU frequency: 160 MHz
|
|
||||||
* Flash frequency: 80 MHz
|
|
||||||
* PSRAM frequency: 80 MHz
|
|
||||||
* Flash mode: DIO
|
|
||||||
* LVGL display refresh period: 30 ms
|
|
||||||
|
|
||||||
| Average FPS | Weighted FPS | Changed settings |
|
|
||||||
| :---: | :---: | ---------------- |
|
|
||||||
| 18 | 24 | Default |
|
|
||||||
| 18 | 26 | + Optimization: Performance (`CONFIG_COMPILER_OPTIMIZATION_PERF=y`) |
|
|
||||||
| 21 | 32 | + CPU frequency: 240 MHz (`CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y`) |
|
|
||||||
| 21 | 32 | + Flash mode: QIO (`CONFIG_ESPTOOLPY_FLASHMODE_QIO=y`) |
|
|
||||||
| 21 | 32 | + LVGL fast memory enabled (`CONFIG_LV_ATTRIBUTE_FAST_MEM_USE_IRAM=y`) |
|
|
||||||
| 35 | 34 | + (`CONFIG_LV_DISP_DEF_REFR_PERIOD=10`) |
|
|
||||||
|
|
||||||
## Conclusion
|
## Conclusion
|
||||||
|
|
||||||
The graphical performance depends on a lot of things and settings, many of which affect the whole system (Compiler, Flash, CPU, PSRAM configuration...). The user should primarily focus on trade-off between frame-buffer(s) size and RAM consumption of the buffer, before optimizing the design further.
|
The graphical performance depends on a lot of things and settings, many of which affect the whole system (Compiler, Flash, CPU, PSRAM configuration...). The user should primarily focus on trade-off between frame-buffer(s) size and RAM consumption of the buffer, before optimizing the design further.
|
||||||
|
|||||||
4
Libraries/esp_lvgl_port/examples/i2c_oled/CMakeLists.txt
Normal file
4
Libraries/esp_lvgl_port/examples/i2c_oled/CMakeLists.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(i2c_oled)
|
||||||
73
Libraries/esp_lvgl_port/examples/i2c_oled/README.md
Normal file
73
Libraries/esp_lvgl_port/examples/i2c_oled/README.md
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-P4 | ESP32-S2 | ESP32-S3 |
|
||||||
|
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- | -------- |
|
||||||
|
|
||||||
|
# I2C OLED example
|
||||||
|
|
||||||
|
[esp_lcd](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/lcd.html) supports I2C interfaced OLED LCD, whose color depth is usually 1bpp.
|
||||||
|
|
||||||
|
This example shows how to make use of the SSD1306 panel driver from `esp_lcd` component to facilitate the porting of LVGL library. In the end, example will display a scrolling text on the OLED screen.
|
||||||
|
|
||||||
|
## LVGL Version
|
||||||
|
|
||||||
|
This example is using the **LVGL8** version. For use it with LVGL9 version, please delete file [sdkconfig.defaults](sdkconfig.defaults) and change version to `"^9"` on this line in [idf_component.yml](main/idf_component.yml) file:
|
||||||
|
```
|
||||||
|
lvgl/lvgl: "^8"
|
||||||
|
```
|
||||||
|
|
||||||
|
NOTE: When you are changing LVGL versions, please remove these files and folders before new build:
|
||||||
|
- build/
|
||||||
|
- managed_components/
|
||||||
|
- dependencies.lock
|
||||||
|
- sdkconfig
|
||||||
|
|
||||||
|
## How to use the example
|
||||||
|
|
||||||
|
### Hardware Required
|
||||||
|
|
||||||
|
* An ESP development board
|
||||||
|
* An SSD1306 OLED LCD, with I2C interface
|
||||||
|
* An USB cable for power supply and programming
|
||||||
|
|
||||||
|
### Hardware Connection
|
||||||
|
|
||||||
|
The connection between ESP Board and the LCD is as follows:
|
||||||
|
|
||||||
|
```
|
||||||
|
ESP Board OLED LCD (I2C)
|
||||||
|
+------------------+ +-------------------+
|
||||||
|
| GND+--------------+GND |
|
||||||
|
| | | |
|
||||||
|
| 3V3+--------------+VCC |
|
||||||
|
| | | |
|
||||||
|
| SDA+--------------+SDA |
|
||||||
|
| | | |
|
||||||
|
| SCL+--------------+SCL |
|
||||||
|
+------------------+ +-------------------+
|
||||||
|
```
|
||||||
|
|
||||||
|
The GPIO number used by this example can be changed in [lvgl_example_main.c](main/i2c_oled_example_main.c). Please pay attention to the I2C hardware device address as well, you should refer to your module's spec and schematic to determine that address.
|
||||||
|
|
||||||
|
### Build and Flash
|
||||||
|
|
||||||
|
Run `idf.py -p PORT build flash monitor` to build, flash and monitor the project. A scrolling text will show up on the LCD as expected.
|
||||||
|
|
||||||
|
The first time you run `idf.py` for the example will cost extra time as the build system needs to address the component dependencies and downloads the missing components from registry into `managed_components` folder.
|
||||||
|
|
||||||
|
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||||
|
|
||||||
|
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
|
||||||
|
|
||||||
|
### Example Output
|
||||||
|
|
||||||
|
```bash
|
||||||
|
...
|
||||||
|
I (0) cpu_start: Starting scheduler on APP CPU.
|
||||||
|
I (345) example: Initialize I2C bus
|
||||||
|
I (345) example: Install panel IO
|
||||||
|
I (345) example: Install SSD1306 panel driver
|
||||||
|
I (455) example: Initialize LVGL library
|
||||||
|
I (455) example: Register display driver to LVGL
|
||||||
|
I (455) example: Install LVGL tick timer
|
||||||
|
I (455) example: Display LVGL Scroll Text
|
||||||
|
...
|
||||||
|
```
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
idf_component_register(SRCS "i2c_oled_example_main.c" "lvgl_demo_ui.c"
|
||||||
|
INCLUDE_DIRS ".")
|
||||||
@ -0,0 +1,35 @@
|
|||||||
|
menu "Example Configuration"
|
||||||
|
|
||||||
|
choice EXAMPLE_LCD_CONTROLLER
|
||||||
|
prompt "LCD controller model"
|
||||||
|
default EXAMPLE_LCD_CONTROLLER_SSD1306
|
||||||
|
help
|
||||||
|
Select LCD controller model
|
||||||
|
|
||||||
|
config EXAMPLE_LCD_CONTROLLER_SSD1306
|
||||||
|
bool "SSD1306"
|
||||||
|
|
||||||
|
config EXAMPLE_LCD_CONTROLLER_SH1107
|
||||||
|
bool "SH1107"
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
if EXAMPLE_LCD_CONTROLLER_SSD1306
|
||||||
|
choice EXAMPLE_SSD1306_HEIGHT
|
||||||
|
prompt "SSD1306 Height in pixels"
|
||||||
|
default EXAMPLE_SSD1306_HEIGHT_64
|
||||||
|
help
|
||||||
|
Height of the display in pixels. a.k.a vertical resolution
|
||||||
|
|
||||||
|
config EXAMPLE_SSD1306_HEIGHT_64
|
||||||
|
bool "64"
|
||||||
|
config EXAMPLE_SSD1306_HEIGHT_32
|
||||||
|
bool "32"
|
||||||
|
endchoice
|
||||||
|
|
||||||
|
config EXAMPLE_SSD1306_HEIGHT
|
||||||
|
int
|
||||||
|
default 64 if EXAMPLE_SSD1306_HEIGHT_64
|
||||||
|
default 32 if EXAMPLE_SSD1306_HEIGHT_32
|
||||||
|
endif
|
||||||
|
|
||||||
|
endmenu
|
||||||
@ -0,0 +1,155 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: CC0-1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "esp_timer.h"
|
||||||
|
#include "esp_lcd_panel_io.h"
|
||||||
|
#include "esp_lcd_panel_ops.h"
|
||||||
|
#include "driver/i2c_master.h"
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "lvgl.h"
|
||||||
|
#include "esp_lvgl_port.h"
|
||||||
|
|
||||||
|
#if CONFIG_EXAMPLE_LCD_CONTROLLER_SH1107
|
||||||
|
#include "esp_lcd_sh1107.h"
|
||||||
|
#else
|
||||||
|
#include "esp_lcd_panel_vendor.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static const char *TAG = "example";
|
||||||
|
|
||||||
|
#define I2C_HOST 0
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//////////////////// Please update the following configuration according to your LCD spec //////////////////////////////
|
||||||
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
#define EXAMPLE_LCD_PIXEL_CLOCK_HZ (400 * 1000)
|
||||||
|
#define EXAMPLE_PIN_NUM_SDA 18
|
||||||
|
#define EXAMPLE_PIN_NUM_SCL 23
|
||||||
|
#define EXAMPLE_PIN_NUM_RST -1
|
||||||
|
#define EXAMPLE_I2C_HW_ADDR 0x3C
|
||||||
|
|
||||||
|
// The pixel number in horizontal and vertical
|
||||||
|
#if CONFIG_EXAMPLE_LCD_CONTROLLER_SSD1306
|
||||||
|
#define EXAMPLE_LCD_H_RES 128
|
||||||
|
#define EXAMPLE_LCD_V_RES CONFIG_EXAMPLE_SSD1306_HEIGHT
|
||||||
|
#elif CONFIG_EXAMPLE_LCD_CONTROLLER_SH1107
|
||||||
|
#define EXAMPLE_LCD_H_RES 64
|
||||||
|
#define EXAMPLE_LCD_V_RES 128
|
||||||
|
#endif
|
||||||
|
// Bit number used to represent command and parameter
|
||||||
|
#define EXAMPLE_LCD_CMD_BITS 8
|
||||||
|
#define EXAMPLE_LCD_PARAM_BITS 8
|
||||||
|
|
||||||
|
extern void example_lvgl_demo_ui(lv_disp_t *disp);
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
ESP_LOGI(TAG, "Initialize I2C bus");
|
||||||
|
i2c_master_bus_handle_t i2c_bus = NULL;
|
||||||
|
i2c_master_bus_config_t bus_config = {
|
||||||
|
.clk_source = I2C_CLK_SRC_DEFAULT,
|
||||||
|
.glitch_ignore_cnt = 7,
|
||||||
|
.i2c_port = I2C_HOST,
|
||||||
|
.sda_io_num = EXAMPLE_PIN_NUM_SDA,
|
||||||
|
.scl_io_num = EXAMPLE_PIN_NUM_SCL,
|
||||||
|
.flags.enable_internal_pullup = true,
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(i2c_new_master_bus(&bus_config, &i2c_bus));
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Install panel IO");
|
||||||
|
esp_lcd_panel_io_handle_t io_handle = NULL;
|
||||||
|
esp_lcd_panel_io_i2c_config_t io_config = {
|
||||||
|
.dev_addr = EXAMPLE_I2C_HW_ADDR,
|
||||||
|
.scl_speed_hz = EXAMPLE_LCD_PIXEL_CLOCK_HZ,
|
||||||
|
.control_phase_bytes = 1, // According to SSD1306 datasheet
|
||||||
|
.lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS, // According to SSD1306 datasheet
|
||||||
|
.lcd_param_bits = EXAMPLE_LCD_PARAM_BITS, // According to SSD1306 datasheet
|
||||||
|
#if CONFIG_EXAMPLE_LCD_CONTROLLER_SSD1306
|
||||||
|
.dc_bit_offset = 6, // According to SSD1306 datasheet
|
||||||
|
#elif CONFIG_EXAMPLE_LCD_CONTROLLER_SH1107
|
||||||
|
.dc_bit_offset = 0, // According to SH1107 datasheet
|
||||||
|
.flags =
|
||||||
|
{
|
||||||
|
.disable_control_phase = 1,
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(esp_lcd_new_panel_io_i2c(i2c_bus, &io_config, &io_handle));
|
||||||
|
|
||||||
|
esp_lcd_panel_handle_t panel_handle = NULL;
|
||||||
|
esp_lcd_panel_dev_config_t panel_config = {
|
||||||
|
.bits_per_pixel = 1,
|
||||||
|
.reset_gpio_num = EXAMPLE_PIN_NUM_RST,
|
||||||
|
#if (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5,0,0))
|
||||||
|
.color_space = ESP_LCD_COLOR_SPACE_MONOCHROME,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
#if CONFIG_EXAMPLE_LCD_CONTROLLER_SSD1306
|
||||||
|
#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5,3,0))
|
||||||
|
esp_lcd_panel_ssd1306_config_t ssd1306_config = {
|
||||||
|
.height = EXAMPLE_LCD_V_RES,
|
||||||
|
};
|
||||||
|
panel_config.vendor_config = &ssd1306_config;
|
||||||
|
#endif
|
||||||
|
ESP_LOGI(TAG, "Install SSD1306 panel driver");
|
||||||
|
ESP_ERROR_CHECK(esp_lcd_new_panel_ssd1306(io_handle, &panel_config, &panel_handle));
|
||||||
|
#elif CONFIG_EXAMPLE_LCD_CONTROLLER_SH1107
|
||||||
|
ESP_LOGI(TAG, "Install SH1107 panel driver");
|
||||||
|
ESP_ERROR_CHECK(esp_lcd_new_panel_sh1107(io_handle, &panel_config, &panel_handle));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(esp_lcd_panel_reset(panel_handle));
|
||||||
|
ESP_ERROR_CHECK(esp_lcd_panel_init(panel_handle));
|
||||||
|
ESP_ERROR_CHECK(esp_lcd_panel_disp_on_off(panel_handle, true));
|
||||||
|
|
||||||
|
#if CONFIG_EXAMPLE_LCD_CONTROLLER_SH1107
|
||||||
|
ESP_ERROR_CHECK(esp_lcd_panel_invert_color(panel_handle, true));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Initialize LVGL");
|
||||||
|
const lvgl_port_cfg_t lvgl_cfg = ESP_LVGL_PORT_INIT_CONFIG();
|
||||||
|
lvgl_port_init(&lvgl_cfg);
|
||||||
|
|
||||||
|
const lvgl_port_display_cfg_t disp_cfg = {
|
||||||
|
.io_handle = io_handle,
|
||||||
|
.panel_handle = panel_handle,
|
||||||
|
.buffer_size = EXAMPLE_LCD_H_RES * EXAMPLE_LCD_V_RES,
|
||||||
|
.double_buffer = true,
|
||||||
|
.hres = EXAMPLE_LCD_H_RES,
|
||||||
|
.vres = EXAMPLE_LCD_V_RES,
|
||||||
|
.monochrome = true,
|
||||||
|
#if LVGL_VERSION_MAJOR >= 9
|
||||||
|
.color_format = LV_COLOR_FORMAT_RGB565,
|
||||||
|
#endif
|
||||||
|
.rotation = {
|
||||||
|
.swap_xy = false,
|
||||||
|
.mirror_x = false,
|
||||||
|
.mirror_y = false,
|
||||||
|
},
|
||||||
|
.flags = {
|
||||||
|
#if LVGL_VERSION_MAJOR >= 9
|
||||||
|
.swap_bytes = false,
|
||||||
|
#endif
|
||||||
|
.sw_rotate = false,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
lv_disp_t *disp = lvgl_port_add_disp(&disp_cfg);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Display LVGL Scroll Text");
|
||||||
|
// Lock the mutex due to the LVGL APIs are not thread-safe
|
||||||
|
if (lvgl_port_lock(0)) {
|
||||||
|
/* Rotation of the screen */
|
||||||
|
lv_disp_set_rotation(disp, LV_DISPLAY_ROTATION_0);
|
||||||
|
|
||||||
|
example_lvgl_demo_ui(disp);
|
||||||
|
// Release the mutex
|
||||||
|
lvgl_port_unlock();
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
dependencies:
|
||||||
|
idf: ">=4.4"
|
||||||
|
lvgl/lvgl: "^8"
|
||||||
|
esp_lcd_sh1107: "^1"
|
||||||
|
esp_lvgl_port:
|
||||||
|
version: "*"
|
||||||
|
override_path: "../../../"
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: CC0-1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "lvgl.h"
|
||||||
|
|
||||||
|
void example_lvgl_demo_ui(lv_disp_t *disp)
|
||||||
|
{
|
||||||
|
lv_obj_t *scr = lv_disp_get_scr_act(disp);
|
||||||
|
lv_obj_t *label = lv_label_create(scr);
|
||||||
|
lv_label_set_long_mode(label, LV_LABEL_LONG_SCROLL_CIRCULAR); /* Circular scroll */
|
||||||
|
lv_label_set_text(label, "Hello Espressif, Hello LVGL.");
|
||||||
|
/* Size of the screen (if you use rotation 90 or 270, please set disp->driver->ver_res) */
|
||||||
|
#if LVGL_VERSION_MAJOR >= 9
|
||||||
|
lv_obj_set_width(label, lv_display_get_physical_horizontal_resolution(disp));
|
||||||
|
#else
|
||||||
|
lv_obj_set_width(label, disp->driver->hor_res);
|
||||||
|
#endif
|
||||||
|
lv_obj_align(label, LV_ALIGN_TOP_MID, 0, 0);
|
||||||
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
# This file was generated using idf.py save-defconfig. It can be edited manually.
|
||||||
|
# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration
|
||||||
|
#
|
||||||
|
CONFIG_LV_USE_USER_DATA=y
|
||||||
|
CONFIG_LV_COLOR_DEPTH_1=y
|
||||||
9
Libraries/esp_lvgl_port/examples/rgb_lcd/CMakeLists.txt
Normal file
9
Libraries/esp_lvgl_port/examples/rgb_lcd/CMakeLists.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||||
|
# in this exact order for cmake to work correctly
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
# "Trim" the build. Include the minimal set of components, main and anything it depends on.
|
||||||
|
set(COMPONENTS main)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(rgb_lcd)
|
||||||
19
Libraries/esp_lvgl_port/examples/rgb_lcd/README.md
Normal file
19
Libraries/esp_lvgl_port/examples/rgb_lcd/README.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
# ESP LVGL RGB Screen Example
|
||||||
|
|
||||||
|
Very simple example for demonstration of initialization and usage of the `esp_lvgl_port` component with RGB LCD. This example contains four main parts:
|
||||||
|
|
||||||
|
## 1. LCD HW initialization - `app_lcd_init()`
|
||||||
|
|
||||||
|
Standard HW initialization of the LCD using [`esp_lcd`](https://github.com/espressif/esp-idf/tree/master/components/esp_lcd) component. Settings of this example are fully compatible with [ESP32-S3-LCD-EV-Board-2](https://github.com/espressif/esp-bsp/tree/master/bsp/esp32_s3_lcd_ev_board) board.
|
||||||
|
|
||||||
|
## 2. Touch HW initialization - `app_touch_init()`
|
||||||
|
|
||||||
|
Standard HW initialization of the LCD touch using [`esp_lcd_touch`](https://github.com/espressif/esp-bsp/tree/master/components/lcd_touch/esp_lcd_touch) component. Settings of this example are fully compatible with [ESP32-S3-LCD-EV-Board-2](https://github.com/espressif/esp-bsp/tree/master/bsp/esp32_s3_lcd_ev_board) board.
|
||||||
|
|
||||||
|
## 3. LVGL port initialization - `app_lvgl_init()`
|
||||||
|
|
||||||
|
Initialization of the LVGL port.
|
||||||
|
|
||||||
|
## 4. LVGL objects example usage - `app_main_display()`
|
||||||
|
|
||||||
|
Very simple demonstration code of using LVGL objects after LVGL port initialization.
|
||||||
10
Libraries/esp_lvgl_port/examples/rgb_lcd/main/CMakeLists.txt
Normal file
10
Libraries/esp_lvgl_port/examples/rgb_lcd/main/CMakeLists.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
idf_component_register(SRCS "main.c"
|
||||||
|
INCLUDE_DIRS "." ${LV_DEMO_DIR})
|
||||||
|
|
||||||
|
lvgl_port_create_c_image("images/esp_logo.png" "images/" "ARGB8888" "NONE")
|
||||||
|
lvgl_port_add_images(${COMPONENT_LIB} "images/")
|
||||||
|
|
||||||
|
set_source_files_properties(
|
||||||
|
PROPERTIES COMPILE_OPTIONS
|
||||||
|
"-DLV_LVGL_H_INCLUDE_SIMPLE;-Wno-format;"
|
||||||
|
)
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
dependencies:
|
||||||
|
idf: ">=5.0"
|
||||||
|
esp_lcd_touch_gt1151:
|
||||||
|
version: "^1"
|
||||||
|
override_path: "../../../../lcd_touch/esp_lcd_touch_gt1151/"
|
||||||
|
esp_lvgl_port:
|
||||||
|
version: "*"
|
||||||
|
override_path: "../../../"
|
||||||
1
Libraries/esp_lvgl_port/examples/rgb_lcd/main/images/.gitignore
vendored
Normal file
1
Libraries/esp_lvgl_port/examples/rgb_lcd/main/images/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.c
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 6.5 KiB |
312
Libraries/esp_lvgl_port/examples/rgb_lcd/main/main.c
Normal file
312
Libraries/esp_lvgl_port/examples/rgb_lcd/main/main.c
Normal file
@ -0,0 +1,312 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_check.h"
|
||||||
|
#include "driver/i2c.h"
|
||||||
|
#include "esp_lcd_panel_io.h"
|
||||||
|
#include "esp_lcd_panel_ops.h"
|
||||||
|
#include "esp_lcd_panel_rgb.h"
|
||||||
|
#include "esp_lvgl_port.h"
|
||||||
|
#include "lv_demos.h"
|
||||||
|
|
||||||
|
#include "esp_lcd_touch_gt1151.h"
|
||||||
|
|
||||||
|
/* LCD size */
|
||||||
|
#define EXAMPLE_LCD_H_RES (800)
|
||||||
|
#define EXAMPLE_LCD_V_RES (480)
|
||||||
|
|
||||||
|
/* LCD settings */
|
||||||
|
#define EXAMPLE_LCD_LVGL_FULL_REFRESH (0)
|
||||||
|
#define EXAMPLE_LCD_LVGL_DIRECT_MODE (1)
|
||||||
|
#define EXAMPLE_LCD_LVGL_AVOID_TEAR (1)
|
||||||
|
#define EXAMPLE_LCD_RGB_BOUNCE_BUFFER_MODE (1)
|
||||||
|
#define EXAMPLE_LCD_DRAW_BUFF_DOUBLE (0)
|
||||||
|
#define EXAMPLE_LCD_DRAW_BUFF_HEIGHT (100)
|
||||||
|
#define EXAMPLE_LCD_RGB_BUFFER_NUMS (2)
|
||||||
|
#define EXAMPLE_LCD_RGB_BOUNCE_BUFFER_HEIGHT (10)
|
||||||
|
|
||||||
|
/* LCD pins */
|
||||||
|
#define EXAMPLE_LCD_GPIO_VSYNC (GPIO_NUM_3)
|
||||||
|
#define EXAMPLE_LCD_GPIO_HSYNC (GPIO_NUM_46)
|
||||||
|
#define EXAMPLE_LCD_GPIO_DE (GPIO_NUM_17)
|
||||||
|
#define EXAMPLE_LCD_GPIO_PCLK (GPIO_NUM_9)
|
||||||
|
#define EXAMPLE_LCD_GPIO_DISP (GPIO_NUM_NC)
|
||||||
|
#define EXAMPLE_LCD_GPIO_DATA0 (GPIO_NUM_10)
|
||||||
|
#define EXAMPLE_LCD_GPIO_DATA1 (GPIO_NUM_11)
|
||||||
|
#define EXAMPLE_LCD_GPIO_DATA2 (GPIO_NUM_12)
|
||||||
|
#define EXAMPLE_LCD_GPIO_DATA3 (GPIO_NUM_13)
|
||||||
|
#define EXAMPLE_LCD_GPIO_DATA4 (GPIO_NUM_14)
|
||||||
|
#define EXAMPLE_LCD_GPIO_DATA5 (GPIO_NUM_21)
|
||||||
|
#define EXAMPLE_LCD_GPIO_DATA6 (GPIO_NUM_47)
|
||||||
|
#define EXAMPLE_LCD_GPIO_DATA7 (GPIO_NUM_48)
|
||||||
|
#define EXAMPLE_LCD_GPIO_DATA8 (GPIO_NUM_45)
|
||||||
|
#define EXAMPLE_LCD_GPIO_DATA9 (GPIO_NUM_38)
|
||||||
|
#define EXAMPLE_LCD_GPIO_DATA10 (GPIO_NUM_39)
|
||||||
|
#define EXAMPLE_LCD_GPIO_DATA11 (GPIO_NUM_40)
|
||||||
|
#define EXAMPLE_LCD_GPIO_DATA12 (GPIO_NUM_41)
|
||||||
|
#define EXAMPLE_LCD_GPIO_DATA13 (GPIO_NUM_42)
|
||||||
|
#define EXAMPLE_LCD_GPIO_DATA14 (GPIO_NUM_2)
|
||||||
|
#define EXAMPLE_LCD_GPIO_DATA15 (GPIO_NUM_1)
|
||||||
|
|
||||||
|
/* Touch settings */
|
||||||
|
#define EXAMPLE_TOUCH_I2C_NUM (0)
|
||||||
|
#define EXAMPLE_TOUCH_I2C_CLK_HZ (400000)
|
||||||
|
|
||||||
|
/* LCD touch pins */
|
||||||
|
#define EXAMPLE_TOUCH_I2C_SCL (GPIO_NUM_18)
|
||||||
|
#define EXAMPLE_TOUCH_I2C_SDA (GPIO_NUM_8)
|
||||||
|
|
||||||
|
#define EXAMPLE_LCD_PANEL_35HZ_RGB_TIMING() \
|
||||||
|
{ \
|
||||||
|
.pclk_hz = 18 * 1000 * 1000, \
|
||||||
|
.h_res = EXAMPLE_LCD_H_RES, \
|
||||||
|
.v_res = EXAMPLE_LCD_V_RES, \
|
||||||
|
.hsync_pulse_width = 40, \
|
||||||
|
.hsync_back_porch = 40, \
|
||||||
|
.hsync_front_porch = 48, \
|
||||||
|
.vsync_pulse_width = 23, \
|
||||||
|
.vsync_back_porch = 32, \
|
||||||
|
.vsync_front_porch = 13, \
|
||||||
|
.flags.pclk_active_neg = true, \
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *TAG = "EXAMPLE";
|
||||||
|
|
||||||
|
// LVGL image declare
|
||||||
|
LV_IMG_DECLARE(esp_logo)
|
||||||
|
|
||||||
|
/* LCD IO and panel */
|
||||||
|
static esp_lcd_panel_handle_t lcd_panel = NULL;
|
||||||
|
static esp_lcd_touch_handle_t touch_handle = NULL;
|
||||||
|
|
||||||
|
/* LVGL display and touch */
|
||||||
|
static lv_display_t *lvgl_disp = NULL;
|
||||||
|
static lv_indev_t *lvgl_touch_indev = NULL;
|
||||||
|
|
||||||
|
static esp_err_t app_lcd_init(void)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
|
||||||
|
/* LCD initialization */
|
||||||
|
ESP_LOGI(TAG, "Initialize RGB panel");
|
||||||
|
esp_lcd_rgb_panel_config_t panel_conf = {
|
||||||
|
.clk_src = LCD_CLK_SRC_PLL160M,
|
||||||
|
.psram_trans_align = 64,
|
||||||
|
.data_width = 16,
|
||||||
|
.bits_per_pixel = 16,
|
||||||
|
.de_gpio_num = EXAMPLE_LCD_GPIO_DE,
|
||||||
|
.pclk_gpio_num = EXAMPLE_LCD_GPIO_PCLK,
|
||||||
|
.vsync_gpio_num = EXAMPLE_LCD_GPIO_VSYNC,
|
||||||
|
.hsync_gpio_num = EXAMPLE_LCD_GPIO_HSYNC,
|
||||||
|
.disp_gpio_num = EXAMPLE_LCD_GPIO_DISP,
|
||||||
|
.data_gpio_nums = {
|
||||||
|
EXAMPLE_LCD_GPIO_DATA0,
|
||||||
|
EXAMPLE_LCD_GPIO_DATA1,
|
||||||
|
EXAMPLE_LCD_GPIO_DATA2,
|
||||||
|
EXAMPLE_LCD_GPIO_DATA3,
|
||||||
|
EXAMPLE_LCD_GPIO_DATA4,
|
||||||
|
EXAMPLE_LCD_GPIO_DATA5,
|
||||||
|
EXAMPLE_LCD_GPIO_DATA6,
|
||||||
|
EXAMPLE_LCD_GPIO_DATA7,
|
||||||
|
EXAMPLE_LCD_GPIO_DATA8,
|
||||||
|
EXAMPLE_LCD_GPIO_DATA9,
|
||||||
|
EXAMPLE_LCD_GPIO_DATA10,
|
||||||
|
EXAMPLE_LCD_GPIO_DATA11,
|
||||||
|
EXAMPLE_LCD_GPIO_DATA12,
|
||||||
|
EXAMPLE_LCD_GPIO_DATA13,
|
||||||
|
EXAMPLE_LCD_GPIO_DATA14,
|
||||||
|
EXAMPLE_LCD_GPIO_DATA15,
|
||||||
|
},
|
||||||
|
.timings = EXAMPLE_LCD_PANEL_35HZ_RGB_TIMING(),
|
||||||
|
.flags.fb_in_psram = 1,
|
||||||
|
.num_fbs = EXAMPLE_LCD_RGB_BUFFER_NUMS,
|
||||||
|
#if EXAMPLE_LCD_RGB_BOUNCE_BUFFER_MODE
|
||||||
|
.bounce_buffer_size_px = EXAMPLE_LCD_H_RES * EXAMPLE_LCD_RGB_BOUNCE_BUFFER_HEIGHT,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
ESP_GOTO_ON_ERROR(esp_lcd_new_rgb_panel(&panel_conf, &lcd_panel), err, TAG, "RGB init failed");
|
||||||
|
ESP_GOTO_ON_ERROR(esp_lcd_panel_init(lcd_panel), err, TAG, "LCD init failed");
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
err:
|
||||||
|
if (lcd_panel) {
|
||||||
|
esp_lcd_panel_del(lcd_panel);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t app_touch_init(void)
|
||||||
|
{
|
||||||
|
/* Initilize I2C */
|
||||||
|
const i2c_config_t i2c_conf = {
|
||||||
|
.mode = I2C_MODE_MASTER,
|
||||||
|
.sda_io_num = EXAMPLE_TOUCH_I2C_SDA,
|
||||||
|
.sda_pullup_en = GPIO_PULLUP_DISABLE,
|
||||||
|
.scl_io_num = EXAMPLE_TOUCH_I2C_SCL,
|
||||||
|
.scl_pullup_en = GPIO_PULLUP_DISABLE,
|
||||||
|
.master.clk_speed = EXAMPLE_TOUCH_I2C_CLK_HZ
|
||||||
|
};
|
||||||
|
ESP_RETURN_ON_ERROR(i2c_param_config(EXAMPLE_TOUCH_I2C_NUM, &i2c_conf), TAG, "I2C configuration failed");
|
||||||
|
ESP_RETURN_ON_ERROR(i2c_driver_install(EXAMPLE_TOUCH_I2C_NUM, i2c_conf.mode, 0, 0, 0), TAG, "I2C initialization failed");
|
||||||
|
|
||||||
|
/* Initialize touch HW */
|
||||||
|
const esp_lcd_touch_config_t tp_cfg = {
|
||||||
|
.x_max = EXAMPLE_LCD_H_RES,
|
||||||
|
.y_max = EXAMPLE_LCD_V_RES,
|
||||||
|
.rst_gpio_num = GPIO_NUM_NC,
|
||||||
|
.int_gpio_num = GPIO_NUM_NC,
|
||||||
|
.levels = {
|
||||||
|
.reset = 0,
|
||||||
|
.interrupt = 0,
|
||||||
|
},
|
||||||
|
.flags = {
|
||||||
|
.swap_xy = 0,
|
||||||
|
.mirror_x = 0,
|
||||||
|
.mirror_y = 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
esp_lcd_panel_io_handle_t tp_io_handle = NULL;
|
||||||
|
const esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_GT1151_CONFIG();
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)EXAMPLE_TOUCH_I2C_NUM, &tp_io_config, &tp_io_handle), TAG, "");
|
||||||
|
return esp_lcd_touch_new_i2c_gt1151(tp_io_handle, &tp_cfg, &touch_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t app_lvgl_init(void)
|
||||||
|
{
|
||||||
|
/* Initialize LVGL */
|
||||||
|
const lvgl_port_cfg_t lvgl_cfg = {
|
||||||
|
.task_priority = 4, /* LVGL task priority */
|
||||||
|
.task_stack = 6144, /* LVGL task stack size */
|
||||||
|
.task_affinity = -1, /* LVGL task pinned to core (-1 is no affinity) */
|
||||||
|
.task_max_sleep_ms = 500, /* Maximum sleep in LVGL task */
|
||||||
|
.timer_period_ms = 5 /* LVGL timer tick period in ms */
|
||||||
|
};
|
||||||
|
ESP_RETURN_ON_ERROR(lvgl_port_init(&lvgl_cfg), TAG, "LVGL port initialization failed");
|
||||||
|
|
||||||
|
uint32_t buff_size = EXAMPLE_LCD_H_RES * EXAMPLE_LCD_DRAW_BUFF_HEIGHT;
|
||||||
|
#if EXAMPLE_LCD_LVGL_FULL_REFRESH || EXAMPLE_LCD_LVGL_DIRECT_MODE
|
||||||
|
buff_size = EXAMPLE_LCD_H_RES * EXAMPLE_LCD_V_RES;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Add LCD screen */
|
||||||
|
ESP_LOGD(TAG, "Add LCD screen");
|
||||||
|
const lvgl_port_display_cfg_t disp_cfg = {
|
||||||
|
.panel_handle = lcd_panel,
|
||||||
|
.buffer_size = buff_size,
|
||||||
|
.double_buffer = EXAMPLE_LCD_DRAW_BUFF_DOUBLE,
|
||||||
|
.hres = EXAMPLE_LCD_H_RES,
|
||||||
|
.vres = EXAMPLE_LCD_V_RES,
|
||||||
|
.monochrome = false,
|
||||||
|
#if LVGL_VERSION_MAJOR >= 9
|
||||||
|
.color_format = LV_COLOR_FORMAT_RGB565,
|
||||||
|
#endif
|
||||||
|
.rotation = {
|
||||||
|
.swap_xy = false,
|
||||||
|
.mirror_x = false,
|
||||||
|
.mirror_y = false,
|
||||||
|
},
|
||||||
|
.flags = {
|
||||||
|
.buff_dma = false,
|
||||||
|
.buff_spiram = false,
|
||||||
|
#if EXAMPLE_LCD_LVGL_FULL_REFRESH
|
||||||
|
.full_refresh = true,
|
||||||
|
#elif EXAMPLE_LCD_LVGL_DIRECT_MODE
|
||||||
|
.direct_mode = true,
|
||||||
|
#endif
|
||||||
|
#if LVGL_VERSION_MAJOR >= 9
|
||||||
|
.swap_bytes = false,
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const lvgl_port_display_rgb_cfg_t rgb_cfg = {
|
||||||
|
.flags = {
|
||||||
|
#if EXAMPLE_LCD_RGB_BOUNCE_BUFFER_MODE
|
||||||
|
.bb_mode = true,
|
||||||
|
#else
|
||||||
|
.bb_mode = false,
|
||||||
|
#endif
|
||||||
|
#if EXAMPLE_LCD_LVGL_AVOID_TEAR
|
||||||
|
.avoid_tearing = true,
|
||||||
|
#else
|
||||||
|
.avoid_tearing = false,
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
lvgl_disp = lvgl_port_add_disp_rgb(&disp_cfg, &rgb_cfg);
|
||||||
|
|
||||||
|
/* Add touch input (for selected screen) */
|
||||||
|
const lvgl_port_touch_cfg_t touch_cfg = {
|
||||||
|
.disp = lvgl_disp,
|
||||||
|
.handle = touch_handle,
|
||||||
|
};
|
||||||
|
lvgl_touch_indev = lvgl_port_add_touch(&touch_cfg);
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _app_button_cb(lv_event_t *e)
|
||||||
|
{
|
||||||
|
lv_disp_rotation_t rotation = lv_disp_get_rotation(lvgl_disp);
|
||||||
|
rotation++;
|
||||||
|
if (rotation > LV_DISPLAY_ROTATION_270) {
|
||||||
|
rotation = LV_DISPLAY_ROTATION_0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LCD HW rotation */
|
||||||
|
lv_disp_set_rotation(lvgl_disp, rotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void app_main_display(void)
|
||||||
|
{
|
||||||
|
lv_obj_t *scr = lv_scr_act();
|
||||||
|
|
||||||
|
/* Your LVGL objects code here .... */
|
||||||
|
|
||||||
|
/* Create image */
|
||||||
|
lv_obj_t *img_logo = lv_img_create(scr);
|
||||||
|
lv_img_set_src(img_logo, &esp_logo);
|
||||||
|
lv_obj_align(img_logo, LV_ALIGN_TOP_MID, 0, 20);
|
||||||
|
|
||||||
|
/* Label */
|
||||||
|
lv_obj_t *label = lv_label_create(scr);
|
||||||
|
lv_obj_set_width(label, EXAMPLE_LCD_H_RES);
|
||||||
|
lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0);
|
||||||
|
#if LVGL_VERSION_MAJOR == 8
|
||||||
|
lv_label_set_recolor(label, true);
|
||||||
|
lv_label_set_text(label, "#FF0000 "LV_SYMBOL_BELL" Hello world Espressif and LVGL "LV_SYMBOL_BELL"#\n#FF9400 "LV_SYMBOL_WARNING" For simplier initialization, use BSP "LV_SYMBOL_WARNING" #");
|
||||||
|
#else
|
||||||
|
lv_label_set_text(label, LV_SYMBOL_BELL" Hello world Espressif and LVGL "LV_SYMBOL_BELL"\n "LV_SYMBOL_WARNING" For simplier initialization, use BSP "LV_SYMBOL_WARNING);
|
||||||
|
#endif
|
||||||
|
lv_obj_align(label, LV_ALIGN_CENTER, 0, 20);
|
||||||
|
|
||||||
|
/* Button */
|
||||||
|
lv_obj_t *btn = lv_btn_create(scr);
|
||||||
|
label = lv_label_create(btn);
|
||||||
|
lv_label_set_text_static(label, "Rotate screen");
|
||||||
|
lv_obj_align(btn, LV_ALIGN_BOTTOM_MID, 0, -30);
|
||||||
|
lv_obj_add_event_cb(btn, _app_button_cb, LV_EVENT_CLICKED, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
/* LCD HW initialization */
|
||||||
|
ESP_ERROR_CHECK(app_lcd_init());
|
||||||
|
|
||||||
|
/* Touch initialization */
|
||||||
|
ESP_ERROR_CHECK(app_touch_init());
|
||||||
|
|
||||||
|
/* LVGL initialization */
|
||||||
|
ESP_ERROR_CHECK(app_lvgl_init());
|
||||||
|
|
||||||
|
/* Show LVGL objects */
|
||||||
|
lvgl_port_lock(0);
|
||||||
|
//app_main_display();
|
||||||
|
lv_demo_music();
|
||||||
|
lvgl_port_unlock();
|
||||||
|
}
|
||||||
5
Libraries/esp_lvgl_port/examples/rgb_lcd/partitions.csv
Normal file
5
Libraries/esp_lvgl_port/examples/rgb_lcd/partitions.csv
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Name, Type, SubType, Offset, Size, Flags
|
||||||
|
# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
|
||||||
|
nvs, data, nvs, 0x9000, 0x6000,
|
||||||
|
phy_init, data, phy, 0xf000, 0x1000,
|
||||||
|
factory, app, factory, 0x10000, 2M,
|
||||||
|
40
Libraries/esp_lvgl_port/examples/rgb_lcd/sdkconfig.defaults
Normal file
40
Libraries/esp_lvgl_port/examples/rgb_lcd/sdkconfig.defaults
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
CONFIG_IDF_TARGET="esp32s3"
|
||||||
|
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
|
||||||
|
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||||
|
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||||
|
CONFIG_COMPILER_OPTIMIZATION_PERF=y
|
||||||
|
CONFIG_SPIRAM=y
|
||||||
|
CONFIG_SPIRAM_MODE_OCT=y
|
||||||
|
CONFIG_SPIRAM_FETCH_INSTRUCTIONS=y
|
||||||
|
CONFIG_SPIRAM_RODATA=y
|
||||||
|
CONFIG_SPIRAM_SPEED_80M=y
|
||||||
|
CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y
|
||||||
|
CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y
|
||||||
|
CONFIG_FREERTOS_HZ=1000
|
||||||
|
CONFIG_LV_MEM_CUSTOM=y
|
||||||
|
CONFIG_LV_MEMCPY_MEMSET_STD=y
|
||||||
|
CONFIG_LV_ATTRIBUTE_FAST_MEM_USE_IRAM=y
|
||||||
|
CONFIG_LV_FONT_MONTSERRAT_12=y
|
||||||
|
CONFIG_LV_FONT_MONTSERRAT_16=y
|
||||||
|
CONFIG_LV_USE_DEMO_WIDGETS=y
|
||||||
|
CONFIG_LV_USE_DEMO_BENCHMARK=y
|
||||||
|
CONFIG_LV_USE_DEMO_STRESS=y
|
||||||
|
CONFIG_LV_USE_DEMO_MUSIC=y
|
||||||
|
CONFIG_LV_DEMO_MUSIC_AUTO_PLAY=y
|
||||||
|
|
||||||
|
## LVGL8 ##
|
||||||
|
CONFIG_LV_MEM_SIZE_KILOBYTES=48
|
||||||
|
CONFIG_LV_USE_PERF_MONITOR=y
|
||||||
|
|
||||||
|
## LVGL9 ##
|
||||||
|
CONFIG_LV_CONF_SKIP=y
|
||||||
|
|
||||||
|
#CLIB default
|
||||||
|
CONFIG_LV_USE_CLIB_MALLOC=y
|
||||||
|
CONFIG_LV_USE_CLIB_SPRINTF=y
|
||||||
|
CONFIG_LV_USE_CLIB_STRING=y
|
||||||
|
|
||||||
|
# Performance monitor
|
||||||
|
CONFIG_LV_USE_OBSERVER=y
|
||||||
|
CONFIG_LV_USE_SYSMON=y
|
||||||
|
CONFIG_LV_USE_PERF_MONITOR=y
|
||||||
@ -1,2 +1,5 @@
|
|||||||
idf_component_register(SRCS "main.c"
|
idf_component_register(SRCS "main.c"
|
||||||
INCLUDE_DIRS ".")
|
INCLUDE_DIRS ".")
|
||||||
|
|
||||||
|
lvgl_port_create_c_image("images/esp_logo.png" "images/" "ARGB8888" "NONE")
|
||||||
|
lvgl_port_add_images(${COMPONENT_LIB} "images/")
|
||||||
|
|||||||
@ -4,5 +4,5 @@ dependencies:
|
|||||||
version: "^1"
|
version: "^1"
|
||||||
override_path: "../../../../lcd_touch/esp_lcd_touch_tt21100/"
|
override_path: "../../../../lcd_touch/esp_lcd_touch_tt21100/"
|
||||||
esp_lvgl_port:
|
esp_lvgl_port:
|
||||||
version: ">=1.2.0"
|
version: "*"
|
||||||
override_path: "../../../"
|
override_path: "../../../"
|
||||||
|
|||||||
1
Libraries/esp_lvgl_port/examples/touchscreen/main/images/.gitignore
vendored
Normal file
1
Libraries/esp_lvgl_port/examples/touchscreen/main/images/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
*.c
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 6.5 KiB |
@ -51,6 +51,9 @@
|
|||||||
|
|
||||||
static const char *TAG = "EXAMPLE";
|
static const char *TAG = "EXAMPLE";
|
||||||
|
|
||||||
|
// LVGL image declare
|
||||||
|
LV_IMG_DECLARE(esp_logo)
|
||||||
|
|
||||||
/* LCD IO and panel */
|
/* LCD IO and panel */
|
||||||
static esp_lcd_panel_io_handle_t lcd_io = NULL;
|
static esp_lcd_panel_io_handle_t lcd_io = NULL;
|
||||||
static esp_lcd_panel_handle_t lcd_panel = NULL;
|
static esp_lcd_panel_handle_t lcd_panel = NULL;
|
||||||
@ -177,12 +180,14 @@ static esp_err_t app_lvgl_init(void)
|
|||||||
const lvgl_port_display_cfg_t disp_cfg = {
|
const lvgl_port_display_cfg_t disp_cfg = {
|
||||||
.io_handle = lcd_io,
|
.io_handle = lcd_io,
|
||||||
.panel_handle = lcd_panel,
|
.panel_handle = lcd_panel,
|
||||||
.buffer_size = EXAMPLE_LCD_H_RES * EXAMPLE_LCD_DRAW_BUFF_HEIGHT * sizeof(uint16_t),
|
.buffer_size = EXAMPLE_LCD_H_RES * EXAMPLE_LCD_DRAW_BUFF_HEIGHT,
|
||||||
.double_buffer = EXAMPLE_LCD_DRAW_BUFF_DOUBLE,
|
.double_buffer = EXAMPLE_LCD_DRAW_BUFF_DOUBLE,
|
||||||
.hres = EXAMPLE_LCD_H_RES,
|
.hres = EXAMPLE_LCD_H_RES,
|
||||||
.vres = EXAMPLE_LCD_V_RES,
|
.vres = EXAMPLE_LCD_V_RES,
|
||||||
.monochrome = false,
|
.monochrome = false,
|
||||||
/* Rotation values must be same as used in esp_lcd for initial settings of the screen */
|
#if LVGL_VERSION_MAJOR >= 9
|
||||||
|
.color_format = LV_COLOR_FORMAT_RGB565,
|
||||||
|
#endif
|
||||||
.rotation = {
|
.rotation = {
|
||||||
.swap_xy = false,
|
.swap_xy = false,
|
||||||
.mirror_x = true,
|
.mirror_x = true,
|
||||||
@ -190,7 +195,9 @@ static esp_err_t app_lvgl_init(void)
|
|||||||
},
|
},
|
||||||
.flags = {
|
.flags = {
|
||||||
.buff_dma = true,
|
.buff_dma = true,
|
||||||
|
#if LVGL_VERSION_MAJOR >= 9
|
||||||
.swap_bytes = true,
|
.swap_bytes = true,
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
lvgl_disp = lvgl_port_add_disp(&disp_cfg);
|
lvgl_disp = lvgl_port_add_disp(&disp_cfg);
|
||||||
@ -226,6 +233,11 @@ static void app_main_display(void)
|
|||||||
|
|
||||||
/* Your LVGL objects code here .... */
|
/* Your LVGL objects code here .... */
|
||||||
|
|
||||||
|
/* Create image */
|
||||||
|
lv_obj_t *img_logo = lv_img_create(scr);
|
||||||
|
lv_img_set_src(img_logo, &esp_logo);
|
||||||
|
lv_obj_align(img_logo, LV_ALIGN_TOP_MID, 0, 20);
|
||||||
|
|
||||||
/* Label */
|
/* Label */
|
||||||
lv_obj_t *label = lv_label_create(scr);
|
lv_obj_t *label = lv_label_create(scr);
|
||||||
lv_obj_set_width(label, EXAMPLE_LCD_H_RES);
|
lv_obj_set_width(label, EXAMPLE_LCD_H_RES);
|
||||||
@ -236,7 +248,7 @@ static void app_main_display(void)
|
|||||||
#else
|
#else
|
||||||
lv_label_set_text(label, LV_SYMBOL_BELL" Hello world Espressif and LVGL "LV_SYMBOL_BELL"\n "LV_SYMBOL_WARNING" For simplier initialization, use BSP "LV_SYMBOL_WARNING);
|
lv_label_set_text(label, LV_SYMBOL_BELL" Hello world Espressif and LVGL "LV_SYMBOL_BELL"\n "LV_SYMBOL_WARNING" For simplier initialization, use BSP "LV_SYMBOL_WARNING);
|
||||||
#endif
|
#endif
|
||||||
lv_obj_align(label, LV_ALIGN_CENTER, 0, -30);
|
lv_obj_align(label, LV_ALIGN_CENTER, 0, 20);
|
||||||
|
|
||||||
/* Button */
|
/* Button */
|
||||||
lv_obj_t *btn = lv_btn_create(scr);
|
lv_obj_t *btn = lv_btn_create(scr);
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
version: "1.5.0"
|
version: "2.4.3"
|
||||||
description: ESP LVGL port
|
description: ESP LVGL port
|
||||||
url: https://github.com/espressif/esp-bsp/tree/master/components/esp_lvgl_port
|
url: https://github.com/espressif/esp-bsp/tree/master/components/esp_lvgl_port
|
||||||
dependencies:
|
dependencies:
|
||||||
|
|||||||
@ -12,11 +12,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
#include "esp_lvgl_port_button.h"
|
#include "lvgl.h"
|
||||||
#include "esp_lvgl_port_compatibility.h"
|
|
||||||
#include "esp_lvgl_port_disp.h"
|
#include "esp_lvgl_port_disp.h"
|
||||||
#include "esp_lvgl_port_knob.h"
|
|
||||||
#include "esp_lvgl_port_touch.h"
|
#include "esp_lvgl_port_touch.h"
|
||||||
|
#include "esp_lvgl_port_knob.h"
|
||||||
|
#include "esp_lvgl_port_button.h"
|
||||||
#include "esp_lvgl_port_usbhid.h"
|
#include "esp_lvgl_port_usbhid.h"
|
||||||
|
|
||||||
#if LVGL_VERSION_MAJOR == 8
|
#if LVGL_VERSION_MAJOR == 8
|
||||||
@ -27,6 +27,23 @@
|
|||||||
extern "C" {
|
extern "C" {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief LVGL Port task event type
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
LVGL_PORT_EVENT_DISPLAY = 1,
|
||||||
|
LVGL_PORT_EVENT_TOUCH = 2,
|
||||||
|
LVGL_PORT_EVENT_USER = 99,
|
||||||
|
} lvgl_port_event_type_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief LVGL Port task events
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
lvgl_port_event_type_t type;
|
||||||
|
void *param;
|
||||||
|
} lvgl_port_event_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Init configuration structure
|
* @brief Init configuration structure
|
||||||
*/
|
*/
|
||||||
@ -72,6 +89,7 @@ esp_err_t lvgl_port_init(const lvgl_port_cfg_t *cfg);
|
|||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
* - ESP_OK on success
|
* - ESP_OK on success
|
||||||
|
* - ESP_ERR_TIMEOUT when stopping the LVGL task times out
|
||||||
*/
|
*/
|
||||||
esp_err_t lvgl_port_deinit(void);
|
esp_err_t lvgl_port_deinit(void);
|
||||||
|
|
||||||
@ -101,7 +119,7 @@ void lvgl_port_unlock(void);
|
|||||||
void lvgl_port_flush_ready(lv_display_t *disp);
|
void lvgl_port_flush_ready(lv_display_t *disp);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Stop lvgl task
|
* @brief Stop lvgl timer
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
@ -111,7 +129,7 @@ void lvgl_port_flush_ready(lv_display_t *disp);
|
|||||||
esp_err_t lvgl_port_stop(void);
|
esp_err_t lvgl_port_stop(void);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Resume lvgl task
|
* @brief Resume lvgl timer
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @return
|
* @return
|
||||||
@ -120,6 +138,20 @@ esp_err_t lvgl_port_stop(void);
|
|||||||
*/
|
*/
|
||||||
esp_err_t lvgl_port_resume(void);
|
esp_err_t lvgl_port_resume(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Notify LVGL task, that display need reload
|
||||||
|
*
|
||||||
|
* @note It is called from LVGL events and touch interrupts
|
||||||
|
*
|
||||||
|
* @param event event type
|
||||||
|
* @param param user param
|
||||||
|
* @return
|
||||||
|
* - ESP_OK on success
|
||||||
|
* - ESP_ERR_NOT_SUPPORTED if it is not implemented
|
||||||
|
* - ESP_ERR_INVALID_STATE if queue is not initialized (can be returned after LVGL deinit)
|
||||||
|
*/
|
||||||
|
esp_err_t lvgl_port_task_wake(lvgl_port_event_type_t event, void *param);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -19,6 +19,13 @@ extern "C" {
|
|||||||
* @brief Backward compatibility with LVGL 8
|
* @brief Backward compatibility with LVGL 8
|
||||||
*/
|
*/
|
||||||
typedef lv_disp_t lv_display_t;
|
typedef lv_disp_t lv_display_t;
|
||||||
|
typedef enum {
|
||||||
|
LV_DISPLAY_ROTATION_0 = LV_DISP_ROT_NONE,
|
||||||
|
LV_DISPLAY_ROTATION_90 = LV_DISP_ROT_90,
|
||||||
|
LV_DISPLAY_ROTATION_180 = LV_DISP_ROT_180,
|
||||||
|
LV_DISPLAY_ROTATION_270 = LV_DISP_ROT_270
|
||||||
|
} lv_disp_rotation_t;
|
||||||
|
typedef lv_disp_rotation_t lv_display_rotation_t;
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,24 +39,54 @@ typedef struct {
|
|||||||
typedef struct {
|
typedef struct {
|
||||||
esp_lcd_panel_io_handle_t io_handle; /*!< LCD panel IO handle */
|
esp_lcd_panel_io_handle_t io_handle; /*!< LCD panel IO handle */
|
||||||
esp_lcd_panel_handle_t panel_handle; /*!< LCD panel handle */
|
esp_lcd_panel_handle_t panel_handle; /*!< LCD panel handle */
|
||||||
uint32_t buffer_size; /*!< Size of the buffer for the screen in pixels */
|
esp_lcd_panel_handle_t control_handle; /*!< LCD panel control handle */
|
||||||
bool double_buffer; /*!< True, if should be allocated two buffers */
|
|
||||||
uint32_t trans_size; /*!< Allocated buffer will be in SRAM to move framebuf */
|
uint32_t buffer_size; /*!< Size of the buffer for the screen in pixels */
|
||||||
|
bool double_buffer; /*!< True, if should be allocated two buffers */
|
||||||
|
uint32_t trans_size; /*!< Allocated buffer will be in SRAM to move framebuf (optional) */
|
||||||
|
|
||||||
uint32_t hres; /*!< LCD display horizontal resolution */
|
uint32_t hres; /*!< LCD display horizontal resolution */
|
||||||
uint32_t vres; /*!< LCD display vertical resolution */
|
uint32_t vres; /*!< LCD display vertical resolution */
|
||||||
bool monochrome; /*!< True, if display is monochrome and using 1bit for 1px */
|
|
||||||
lvgl_port_rotation_cfg_t rotation; /*!< Default values of the screen rotation */
|
|
||||||
|
|
||||||
|
bool monochrome; /*!< True, if display is monochrome and using 1bit for 1px */
|
||||||
|
|
||||||
|
lvgl_port_rotation_cfg_t rotation; /*!< Default values of the screen rotation (Only HW state. Not supported for default SW rotation!) */
|
||||||
|
#if LVGL_VERSION_MAJOR >= 9
|
||||||
|
lv_color_format_t color_format; /*!< The color format of the display */
|
||||||
|
#endif
|
||||||
struct {
|
struct {
|
||||||
unsigned int buff_dma: 1; /*!< Allocated LVGL buffer will be DMA capable */
|
unsigned int buff_dma: 1; /*!< Allocated LVGL buffer will be DMA capable */
|
||||||
unsigned int buff_spiram: 1; /*!< Allocated LVGL buffer will be in PSRAM */
|
unsigned int buff_spiram: 1; /*!< Allocated LVGL buffer will be in PSRAM */
|
||||||
unsigned int sw_rotate: 1; /*!< Use software rotation (slower) */
|
unsigned int sw_rotate: 1; /*!< Use software rotation (slower) or PPA if available */
|
||||||
unsigned int swap_bytes: 1; /*!< Swap bytes in RGB656 (16-bit) before send to LCD driver */
|
#if LVGL_VERSION_MAJOR >= 9
|
||||||
|
unsigned int swap_bytes: 1; /*!< Swap bytes in RGB656 (16-bit) color format before send to LCD driver */
|
||||||
|
#endif
|
||||||
|
unsigned int full_refresh: 1;/*!< 1: Always make the whole screen redrawn */
|
||||||
|
unsigned int direct_mode: 1; /*!< 1: Use screen-sized buffers and draw to absolute coordinates */
|
||||||
} flags;
|
} flags;
|
||||||
} lvgl_port_display_cfg_t;
|
} lvgl_port_display_cfg_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Add display handling to LVGL
|
* @brief Configuration RGB display structure
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
struct {
|
||||||
|
unsigned int bb_mode: 1; /*!< 1: Use bounce buffer mode */
|
||||||
|
unsigned int avoid_tearing: 1; /*!< 1: Use internal RGB buffers as a LVGL draw buffers to avoid tearing effect, enabling this option requires over two LCD buffers and may reduce the frame rate */
|
||||||
|
} flags;
|
||||||
|
} lvgl_port_display_rgb_cfg_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Configuration MIPI-DSI display structure
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
struct {
|
||||||
|
unsigned int avoid_tearing: 1; /*!< 1: Use internal MIPI-DSI buffers as a LVGL draw buffers to avoid tearing effect, enabling this option requires over two LCD buffers and may reduce the frame rate */
|
||||||
|
} flags;
|
||||||
|
} lvgl_port_display_dsi_cfg_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add I2C/SPI/I8080 display handling to LVGL
|
||||||
*
|
*
|
||||||
* @note Allocated memory in this function is not free in deinit. You must call lvgl_port_remove_disp for free all memory!
|
* @note Allocated memory in this function is not free in deinit. You must call lvgl_port_remove_disp for free all memory!
|
||||||
*
|
*
|
||||||
@ -65,6 +95,28 @@ typedef struct {
|
|||||||
*/
|
*/
|
||||||
lv_display_t *lvgl_port_add_disp(const lvgl_port_display_cfg_t *disp_cfg);
|
lv_display_t *lvgl_port_add_disp(const lvgl_port_display_cfg_t *disp_cfg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add MIPI-DSI display handling to LVGL
|
||||||
|
*
|
||||||
|
* @note Allocated memory in this function is not free in deinit. You must call lvgl_port_remove_disp for free all memory!
|
||||||
|
*
|
||||||
|
* @param disp_cfg Display configuration structure
|
||||||
|
* @param dsi_cfg MIPI-DSI display specific configuration structure
|
||||||
|
* @return Pointer to LVGL display or NULL when error occurred
|
||||||
|
*/
|
||||||
|
lv_display_t *lvgl_port_add_disp_dsi(const lvgl_port_display_cfg_t *disp_cfg, const lvgl_port_display_dsi_cfg_t *dsi_cfg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Add RGB display handling to LVGL
|
||||||
|
*
|
||||||
|
* @note Allocated memory in this function is not free in deinit. You must call lvgl_port_remove_disp for free all memory!
|
||||||
|
*
|
||||||
|
* @param disp_cfg Display configuration structure
|
||||||
|
* @param rgb_cfg RGB display specific configuration structure
|
||||||
|
* @return Pointer to LVGL display or NULL when error occurred
|
||||||
|
*/
|
||||||
|
lv_display_t *lvgl_port_add_disp_rgb(const lvgl_port_display_cfg_t *disp_cfg, const lvgl_port_display_rgb_cfg_t *rgb_cfg);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Remove display handling from LVGL
|
* @brief Remove display handling from LVGL
|
||||||
*
|
*
|
||||||
|
|||||||
@ -15,6 +15,9 @@
|
|||||||
#include "lvgl.h"
|
#include "lvgl.h"
|
||||||
|
|
||||||
#if __has_include ("iot_knob.h")
|
#if __has_include ("iot_knob.h")
|
||||||
|
#if !__has_include("iot_button.h")
|
||||||
|
#error LVLG Knob requires button component. Please add it with idf.py add-dependency espressif/button
|
||||||
|
#endif
|
||||||
#include "iot_knob.h"
|
#include "iot_knob.h"
|
||||||
#include "iot_button.h"
|
#include "iot_button.h"
|
||||||
#define ESP_LVGL_PORT_KNOB_COMPONENT 1
|
#define ESP_LVGL_PORT_KNOB_COMPONENT 1
|
||||||
|
|||||||
90
Libraries/esp_lvgl_port/include/esp_lvgl_port_lv_blend.h
Normal file
90
Libraries/esp_lvgl_port/include/esp_lvgl_port_lv_blend.h
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* INCLUDES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
#if !CONFIG_LV_DRAW_SW_ASM_CUSTOM
|
||||||
|
#warning "esp_lvgl_port_lv_blend.h included, but CONFIG_LV_DRAW_SW_ASM_CUSTOM not set. Assembly rendering not used"
|
||||||
|
#else
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* DEFINES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_COLOR_BLEND_TO_ARGB8888
|
||||||
|
#define LV_DRAW_SW_COLOR_BLEND_TO_ARGB8888(dsc) \
|
||||||
|
_lv_color_blend_to_argb8888_esp(dsc)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_COLOR_BLEND_TO_RGB565
|
||||||
|
#define LV_DRAW_SW_COLOR_BLEND_TO_RGB565(dsc) \
|
||||||
|
_lv_color_blend_to_rgb565_esp(dsc)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* TYPEDEFS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t opa;
|
||||||
|
void *dst_buf;
|
||||||
|
uint32_t dst_w;
|
||||||
|
uint32_t dst_h;
|
||||||
|
uint32_t dst_stride;
|
||||||
|
const void *src_buf;
|
||||||
|
uint32_t src_stride;
|
||||||
|
const lv_opa_t *mask_buf;
|
||||||
|
uint32_t mask_stride;
|
||||||
|
} asm_dsc_t;
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* GLOBAL PROTOTYPES
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
extern int lv_color_blend_to_argb8888_esp(asm_dsc_t *asm_dsc);
|
||||||
|
|
||||||
|
static inline lv_result_t _lv_color_blend_to_argb8888_esp(_lv_draw_sw_blend_fill_dsc_t *dsc)
|
||||||
|
{
|
||||||
|
asm_dsc_t asm_dsc = {
|
||||||
|
.dst_buf = dsc->dest_buf,
|
||||||
|
.dst_w = dsc->dest_w,
|
||||||
|
.dst_h = dsc->dest_h,
|
||||||
|
.dst_stride = dsc->dest_stride,
|
||||||
|
.src_buf = &dsc->color,
|
||||||
|
};
|
||||||
|
|
||||||
|
return lv_color_blend_to_argb8888_esp(&asm_dsc);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern int lv_color_blend_to_rgb565_esp(asm_dsc_t *asm_dsc);
|
||||||
|
|
||||||
|
static inline lv_result_t _lv_color_blend_to_rgb565_esp(_lv_draw_sw_blend_fill_dsc_t *dsc)
|
||||||
|
{
|
||||||
|
asm_dsc_t asm_dsc = {
|
||||||
|
.dst_buf = dsc->dest_buf,
|
||||||
|
.dst_w = dsc->dest_w,
|
||||||
|
.dst_h = dsc->dest_h,
|
||||||
|
.dst_stride = dsc->dest_stride,
|
||||||
|
.src_buf = &dsc->color,
|
||||||
|
};
|
||||||
|
|
||||||
|
return lv_color_blend_to_rgb565_esp(&asm_dsc);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // CONFIG_LV_DRAW_SW_ASM_CUSTOM
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /*extern "C"*/
|
||||||
|
#endif
|
||||||
47
Libraries/esp_lvgl_port/priv_include/esp_lvgl_port_priv.h
Normal file
47
Libraries/esp_lvgl_port/priv_include/esp_lvgl_port_priv.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file
|
||||||
|
* @brief ESP LVGL port private
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Rotation configuration
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
LVGL_PORT_DISP_TYPE_OTHER,
|
||||||
|
LVGL_PORT_DISP_TYPE_DSI,
|
||||||
|
LVGL_PORT_DISP_TYPE_RGB,
|
||||||
|
} lvgl_port_disp_type_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Rotation configuration
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
unsigned int avoid_tearing: 1; /*!< Use internal RGB buffers as a LVGL draw buffers to avoid tearing effect */
|
||||||
|
} lvgl_port_disp_priv_cfg_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Notify LVGL task
|
||||||
|
*
|
||||||
|
* @note It is called from RGB vsync ready
|
||||||
|
*
|
||||||
|
* @param value notification value
|
||||||
|
* @return
|
||||||
|
* - true, whether a high priority task has been waken up by this function
|
||||||
|
*/
|
||||||
|
bool lvgl_port_task_notify(uint32_t value);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
78
Libraries/esp_lvgl_port/project_include.cmake
Normal file
78
Libraries/esp_lvgl_port/project_include.cmake
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
# lvgl_port_create_c_image
|
||||||
|
#
|
||||||
|
# Create a C array of image for using with LVGL
|
||||||
|
function(lvgl_port_create_c_image image_path output_path color_format compression)
|
||||||
|
|
||||||
|
#Get Python
|
||||||
|
idf_build_get_property(python PYTHON)
|
||||||
|
|
||||||
|
#Get LVGL version
|
||||||
|
idf_build_get_property(build_components BUILD_COMPONENTS)
|
||||||
|
if(lvgl IN_LIST build_components)
|
||||||
|
set(lvgl_name lvgl) # Local component
|
||||||
|
set(lvgl_ver $ENV{LVGL_VERSION}) # Get the version from env variable (set from LVGL v9.2)
|
||||||
|
else()
|
||||||
|
set(lvgl_name lvgl__lvgl) # Managed component
|
||||||
|
idf_component_get_property(lvgl_ver ${lvgl_name} COMPONENT_VERSION) # Get the version from esp-idf build system
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if("${lvgl_ver}" STREQUAL "")
|
||||||
|
message("Could not determine LVGL version, assuming v9.x")
|
||||||
|
set(lvgl_ver "9.0.0")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
get_filename_component(image_full_path ${image_path} ABSOLUTE)
|
||||||
|
get_filename_component(output_full_path ${output_path} ABSOLUTE)
|
||||||
|
if(NOT EXISTS ${image_full_path})
|
||||||
|
message(FATAL_ERROR "Input image (${image_full_path}) not exists!")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
message(STATUS "Generating C array image: ${image_path}")
|
||||||
|
|
||||||
|
#Create C array image by LVGL version
|
||||||
|
if(lvgl_ver VERSION_LESS "9.0.0")
|
||||||
|
|
||||||
|
if(CONFIG_LV_COLOR_16_SWAP)
|
||||||
|
set(color_format "RGB565SWAP")
|
||||||
|
else()
|
||||||
|
set(color_format "RGB565")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
execute_process(COMMAND git clone https://github.com/W-Mai/lvgl_image_converter.git "${CMAKE_BINARY_DIR}/lvgl_image_converter")
|
||||||
|
execute_process(COMMAND git checkout 9174634e9dcc1b21a63668969406897aad650f35 WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/lvgl_image_converter" OUTPUT_QUIET)
|
||||||
|
execute_process(COMMAND ${python} -m pip install --upgrade pip)
|
||||||
|
execute_process(COMMAND ${python} -m pip install pillow==10.3.0)
|
||||||
|
#execute_process(COMMAND python -m pip install -r "${CMAKE_BINARY_DIR}/lvgl_image_converter/requirements.txt")
|
||||||
|
execute_process(COMMAND ${python} "${CMAKE_BINARY_DIR}/lvgl_image_converter/lv_img_conv.py"
|
||||||
|
-ff C
|
||||||
|
-f true_color_alpha
|
||||||
|
-cf ${color_format}
|
||||||
|
-o ${output_full_path}
|
||||||
|
${image_full_path})
|
||||||
|
else()
|
||||||
|
idf_component_get_property(lvgl_dir ${lvgl_name} COMPONENT_DIR)
|
||||||
|
get_filename_component(script_path ${lvgl_dir}/scripts/LVGLImage.py ABSOLUTE)
|
||||||
|
set(lvglimage_py ${python} ${script_path})
|
||||||
|
|
||||||
|
#Install dependencies
|
||||||
|
execute_process(COMMAND ${python} -m pip install pypng lz4 OUTPUT_QUIET)
|
||||||
|
|
||||||
|
execute_process(COMMAND ${lvglimage_py}
|
||||||
|
--ofmt=C
|
||||||
|
--cf=${color_format}
|
||||||
|
--compress=${compression}
|
||||||
|
-o ${output_full_path}
|
||||||
|
${image_full_path})
|
||||||
|
endif()
|
||||||
|
|
||||||
|
endfunction()
|
||||||
|
|
||||||
|
# lvgl_port_add_images
|
||||||
|
#
|
||||||
|
# Add all images to build
|
||||||
|
function(lvgl_port_add_images component output_path)
|
||||||
|
#Add images to sources
|
||||||
|
file(GLOB_RECURSE IMAGE_SOURCES ${output_path}*.c)
|
||||||
|
target_sources(${component} PRIVATE ${IMAGE_SOURCES})
|
||||||
|
idf_build_set_property(COMPILE_OPTIONS "-DLV_LVGL_H_INCLUDE_SIMPLE=1" APPEND)
|
||||||
|
endfunction()
|
||||||
@ -4,6 +4,7 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
#include "esp_system.h"
|
#include "esp_system.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
@ -13,16 +14,21 @@
|
|||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "freertos/semphr.h"
|
#include "freertos/semphr.h"
|
||||||
#include "esp_lvgl_port.h"
|
#include "esp_lvgl_port.h"
|
||||||
|
#include "esp_lvgl_port_priv.h"
|
||||||
#include "lvgl.h"
|
#include "lvgl.h"
|
||||||
|
|
||||||
static const char *TAG = "LVGL";
|
static const char *TAG = "LVGL";
|
||||||
|
|
||||||
|
#define ESP_LVGL_PORT_TASK_MUX_DELAY_MS 10000
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Types definitions
|
* Types definitions
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
typedef struct lvgl_port_ctx_s {
|
typedef struct lvgl_port_ctx_s {
|
||||||
|
TaskHandle_t lvgl_task;
|
||||||
SemaphoreHandle_t lvgl_mux;
|
SemaphoreHandle_t lvgl_mux;
|
||||||
|
SemaphoreHandle_t task_mux;
|
||||||
esp_timer_handle_t tick_timer;
|
esp_timer_handle_t tick_timer;
|
||||||
bool running;
|
bool running;
|
||||||
int task_max_sleep_ms;
|
int task_max_sleep_ms;
|
||||||
@ -63,14 +69,18 @@ esp_err_t lvgl_port_init(const lvgl_port_cfg_t *cfg)
|
|||||||
if (lvgl_port_ctx.task_max_sleep_ms == 0) {
|
if (lvgl_port_ctx.task_max_sleep_ms == 0) {
|
||||||
lvgl_port_ctx.task_max_sleep_ms = 500;
|
lvgl_port_ctx.task_max_sleep_ms = 500;
|
||||||
}
|
}
|
||||||
|
/* LVGL semaphore */
|
||||||
lvgl_port_ctx.lvgl_mux = xSemaphoreCreateRecursiveMutex();
|
lvgl_port_ctx.lvgl_mux = xSemaphoreCreateRecursiveMutex();
|
||||||
ESP_GOTO_ON_FALSE(lvgl_port_ctx.lvgl_mux, ESP_ERR_NO_MEM, err, TAG, "Create LVGL mutex fail!");
|
ESP_GOTO_ON_FALSE(lvgl_port_ctx.lvgl_mux, ESP_ERR_NO_MEM, err, TAG, "Create LVGL mutex fail!");
|
||||||
|
/* Task semaphore */
|
||||||
|
lvgl_port_ctx.task_mux = xSemaphoreCreateMutex();
|
||||||
|
ESP_GOTO_ON_FALSE(lvgl_port_ctx.task_mux, ESP_ERR_NO_MEM, err, TAG, "Create LVGL task sem fail!");
|
||||||
|
|
||||||
BaseType_t res;
|
BaseType_t res;
|
||||||
if (cfg->task_affinity < 0) {
|
if (cfg->task_affinity < 0) {
|
||||||
res = xTaskCreate(lvgl_port_task, "LVGL task", cfg->task_stack, NULL, cfg->task_priority, NULL);
|
res = xTaskCreate(lvgl_port_task, "taskLVGL", cfg->task_stack, NULL, cfg->task_priority, &lvgl_port_ctx.lvgl_task);
|
||||||
} else {
|
} else {
|
||||||
res = xTaskCreatePinnedToCore(lvgl_port_task, "LVGL task", cfg->task_stack, NULL, cfg->task_priority, NULL, cfg->task_affinity);
|
res = xTaskCreatePinnedToCore(lvgl_port_task, "taskLVGL", cfg->task_stack, NULL, cfg->task_priority, &lvgl_port_ctx.lvgl_task, cfg->task_affinity);
|
||||||
}
|
}
|
||||||
ESP_GOTO_ON_FALSE(res == pdPASS, ESP_FAIL, err, TAG, "Create LVGL task fail!");
|
ESP_GOTO_ON_FALSE(res == pdPASS, ESP_FAIL, err, TAG, "Create LVGL task fail!");
|
||||||
|
|
||||||
@ -118,10 +128,17 @@ esp_err_t lvgl_port_deinit(void)
|
|||||||
/* Stop running task */
|
/* Stop running task */
|
||||||
if (lvgl_port_ctx.running) {
|
if (lvgl_port_ctx.running) {
|
||||||
lvgl_port_ctx.running = false;
|
lvgl_port_ctx.running = false;
|
||||||
} else {
|
|
||||||
lvgl_port_task_deinit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Wait for stop task */
|
||||||
|
if (xSemaphoreTake(lvgl_port_ctx.task_mux, pdMS_TO_TICKS(ESP_LVGL_PORT_TASK_MUX_DELAY_MS)) != pdTRUE) {
|
||||||
|
ESP_LOGE(TAG, "Failed to stop LVGL task");
|
||||||
|
return ESP_ERR_TIMEOUT;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "Stopped LVGL task");
|
||||||
|
|
||||||
|
lvgl_port_task_deinit();
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,6 +156,26 @@ void lvgl_port_unlock(void)
|
|||||||
xSemaphoreGiveRecursive(lvgl_port_ctx.lvgl_mux);
|
xSemaphoreGiveRecursive(lvgl_port_ctx.lvgl_mux);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_err_t lvgl_port_task_wake(lvgl_port_event_type_t event, void *param)
|
||||||
|
{
|
||||||
|
ESP_LOGE(TAG, "Task wake is not supported, when used LVGL8!");
|
||||||
|
return ESP_ERR_NOT_SUPPORTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
IRAM_ATTR bool lvgl_port_task_notify(uint32_t value)
|
||||||
|
{
|
||||||
|
BaseType_t need_yield = pdFALSE;
|
||||||
|
|
||||||
|
// Notify LVGL task
|
||||||
|
if (xPortInIsrContext() == pdTRUE) {
|
||||||
|
xTaskNotifyFromISR(lvgl_port_ctx.lvgl_task, value, eNoAction, &need_yield);
|
||||||
|
} else {
|
||||||
|
xTaskNotify(lvgl_port_ctx.lvgl_task, value, eNoAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (need_yield == pdTRUE);
|
||||||
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Private functions
|
* Private functions
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
@ -147,6 +184,13 @@ static void lvgl_port_task(void *arg)
|
|||||||
{
|
{
|
||||||
uint32_t task_delay_ms = lvgl_port_ctx.task_max_sleep_ms;
|
uint32_t task_delay_ms = lvgl_port_ctx.task_max_sleep_ms;
|
||||||
|
|
||||||
|
/* Take the task semaphore */
|
||||||
|
if (xSemaphoreTake(lvgl_port_ctx.task_mux, 0) != pdTRUE) {
|
||||||
|
ESP_LOGE(TAG, "Failed to take LVGL task sem");
|
||||||
|
lvgl_port_task_deinit();
|
||||||
|
vTaskDelete( NULL );
|
||||||
|
}
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Starting LVGL task");
|
ESP_LOGI(TAG, "Starting LVGL task");
|
||||||
lvgl_port_ctx.running = true;
|
lvgl_port_ctx.running = true;
|
||||||
while (lvgl_port_ctx.running) {
|
while (lvgl_port_ctx.running) {
|
||||||
@ -154,15 +198,16 @@ static void lvgl_port_task(void *arg)
|
|||||||
task_delay_ms = lv_timer_handler();
|
task_delay_ms = lv_timer_handler();
|
||||||
lvgl_port_unlock();
|
lvgl_port_unlock();
|
||||||
}
|
}
|
||||||
if ((task_delay_ms > lvgl_port_ctx.task_max_sleep_ms) || (1 == task_delay_ms)) {
|
if (task_delay_ms > lvgl_port_ctx.task_max_sleep_ms) {
|
||||||
task_delay_ms = lvgl_port_ctx.task_max_sleep_ms;
|
task_delay_ms = lvgl_port_ctx.task_max_sleep_ms;
|
||||||
} else if (task_delay_ms < 1) {
|
} else if (task_delay_ms < 5) {
|
||||||
task_delay_ms = 1;
|
task_delay_ms = 5;
|
||||||
}
|
}
|
||||||
vTaskDelay(pdMS_TO_TICKS(task_delay_ms));
|
vTaskDelay(pdMS_TO_TICKS(task_delay_ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
lvgl_port_task_deinit();
|
/* Give semaphore back */
|
||||||
|
xSemaphoreGive(lvgl_port_ctx.task_mux);
|
||||||
|
|
||||||
/* Close task */
|
/* Close task */
|
||||||
vTaskDelete( NULL );
|
vTaskDelete( NULL );
|
||||||
@ -173,6 +218,9 @@ static void lvgl_port_task_deinit(void)
|
|||||||
if (lvgl_port_ctx.lvgl_mux) {
|
if (lvgl_port_ctx.lvgl_mux) {
|
||||||
vSemaphoreDelete(lvgl_port_ctx.lvgl_mux);
|
vSemaphoreDelete(lvgl_port_ctx.lvgl_mux);
|
||||||
}
|
}
|
||||||
|
if (lvgl_port_ctx.task_mux) {
|
||||||
|
vSemaphoreDelete(lvgl_port_ctx.task_mux);
|
||||||
|
}
|
||||||
memset(&lvgl_port_ctx, 0, sizeof(lvgl_port_ctx));
|
memset(&lvgl_port_ctx, 0, sizeof(lvgl_port_ctx));
|
||||||
#if LV_ENABLE_GC || !LV_MEM_CUSTOM
|
#if LV_ENABLE_GC || !LV_MEM_CUSTOM
|
||||||
/* Deinitialize LVGL */
|
/* Deinitialize LVGL */
|
||||||
|
|||||||
@ -4,13 +4,27 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
#include "esp_check.h"
|
#include "esp_check.h"
|
||||||
#include "esp_heap_caps.h"
|
#include "esp_heap_caps.h"
|
||||||
|
#include "esp_idf_version.h"
|
||||||
#include "esp_lcd_panel_io.h"
|
#include "esp_lcd_panel_io.h"
|
||||||
#include "esp_lcd_panel_ops.h"
|
#include "esp_lcd_panel_ops.h"
|
||||||
#include "esp_lvgl_port.h"
|
#include "esp_lvgl_port.h"
|
||||||
|
#include "esp_lvgl_port_priv.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "freertos/semphr.h"
|
||||||
|
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32S3 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||||
|
#include "esp_lcd_panel_rgb.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (CONFIG_IDF_TARGET_ESP32P4 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0))
|
||||||
|
#include "esp_lcd_mipi_dsi.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#if (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 4, 4)) || (ESP_IDF_VERSION == ESP_IDF_VERSION_VAL(5, 0, 0))
|
#if (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 4, 4)) || (ESP_IDF_VERSION == ESP_IDF_VERSION_VAL(5, 0, 0))
|
||||||
#define LVGL_PORT_HANDLE_FLUSH_READY 0
|
#define LVGL_PORT_HANDLE_FLUSH_READY 0
|
||||||
@ -25,8 +39,10 @@ static const char *TAG = "LVGL";
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
lvgl_port_disp_type_t disp_type; /* Display type */
|
||||||
esp_lcd_panel_io_handle_t io_handle; /* LCD panel IO handle */
|
esp_lcd_panel_io_handle_t io_handle; /* LCD panel IO handle */
|
||||||
esp_lcd_panel_handle_t panel_handle; /* LCD panel handle */
|
esp_lcd_panel_handle_t panel_handle; /* LCD panel handle */
|
||||||
|
esp_lcd_panel_handle_t control_handle; /* LCD panel control handle */
|
||||||
lvgl_port_rotation_cfg_t rotation; /* Default values of the screen rotation */
|
lvgl_port_rotation_cfg_t rotation; /* Default values of the screen rotation */
|
||||||
lv_disp_drv_t disp_drv; /* LVGL display driver */
|
lv_disp_drv_t disp_drv; /* LVGL display driver */
|
||||||
lv_color_t *trans_buf; /* Buffer send to driver */
|
lv_color_t *trans_buf; /* Buffer send to driver */
|
||||||
@ -37,9 +53,17 @@ typedef struct {
|
|||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Function definitions
|
* Function definitions
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
static lv_disp_t *lvgl_port_add_disp_priv(const lvgl_port_display_cfg_t *disp_cfg, const lvgl_port_disp_priv_cfg_t *priv_cfg);
|
||||||
|
static lvgl_port_display_ctx_t *lvgl_port_get_display_ctx(lv_disp_t *disp);
|
||||||
#if LVGL_PORT_HANDLE_FLUSH_READY
|
#if LVGL_PORT_HANDLE_FLUSH_READY
|
||||||
static bool lvgl_port_flush_ready_callback(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx);
|
static bool lvgl_port_flush_io_ready_callback(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx);
|
||||||
|
#if (CONFIG_IDF_TARGET_ESP32S3 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0))
|
||||||
|
static bool lvgl_port_flush_rgb_vsync_ready_callback(esp_lcd_panel_handle_t panel_io, const esp_lcd_rgb_panel_event_data_t *edata, void *user_ctx);
|
||||||
|
#endif
|
||||||
|
#if (CONFIG_IDF_TARGET_ESP32P4 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0))
|
||||||
|
static bool lvgl_port_flush_dpi_panel_ready_callback(esp_lcd_panel_handle_t panel_io, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx);
|
||||||
|
static bool lvgl_port_flush_dpi_vsync_ready_callback(esp_lcd_panel_handle_t panel_io, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx);
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
static void lvgl_port_flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map);
|
static void lvgl_port_flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map);
|
||||||
static void lvgl_port_update_callback(lv_disp_drv_t *drv);
|
static void lvgl_port_update_callback(lv_disp_drv_t *drv);
|
||||||
@ -51,111 +75,90 @@ static void lvgl_port_pix_monochrome_callback(lv_disp_drv_t *drv, uint8_t *buf,
|
|||||||
|
|
||||||
lv_disp_t *lvgl_port_add_disp(const lvgl_port_display_cfg_t *disp_cfg)
|
lv_disp_t *lvgl_port_add_disp(const lvgl_port_display_cfg_t *disp_cfg)
|
||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
lv_disp_t *disp = lvgl_port_add_disp_priv(disp_cfg, NULL);
|
||||||
lv_disp_t *disp = NULL;
|
|
||||||
lv_color_t *buf1 = NULL;
|
|
||||||
lv_color_t *buf2 = NULL;
|
|
||||||
lv_color_t *buf3 = NULL;
|
|
||||||
SemaphoreHandle_t trans_sem = NULL;
|
|
||||||
assert(disp_cfg != NULL);
|
|
||||||
assert(disp_cfg->io_handle != NULL);
|
|
||||||
assert(disp_cfg->panel_handle != NULL);
|
|
||||||
assert(disp_cfg->buffer_size > 0);
|
|
||||||
assert(disp_cfg->hres > 0);
|
|
||||||
assert(disp_cfg->vres > 0);
|
|
||||||
|
|
||||||
/* Display context */
|
if (disp != NULL) {
|
||||||
lvgl_port_display_ctx_t *disp_ctx = malloc(sizeof(lvgl_port_display_ctx_t));
|
lvgl_port_display_ctx_t *disp_ctx = lvgl_port_get_display_ctx(disp);
|
||||||
ESP_GOTO_ON_FALSE(disp_ctx, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for display context allocation!");
|
/* Set display type */
|
||||||
disp_ctx->io_handle = disp_cfg->io_handle;
|
disp_ctx->disp_type = LVGL_PORT_DISP_TYPE_OTHER;
|
||||||
disp_ctx->panel_handle = disp_cfg->panel_handle;
|
|
||||||
disp_ctx->rotation.swap_xy = disp_cfg->rotation.swap_xy;
|
|
||||||
disp_ctx->rotation.mirror_x = disp_cfg->rotation.mirror_x;
|
|
||||||
disp_ctx->rotation.mirror_y = disp_cfg->rotation.mirror_y;
|
|
||||||
disp_ctx->trans_size = disp_cfg->trans_size;
|
|
||||||
|
|
||||||
uint32_t buff_caps = MALLOC_CAP_DEFAULT;
|
assert(disp_ctx->io_handle != NULL);
|
||||||
if (disp_cfg->flags.buff_dma && disp_cfg->flags.buff_spiram && (0 == disp_cfg->trans_size)) {
|
|
||||||
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "Alloc DMA capable buffer in SPIRAM is not supported!");
|
|
||||||
} else if (disp_cfg->flags.buff_dma) {
|
|
||||||
buff_caps = MALLOC_CAP_DMA;
|
|
||||||
} else if (disp_cfg->flags.buff_spiram) {
|
|
||||||
buff_caps = MALLOC_CAP_SPIRAM;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (disp_cfg->trans_size) {
|
|
||||||
buf3 = heap_caps_malloc(disp_cfg->trans_size * sizeof(lv_color_t), MALLOC_CAP_DMA);
|
|
||||||
ESP_GOTO_ON_FALSE(buf3, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for buffer(transport) allocation!");
|
|
||||||
disp_ctx->trans_buf = buf3;
|
|
||||||
|
|
||||||
trans_sem = xSemaphoreCreateCounting(1, 0);
|
|
||||||
ESP_GOTO_ON_FALSE(trans_sem, ESP_ERR_NO_MEM, err, TAG, "Failed to create transport counting Semaphore");
|
|
||||||
disp_ctx->trans_sem = trans_sem;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* alloc draw buffers used by LVGL */
|
|
||||||
/* it's recommended to choose the size of the draw buffer(s) to be at least 1/10 screen sized */
|
|
||||||
buf1 = heap_caps_malloc(disp_cfg->buffer_size * sizeof(lv_color_t), buff_caps);
|
|
||||||
ESP_GOTO_ON_FALSE(buf1, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for LVGL buffer (buf1) allocation!");
|
|
||||||
if (disp_cfg->double_buffer) {
|
|
||||||
buf2 = heap_caps_malloc(disp_cfg->buffer_size * sizeof(lv_color_t), buff_caps);
|
|
||||||
ESP_GOTO_ON_FALSE(buf2, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for LVGL buffer (buf2) allocation!");
|
|
||||||
}
|
|
||||||
lv_disp_draw_buf_t *disp_buf = malloc(sizeof(lv_disp_draw_buf_t));
|
|
||||||
ESP_GOTO_ON_FALSE(disp_buf, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for LVGL display buffer allocation!");
|
|
||||||
|
|
||||||
/* initialize LVGL draw buffers */
|
|
||||||
lv_disp_draw_buf_init(disp_buf, buf1, buf2, disp_cfg->buffer_size);
|
|
||||||
|
|
||||||
ESP_LOGD(TAG, "Register display driver to LVGL");
|
|
||||||
lv_disp_drv_init(&disp_ctx->disp_drv);
|
|
||||||
disp_ctx->disp_drv.hor_res = disp_cfg->hres;
|
|
||||||
disp_ctx->disp_drv.ver_res = disp_cfg->vres;
|
|
||||||
disp_ctx->disp_drv.flush_cb = lvgl_port_flush_callback;
|
|
||||||
disp_ctx->disp_drv.draw_buf = disp_buf;
|
|
||||||
disp_ctx->disp_drv.user_data = disp_ctx;
|
|
||||||
|
|
||||||
disp_ctx->disp_drv.sw_rotate = disp_cfg->flags.sw_rotate;
|
|
||||||
if (disp_ctx->disp_drv.sw_rotate == false) {
|
|
||||||
disp_ctx->disp_drv.drv_update_cb = lvgl_port_update_callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if LVGL_PORT_HANDLE_FLUSH_READY
|
#if LVGL_PORT_HANDLE_FLUSH_READY
|
||||||
/* Register done callback */
|
const esp_lcd_panel_io_callbacks_t cbs = {
|
||||||
const esp_lcd_panel_io_callbacks_t cbs = {
|
.on_color_trans_done = lvgl_port_flush_io_ready_callback,
|
||||||
.on_color_trans_done = lvgl_port_flush_ready_callback,
|
};
|
||||||
};
|
/* Register done callback */
|
||||||
esp_lcd_panel_io_register_event_callbacks(disp_ctx->io_handle, &cbs, &disp_ctx->disp_drv);
|
esp_lcd_panel_io_register_event_callbacks(disp_ctx->io_handle, &cbs, &disp_ctx->disp_drv);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Monochrome display settings */
|
|
||||||
if (disp_cfg->monochrome) {
|
|
||||||
/* When using monochromatic display, there must be used full bufer! */
|
|
||||||
ESP_GOTO_ON_FALSE((disp_cfg->hres * disp_cfg->vres == disp_cfg->buffer_size), ESP_ERR_INVALID_ARG, err, TAG, "Monochromatic display must using full buffer!");
|
|
||||||
|
|
||||||
disp_ctx->disp_drv.full_refresh = 1;
|
|
||||||
disp_ctx->disp_drv.set_px_cb = lvgl_port_pix_monochrome_callback;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
disp = lv_disp_drv_register(&disp_ctx->disp_drv);
|
return disp;
|
||||||
|
}
|
||||||
|
|
||||||
err:
|
lv_display_t *lvgl_port_add_disp_dsi(const lvgl_port_display_cfg_t *disp_cfg, const lvgl_port_display_dsi_cfg_t *dsi_cfg)
|
||||||
if (ret != ESP_OK) {
|
{
|
||||||
if (buf1) {
|
assert(dsi_cfg != NULL);
|
||||||
free(buf1);
|
const lvgl_port_disp_priv_cfg_t priv_cfg = {
|
||||||
|
.avoid_tearing = dsi_cfg->flags.avoid_tearing,
|
||||||
|
};
|
||||||
|
lv_disp_t *disp = lvgl_port_add_disp_priv(disp_cfg, &priv_cfg);
|
||||||
|
|
||||||
|
if (disp != NULL) {
|
||||||
|
lvgl_port_display_ctx_t *disp_ctx = lvgl_port_get_display_ctx(disp);
|
||||||
|
/* Set display type */
|
||||||
|
disp_ctx->disp_type = LVGL_PORT_DISP_TYPE_DSI;
|
||||||
|
|
||||||
|
#if (CONFIG_IDF_TARGET_ESP32P4 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0))
|
||||||
|
esp_lcd_dpi_panel_event_callbacks_t cbs = {0};
|
||||||
|
if (dsi_cfg->flags.avoid_tearing) {
|
||||||
|
cbs.on_refresh_done = lvgl_port_flush_dpi_vsync_ready_callback;
|
||||||
|
} else {
|
||||||
|
cbs.on_color_trans_done = lvgl_port_flush_dpi_panel_ready_callback;
|
||||||
}
|
}
|
||||||
if (buf2) {
|
/* Register done callback */
|
||||||
free(buf2);
|
esp_lcd_dpi_panel_register_event_callbacks(disp_ctx->panel_handle, &cbs, &disp_ctx->disp_drv);
|
||||||
}
|
#else
|
||||||
if (buf3) {
|
ESP_RETURN_ON_FALSE(false, NULL, TAG, "MIPI-DSI is supported only on ESP32P4 and from IDF 5.3!");
|
||||||
free(buf3);
|
#endif
|
||||||
}
|
}
|
||||||
if (trans_sem) {
|
|
||||||
vSemaphoreDelete(trans_sem);
|
return disp;
|
||||||
}
|
}
|
||||||
if (disp_ctx) {
|
|
||||||
free(disp_ctx);
|
lv_display_t *lvgl_port_add_disp_rgb(const lvgl_port_display_cfg_t *disp_cfg, const lvgl_port_display_rgb_cfg_t *rgb_cfg)
|
||||||
|
{
|
||||||
|
assert(rgb_cfg != NULL);
|
||||||
|
const lvgl_port_disp_priv_cfg_t priv_cfg = {
|
||||||
|
.avoid_tearing = rgb_cfg->flags.avoid_tearing,
|
||||||
|
};
|
||||||
|
lv_disp_t *disp = lvgl_port_add_disp_priv(disp_cfg, &priv_cfg);
|
||||||
|
|
||||||
|
if (disp != NULL) {
|
||||||
|
lvgl_port_display_ctx_t *disp_ctx = lvgl_port_get_display_ctx(disp);
|
||||||
|
/* Set display type */
|
||||||
|
disp_ctx->disp_type = LVGL_PORT_DISP_TYPE_RGB;
|
||||||
|
|
||||||
|
#if (CONFIG_IDF_TARGET_ESP32S3 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0))
|
||||||
|
/* Register done callback */
|
||||||
|
const esp_lcd_rgb_panel_event_callbacks_t vsync_cbs = {
|
||||||
|
.on_vsync = lvgl_port_flush_rgb_vsync_ready_callback,
|
||||||
|
};
|
||||||
|
|
||||||
|
const esp_lcd_rgb_panel_event_callbacks_t bb_cbs = {
|
||||||
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 2)
|
||||||
|
.on_bounce_frame_finish = lvgl_port_flush_rgb_vsync_ready_callback,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
if (rgb_cfg->flags.bb_mode && (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 2))) {
|
||||||
|
ESP_ERROR_CHECK(esp_lcd_rgb_panel_register_event_callbacks(disp_ctx->panel_handle, &bb_cbs, &disp_ctx->disp_drv));
|
||||||
|
} else {
|
||||||
|
ESP_ERROR_CHECK(esp_lcd_rgb_panel_register_event_callbacks(disp_ctx->panel_handle, &vsync_cbs, &disp_ctx->disp_drv));
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
ESP_RETURN_ON_FALSE(false, NULL, TAG, "RGB is supported only on ESP32S3 and from IDF 5.0!");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
return disp;
|
return disp;
|
||||||
@ -167,6 +170,9 @@ esp_err_t lvgl_port_remove_disp(lv_disp_t *disp)
|
|||||||
lv_disp_drv_t *disp_drv = disp->driver;
|
lv_disp_drv_t *disp_drv = disp->driver;
|
||||||
assert(disp_drv);
|
assert(disp_drv);
|
||||||
lvgl_port_display_ctx_t *disp_ctx = (lvgl_port_display_ctx_t *)disp_drv->user_data;
|
lvgl_port_display_ctx_t *disp_ctx = (lvgl_port_display_ctx_t *)disp_drv->user_data;
|
||||||
|
if (disp_ctx->trans_sem) {
|
||||||
|
vSemaphoreDelete(disp_ctx->trans_sem);
|
||||||
|
}
|
||||||
|
|
||||||
lv_disp_remove(disp);
|
lv_disp_remove(disp);
|
||||||
|
|
||||||
@ -201,8 +207,154 @@ void lvgl_port_flush_ready(lv_disp_t *disp)
|
|||||||
* Private functions
|
* Private functions
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
|
static lvgl_port_display_ctx_t *lvgl_port_get_display_ctx(lv_disp_t *disp)
|
||||||
|
{
|
||||||
|
assert(disp);
|
||||||
|
lv_disp_drv_t *disp_drv = disp->driver;
|
||||||
|
assert(disp_drv);
|
||||||
|
lvgl_port_display_ctx_t *disp_ctx = (lvgl_port_display_ctx_t *)disp_drv->user_data;
|
||||||
|
return disp_ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
static lv_disp_t *lvgl_port_add_disp_priv(const lvgl_port_display_cfg_t *disp_cfg, const lvgl_port_disp_priv_cfg_t *priv_cfg)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
lv_disp_t *disp = NULL;
|
||||||
|
lv_color_t *buf1 = NULL;
|
||||||
|
lv_color_t *buf2 = NULL;
|
||||||
|
lv_color_t *buf3 = NULL;
|
||||||
|
uint32_t buffer_size = 0;
|
||||||
|
SemaphoreHandle_t trans_sem = NULL;
|
||||||
|
assert(disp_cfg != NULL);
|
||||||
|
assert(disp_cfg->panel_handle != NULL);
|
||||||
|
assert(disp_cfg->buffer_size > 0);
|
||||||
|
assert(disp_cfg->hres > 0);
|
||||||
|
assert(disp_cfg->vres > 0);
|
||||||
|
|
||||||
|
/* Display context */
|
||||||
|
lvgl_port_display_ctx_t *disp_ctx = malloc(sizeof(lvgl_port_display_ctx_t));
|
||||||
|
ESP_GOTO_ON_FALSE(disp_ctx, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for display context allocation!");
|
||||||
|
memset(disp_ctx, 0, sizeof(lvgl_port_display_ctx_t));
|
||||||
|
disp_ctx->io_handle = disp_cfg->io_handle;
|
||||||
|
disp_ctx->panel_handle = disp_cfg->panel_handle;
|
||||||
|
disp_ctx->control_handle = disp_cfg->control_handle;
|
||||||
|
disp_ctx->rotation.swap_xy = disp_cfg->rotation.swap_xy;
|
||||||
|
disp_ctx->rotation.mirror_x = disp_cfg->rotation.mirror_x;
|
||||||
|
disp_ctx->rotation.mirror_y = disp_cfg->rotation.mirror_y;
|
||||||
|
disp_ctx->trans_size = disp_cfg->trans_size;
|
||||||
|
|
||||||
|
buffer_size = disp_cfg->buffer_size;
|
||||||
|
|
||||||
|
/* Use RGB internal buffers for avoid tearing effect */
|
||||||
|
if (priv_cfg && priv_cfg->avoid_tearing) {
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32S3 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||||
|
buffer_size = disp_cfg->hres * disp_cfg->vres;
|
||||||
|
ESP_GOTO_ON_ERROR(esp_lcd_rgb_panel_get_frame_buffer(disp_cfg->panel_handle, 2, (void *)&buf1, (void *)&buf2), err, TAG, "Get RGB buffers failed");
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32P4 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
|
||||||
|
buffer_size = disp_cfg->hres * disp_cfg->vres;
|
||||||
|
ESP_GOTO_ON_ERROR(esp_lcd_dpi_panel_get_frame_buffer(disp_cfg->panel_handle, 2, (void *)&buf1, (void *)&buf2), err, TAG, "Get RGB buffers failed");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
trans_sem = xSemaphoreCreateCounting(1, 0);
|
||||||
|
ESP_GOTO_ON_FALSE(trans_sem, ESP_ERR_NO_MEM, err, TAG, "Failed to create transport counting Semaphore");
|
||||||
|
disp_ctx->trans_sem = trans_sem;
|
||||||
|
} else {
|
||||||
|
uint32_t buff_caps = MALLOC_CAP_DEFAULT;
|
||||||
|
if (disp_cfg->flags.buff_dma && disp_cfg->flags.buff_spiram && (0 == disp_cfg->trans_size)) {
|
||||||
|
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "Alloc DMA capable buffer in SPIRAM is not supported!");
|
||||||
|
} else if (disp_cfg->flags.buff_dma) {
|
||||||
|
buff_caps = MALLOC_CAP_DMA;
|
||||||
|
} else if (disp_cfg->flags.buff_spiram) {
|
||||||
|
buff_caps = MALLOC_CAP_SPIRAM;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (disp_cfg->trans_size) {
|
||||||
|
buf3 = heap_caps_malloc(disp_cfg->trans_size * sizeof(lv_color_t), MALLOC_CAP_DMA);
|
||||||
|
ESP_GOTO_ON_FALSE(buf3, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for buffer(transport) allocation!");
|
||||||
|
disp_ctx->trans_buf = buf3;
|
||||||
|
|
||||||
|
trans_sem = xSemaphoreCreateCounting(1, 0);
|
||||||
|
ESP_GOTO_ON_FALSE(trans_sem, ESP_ERR_NO_MEM, err, TAG, "Failed to create transport counting Semaphore");
|
||||||
|
disp_ctx->trans_sem = trans_sem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* alloc draw buffers used by LVGL */
|
||||||
|
/* it's recommended to choose the size of the draw buffer(s) to be at least 1/10 screen sized */
|
||||||
|
buf1 = heap_caps_malloc(buffer_size * sizeof(lv_color_t), buff_caps);
|
||||||
|
ESP_GOTO_ON_FALSE(buf1, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for LVGL buffer (buf1) allocation!");
|
||||||
|
if (disp_cfg->double_buffer) {
|
||||||
|
buf2 = heap_caps_malloc(buffer_size * sizeof(lv_color_t), buff_caps);
|
||||||
|
ESP_GOTO_ON_FALSE(buf2, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for LVGL buffer (buf2) allocation!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_disp_draw_buf_t *disp_buf = malloc(sizeof(lv_disp_draw_buf_t));
|
||||||
|
ESP_GOTO_ON_FALSE(disp_buf, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for LVGL display buffer allocation!");
|
||||||
|
|
||||||
|
/* initialize LVGL draw buffers */
|
||||||
|
lv_disp_draw_buf_init(disp_buf, buf1, buf2, buffer_size);
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "Register display driver to LVGL");
|
||||||
|
lv_disp_drv_init(&disp_ctx->disp_drv);
|
||||||
|
disp_ctx->disp_drv.hor_res = disp_cfg->hres;
|
||||||
|
disp_ctx->disp_drv.ver_res = disp_cfg->vres;
|
||||||
|
disp_ctx->disp_drv.flush_cb = lvgl_port_flush_callback;
|
||||||
|
disp_ctx->disp_drv.draw_buf = disp_buf;
|
||||||
|
disp_ctx->disp_drv.user_data = disp_ctx;
|
||||||
|
|
||||||
|
disp_ctx->disp_drv.sw_rotate = disp_cfg->flags.sw_rotate;
|
||||||
|
if (disp_ctx->disp_drv.sw_rotate == false) {
|
||||||
|
disp_ctx->disp_drv.drv_update_cb = lvgl_port_update_callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Monochrome display settings */
|
||||||
|
if (disp_cfg->monochrome) {
|
||||||
|
/* When using monochromatic display, there must be used full bufer! */
|
||||||
|
ESP_GOTO_ON_FALSE((disp_cfg->hres * disp_cfg->vres == buffer_size), ESP_ERR_INVALID_ARG, err, TAG, "Monochromatic display must using full buffer!");
|
||||||
|
|
||||||
|
disp_ctx->disp_drv.full_refresh = 1;
|
||||||
|
disp_ctx->disp_drv.set_px_cb = lvgl_port_pix_monochrome_callback;
|
||||||
|
} else if (disp_cfg->flags.direct_mode) {
|
||||||
|
/* When using direct_mode, there must be used full bufer! */
|
||||||
|
ESP_GOTO_ON_FALSE((disp_cfg->hres * disp_cfg->vres == buffer_size), ESP_ERR_INVALID_ARG, err, TAG, "Direct mode must using full buffer!");
|
||||||
|
|
||||||
|
disp_ctx->disp_drv.direct_mode = 1;
|
||||||
|
} else if (disp_cfg->flags.full_refresh) {
|
||||||
|
/* When using full_refresh, there must be used full bufer! */
|
||||||
|
ESP_GOTO_ON_FALSE((disp_cfg->hres * disp_cfg->vres == buffer_size), ESP_ERR_INVALID_ARG, err, TAG, "Full refresh must using full buffer!");
|
||||||
|
|
||||||
|
disp_ctx->disp_drv.full_refresh = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
disp = lv_disp_drv_register(&disp_ctx->disp_drv);
|
||||||
|
|
||||||
|
/* Apply rotation from initial display configuration */
|
||||||
|
lvgl_port_update_callback(&disp_ctx->disp_drv);
|
||||||
|
|
||||||
|
err:
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
if (buf1) {
|
||||||
|
free(buf1);
|
||||||
|
}
|
||||||
|
if (buf2) {
|
||||||
|
free(buf2);
|
||||||
|
}
|
||||||
|
if (buf3) {
|
||||||
|
free(buf3);
|
||||||
|
}
|
||||||
|
if (trans_sem) {
|
||||||
|
vSemaphoreDelete(trans_sem);
|
||||||
|
}
|
||||||
|
if (disp_ctx) {
|
||||||
|
free(disp_ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return disp;
|
||||||
|
}
|
||||||
|
|
||||||
#if LVGL_PORT_HANDLE_FLUSH_READY
|
#if LVGL_PORT_HANDLE_FLUSH_READY
|
||||||
static bool lvgl_port_flush_ready_callback(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx)
|
static bool lvgl_port_flush_io_ready_callback(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx)
|
||||||
{
|
{
|
||||||
BaseType_t taskAwake = pdFALSE;
|
BaseType_t taskAwake = pdFALSE;
|
||||||
|
|
||||||
@ -218,6 +370,59 @@ static bool lvgl_port_flush_ready_callback(esp_lcd_panel_io_handle_t panel_io, e
|
|||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if (CONFIG_IDF_TARGET_ESP32P4 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0))
|
||||||
|
static bool lvgl_port_flush_dpi_panel_ready_callback(esp_lcd_panel_handle_t panel_io, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx)
|
||||||
|
{
|
||||||
|
BaseType_t taskAwake = pdFALSE;
|
||||||
|
|
||||||
|
lv_disp_drv_t *disp_drv = (lv_disp_drv_t *)user_ctx;
|
||||||
|
assert(disp_drv != NULL);
|
||||||
|
lvgl_port_display_ctx_t *disp_ctx = disp_drv->user_data;
|
||||||
|
assert(disp_ctx != NULL);
|
||||||
|
lv_disp_flush_ready(disp_drv);
|
||||||
|
|
||||||
|
if (disp_ctx->trans_size && disp_ctx->trans_sem) {
|
||||||
|
xSemaphoreGiveFromISR(disp_ctx->trans_sem, &taskAwake);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool lvgl_port_flush_dpi_vsync_ready_callback(esp_lcd_panel_handle_t panel_io, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx)
|
||||||
|
{
|
||||||
|
BaseType_t need_yield = pdFALSE;
|
||||||
|
|
||||||
|
lv_disp_drv_t *disp_drv = (lv_disp_drv_t *)user_ctx;
|
||||||
|
assert(disp_drv != NULL);
|
||||||
|
lvgl_port_display_ctx_t *disp_ctx = disp_drv->user_data;
|
||||||
|
assert(disp_ctx != NULL);
|
||||||
|
|
||||||
|
if (disp_ctx->trans_sem) {
|
||||||
|
xSemaphoreGiveFromISR(disp_ctx->trans_sem, &need_yield);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (need_yield == pdTRUE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (CONFIG_IDF_TARGET_ESP32S3 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0))
|
||||||
|
static bool lvgl_port_flush_rgb_vsync_ready_callback(esp_lcd_panel_handle_t panel_io, const esp_lcd_rgb_panel_event_data_t *edata, void *user_ctx)
|
||||||
|
{
|
||||||
|
BaseType_t need_yield = pdFALSE;
|
||||||
|
|
||||||
|
lv_disp_drv_t *disp_drv = (lv_disp_drv_t *)user_ctx;
|
||||||
|
assert(disp_drv != NULL);
|
||||||
|
lvgl_port_display_ctx_t *disp_ctx = disp_drv->user_data;
|
||||||
|
assert(disp_ctx != NULL);
|
||||||
|
|
||||||
|
if (disp_ctx->trans_sem) {
|
||||||
|
xSemaphoreGiveFromISR(disp_ctx->trans_sem, &need_yield);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (need_yield == pdTRUE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void lvgl_port_flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
|
static void lvgl_port_flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
|
||||||
@ -248,8 +453,22 @@ static void lvgl_port_flush_callback(lv_disp_drv_t *drv, const lv_area_t *area,
|
|||||||
lv_color_t *from = color_map;
|
lv_color_t *from = color_map;
|
||||||
lv_color_t *to = NULL;
|
lv_color_t *to = NULL;
|
||||||
|
|
||||||
if (0 == disp_ctx->trans_size) {
|
if (disp_ctx->trans_size == 0) {
|
||||||
esp_lcd_panel_draw_bitmap(disp_ctx->panel_handle, x_start, y_start, x_end + 1, y_end + 1, color_map);
|
if ((disp_ctx->disp_type == LVGL_PORT_DISP_TYPE_RGB || disp_ctx->disp_type == LVGL_PORT_DISP_TYPE_DSI) && (drv->direct_mode || drv->full_refresh)) {
|
||||||
|
if (lv_disp_flush_is_last(drv)) {
|
||||||
|
/* If the interface is I80 or SPI, this step cannot be used for drawing. */
|
||||||
|
esp_lcd_panel_draw_bitmap(disp_ctx->panel_handle, x_start, y_start, x_end + 1, y_end + 1, color_map);
|
||||||
|
/* Waiting for the last frame buffer to complete transmission */
|
||||||
|
xSemaphoreTake(disp_ctx->trans_sem, 0);
|
||||||
|
xSemaphoreTake(disp_ctx->trans_sem, portMAX_DELAY);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
esp_lcd_panel_draw_bitmap(disp_ctx->panel_handle, x_start, y_start, x_end + 1, y_end + 1, color_map);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (disp_ctx->disp_type == LVGL_PORT_DISP_TYPE_RGB || (disp_ctx->disp_type == LVGL_PORT_DISP_TYPE_DSI && (drv->direct_mode || drv->full_refresh))) {
|
||||||
|
lv_disp_flush_ready(drv);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
y_start_tmp = y_start;
|
y_start_tmp = y_start;
|
||||||
max_line = ((disp_ctx->trans_size / width) > height) ? (height) : (disp_ctx->trans_size / width);
|
max_line = ((disp_ctx->trans_size / width) > height) ? (height) : (disp_ctx->trans_size / width);
|
||||||
@ -283,36 +502,36 @@ static void lvgl_port_update_callback(lv_disp_drv_t *drv)
|
|||||||
assert(drv);
|
assert(drv);
|
||||||
lvgl_port_display_ctx_t *disp_ctx = (lvgl_port_display_ctx_t *)drv->user_data;
|
lvgl_port_display_ctx_t *disp_ctx = (lvgl_port_display_ctx_t *)drv->user_data;
|
||||||
assert(disp_ctx != NULL);
|
assert(disp_ctx != NULL);
|
||||||
esp_lcd_panel_handle_t panel_handle = disp_ctx->panel_handle;
|
esp_lcd_panel_handle_t control_handle = (disp_ctx->control_handle ? disp_ctx->control_handle : disp_ctx->panel_handle);
|
||||||
|
|
||||||
/* Solve rotation screen and touch */
|
/* Solve rotation screen and touch */
|
||||||
switch (drv->rotated) {
|
switch (drv->rotated) {
|
||||||
case LV_DISP_ROT_NONE:
|
case LV_DISP_ROT_NONE:
|
||||||
/* Rotate LCD display */
|
/* Rotate LCD display */
|
||||||
esp_lcd_panel_swap_xy(panel_handle, disp_ctx->rotation.swap_xy);
|
esp_lcd_panel_swap_xy(control_handle, disp_ctx->rotation.swap_xy);
|
||||||
esp_lcd_panel_mirror(panel_handle, disp_ctx->rotation.mirror_x, disp_ctx->rotation.mirror_y);
|
esp_lcd_panel_mirror(control_handle, disp_ctx->rotation.mirror_x, disp_ctx->rotation.mirror_y);
|
||||||
break;
|
break;
|
||||||
case LV_DISP_ROT_90:
|
case LV_DISP_ROT_90:
|
||||||
/* Rotate LCD display */
|
/* Rotate LCD display */
|
||||||
esp_lcd_panel_swap_xy(panel_handle, !disp_ctx->rotation.swap_xy);
|
esp_lcd_panel_swap_xy(control_handle, !disp_ctx->rotation.swap_xy);
|
||||||
if (disp_ctx->rotation.swap_xy) {
|
if (disp_ctx->rotation.swap_xy) {
|
||||||
esp_lcd_panel_mirror(panel_handle, !disp_ctx->rotation.mirror_x, disp_ctx->rotation.mirror_y);
|
esp_lcd_panel_mirror(control_handle, !disp_ctx->rotation.mirror_x, disp_ctx->rotation.mirror_y);
|
||||||
} else {
|
} else {
|
||||||
esp_lcd_panel_mirror(panel_handle, disp_ctx->rotation.mirror_x, !disp_ctx->rotation.mirror_y);
|
esp_lcd_panel_mirror(control_handle, disp_ctx->rotation.mirror_x, !disp_ctx->rotation.mirror_y);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case LV_DISP_ROT_180:
|
case LV_DISP_ROT_180:
|
||||||
/* Rotate LCD display */
|
/* Rotate LCD display */
|
||||||
esp_lcd_panel_swap_xy(panel_handle, disp_ctx->rotation.swap_xy);
|
esp_lcd_panel_swap_xy(control_handle, disp_ctx->rotation.swap_xy);
|
||||||
esp_lcd_panel_mirror(panel_handle, !disp_ctx->rotation.mirror_x, !disp_ctx->rotation.mirror_y);
|
esp_lcd_panel_mirror(control_handle, !disp_ctx->rotation.mirror_x, !disp_ctx->rotation.mirror_y);
|
||||||
break;
|
break;
|
||||||
case LV_DISP_ROT_270:
|
case LV_DISP_ROT_270:
|
||||||
/* Rotate LCD display */
|
/* Rotate LCD display */
|
||||||
esp_lcd_panel_swap_xy(panel_handle, !disp_ctx->rotation.swap_xy);
|
esp_lcd_panel_swap_xy(control_handle, !disp_ctx->rotation.swap_xy);
|
||||||
if (disp_ctx->rotation.swap_xy) {
|
if (disp_ctx->rotation.swap_xy) {
|
||||||
esp_lcd_panel_mirror(panel_handle, disp_ctx->rotation.mirror_x, !disp_ctx->rotation.mirror_y);
|
esp_lcd_panel_mirror(control_handle, disp_ctx->rotation.mirror_x, !disp_ctx->rotation.mirror_y);
|
||||||
} else {
|
} else {
|
||||||
esp_lcd_panel_mirror(panel_handle, !disp_ctx->rotation.mirror_x, disp_ctx->rotation.mirror_y);
|
esp_lcd_panel_mirror(control_handle, !disp_ctx->rotation.mirror_x, disp_ctx->rotation.mirror_y);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,10 +4,14 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
#include "esp_check.h"
|
#include "esp_check.h"
|
||||||
#include "esp_lvgl_port.h"
|
#include "esp_lvgl_port.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "freertos/semphr.h"
|
||||||
|
|
||||||
#include "usb/hid_host.h"
|
#include "usb/hid_host.h"
|
||||||
#include "usb/hid_usage_keyboard.h"
|
#include "usb/hid_usage_keyboard.h"
|
||||||
|
|||||||
@ -4,25 +4,34 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
#include "esp_system.h"
|
#include "esp_system.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
#include "esp_check.h"
|
#include "esp_check.h"
|
||||||
#include "esp_timer.h"
|
#include "esp_timer.h"
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/portmacro.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "freertos/semphr.h"
|
#include "freertos/semphr.h"
|
||||||
#include "esp_lvgl_port.h"
|
#include "esp_lvgl_port.h"
|
||||||
|
#include "esp_lvgl_port_priv.h"
|
||||||
#include "lvgl.h"
|
#include "lvgl.h"
|
||||||
|
|
||||||
static const char *TAG = "LVGL";
|
static const char *TAG = "LVGL";
|
||||||
|
|
||||||
|
#define ESP_LVGL_PORT_TASK_MUX_DELAY_MS 10000
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Types definitions
|
* Types definitions
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
typedef struct lvgl_port_ctx_s {
|
typedef struct lvgl_port_ctx_s {
|
||||||
|
TaskHandle_t lvgl_task;
|
||||||
SemaphoreHandle_t lvgl_mux;
|
SemaphoreHandle_t lvgl_mux;
|
||||||
|
SemaphoreHandle_t timer_mux;
|
||||||
|
QueueHandle_t lvgl_queue;
|
||||||
|
SemaphoreHandle_t task_init_mux;
|
||||||
esp_timer_handle_t tick_timer;
|
esp_timer_handle_t tick_timer;
|
||||||
bool running;
|
bool running;
|
||||||
int task_max_sleep_ms;
|
int task_max_sleep_ms;
|
||||||
@ -53,24 +62,31 @@ esp_err_t lvgl_port_init(const lvgl_port_cfg_t *cfg)
|
|||||||
|
|
||||||
memset(&lvgl_port_ctx, 0, sizeof(lvgl_port_ctx));
|
memset(&lvgl_port_ctx, 0, sizeof(lvgl_port_ctx));
|
||||||
|
|
||||||
/* LVGL init */
|
|
||||||
lv_init();
|
|
||||||
/* Tick init */
|
/* Tick init */
|
||||||
lvgl_port_ctx.timer_period_ms = cfg->timer_period_ms;
|
lvgl_port_ctx.timer_period_ms = cfg->timer_period_ms;
|
||||||
ESP_RETURN_ON_ERROR(lvgl_port_tick_init(), TAG, "");
|
|
||||||
/* Create task */
|
/* Create task */
|
||||||
lvgl_port_ctx.task_max_sleep_ms = cfg->task_max_sleep_ms;
|
lvgl_port_ctx.task_max_sleep_ms = cfg->task_max_sleep_ms;
|
||||||
if (lvgl_port_ctx.task_max_sleep_ms == 0) {
|
if (lvgl_port_ctx.task_max_sleep_ms == 0) {
|
||||||
lvgl_port_ctx.task_max_sleep_ms = 500;
|
lvgl_port_ctx.task_max_sleep_ms = 500;
|
||||||
}
|
}
|
||||||
|
/* Timer semaphore */
|
||||||
|
lvgl_port_ctx.timer_mux = xSemaphoreCreateMutex();
|
||||||
|
ESP_GOTO_ON_FALSE(lvgl_port_ctx.timer_mux, ESP_ERR_NO_MEM, err, TAG, "Create timer mutex fail!");
|
||||||
|
/* LVGL semaphore */
|
||||||
lvgl_port_ctx.lvgl_mux = xSemaphoreCreateRecursiveMutex();
|
lvgl_port_ctx.lvgl_mux = xSemaphoreCreateRecursiveMutex();
|
||||||
ESP_GOTO_ON_FALSE(lvgl_port_ctx.lvgl_mux, ESP_ERR_NO_MEM, err, TAG, "Create LVGL mutex fail!");
|
ESP_GOTO_ON_FALSE(lvgl_port_ctx.lvgl_mux, ESP_ERR_NO_MEM, err, TAG, "Create LVGL mutex fail!");
|
||||||
|
/* Task init semaphore */
|
||||||
|
lvgl_port_ctx.task_init_mux = xSemaphoreCreateMutex();
|
||||||
|
ESP_GOTO_ON_FALSE(lvgl_port_ctx.task_init_mux, ESP_ERR_NO_MEM, err, TAG, "Create LVGL task sem fail!");
|
||||||
|
/* Task queue */
|
||||||
|
lvgl_port_ctx.lvgl_queue = xQueueCreate(100, sizeof(lvgl_port_event_t));
|
||||||
|
ESP_GOTO_ON_FALSE(lvgl_port_ctx.lvgl_queue, ESP_ERR_NO_MEM, err, TAG, "Create LVGL queue fail!");
|
||||||
|
|
||||||
BaseType_t res;
|
BaseType_t res;
|
||||||
if (cfg->task_affinity < 0) {
|
if (cfg->task_affinity < 0) {
|
||||||
res = xTaskCreate(lvgl_port_task, "LVGL task", cfg->task_stack, NULL, cfg->task_priority, NULL);
|
res = xTaskCreate(lvgl_port_task, "taskLVGL", cfg->task_stack, NULL, cfg->task_priority, &lvgl_port_ctx.lvgl_task);
|
||||||
} else {
|
} else {
|
||||||
res = xTaskCreatePinnedToCore(lvgl_port_task, "LVGL task", cfg->task_stack, NULL, cfg->task_priority, NULL, cfg->task_affinity);
|
res = xTaskCreatePinnedToCore(lvgl_port_task, "taskLVGL", cfg->task_stack, NULL, cfg->task_priority, &lvgl_port_ctx.lvgl_task, cfg->task_affinity);
|
||||||
}
|
}
|
||||||
ESP_GOTO_ON_FALSE(res == pdPASS, ESP_FAIL, err, TAG, "Create LVGL task fail!");
|
ESP_GOTO_ON_FALSE(res == pdPASS, ESP_FAIL, err, TAG, "Create LVGL task fail!");
|
||||||
|
|
||||||
@ -118,10 +134,17 @@ esp_err_t lvgl_port_deinit(void)
|
|||||||
/* Stop running task */
|
/* Stop running task */
|
||||||
if (lvgl_port_ctx.running) {
|
if (lvgl_port_ctx.running) {
|
||||||
lvgl_port_ctx.running = false;
|
lvgl_port_ctx.running = false;
|
||||||
} else {
|
|
||||||
lvgl_port_task_deinit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Wait for stop task */
|
||||||
|
if (xSemaphoreTake(lvgl_port_ctx.task_init_mux, pdMS_TO_TICKS(ESP_LVGL_PORT_TASK_MUX_DELAY_MS)) != pdTRUE) {
|
||||||
|
ESP_LOGE(TAG, "Failed to stop LVGL task");
|
||||||
|
return ESP_ERR_TIMEOUT;
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG, "Stopped LVGL task");
|
||||||
|
|
||||||
|
lvgl_port_task_deinit();
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -139,30 +162,107 @@ void lvgl_port_unlock(void)
|
|||||||
xSemaphoreGiveRecursive(lvgl_port_ctx.lvgl_mux);
|
xSemaphoreGiveRecursive(lvgl_port_ctx.lvgl_mux);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
esp_err_t lvgl_port_task_wake(lvgl_port_event_type_t event, void *param)
|
||||||
|
{
|
||||||
|
if (!lvgl_port_ctx.lvgl_queue) {
|
||||||
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
lvgl_port_event_t ev = {
|
||||||
|
.type = event,
|
||||||
|
.param = param,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (xPortInIsrContext() == pdTRUE) {
|
||||||
|
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||||
|
xQueueSendFromISR(lvgl_port_ctx.lvgl_queue, &ev, &xHigherPriorityTaskWoken);
|
||||||
|
if (xHigherPriorityTaskWoken) {
|
||||||
|
portYIELD_FROM_ISR( );
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
xQueueSend(lvgl_port_ctx.lvgl_queue, &ev, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
IRAM_ATTR bool lvgl_port_task_notify(uint32_t value)
|
||||||
|
{
|
||||||
|
BaseType_t need_yield = pdFALSE;
|
||||||
|
|
||||||
|
// Notify LVGL task
|
||||||
|
if (xPortInIsrContext() == pdTRUE) {
|
||||||
|
xTaskNotifyFromISR(lvgl_port_ctx.lvgl_task, value, eNoAction, &need_yield);
|
||||||
|
} else {
|
||||||
|
xTaskNotify(lvgl_port_ctx.lvgl_task, value, eNoAction);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (need_yield == pdTRUE);
|
||||||
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Private functions
|
* Private functions
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
static void lvgl_port_task(void *arg)
|
static void lvgl_port_task(void *arg)
|
||||||
{
|
{
|
||||||
uint32_t task_delay_ms = lvgl_port_ctx.task_max_sleep_ms;
|
lvgl_port_event_t event;
|
||||||
|
uint32_t task_delay_ms = 0;
|
||||||
|
lv_indev_t *indev = NULL;
|
||||||
|
|
||||||
|
/* Take the task semaphore */
|
||||||
|
if (xSemaphoreTake(lvgl_port_ctx.task_init_mux, 0) != pdTRUE) {
|
||||||
|
ESP_LOGE(TAG, "Failed to take LVGL task sem");
|
||||||
|
lvgl_port_task_deinit();
|
||||||
|
vTaskDelete( NULL );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LVGL init */
|
||||||
|
lv_init();
|
||||||
|
/* Tick init */
|
||||||
|
lvgl_port_tick_init();
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Starting LVGL task");
|
ESP_LOGI(TAG, "Starting LVGL task");
|
||||||
lvgl_port_ctx.running = true;
|
lvgl_port_ctx.running = true;
|
||||||
while (lvgl_port_ctx.running) {
|
while (lvgl_port_ctx.running) {
|
||||||
|
/* Wait for queue or timeout (sleep task) */
|
||||||
|
TickType_t wait = (pdMS_TO_TICKS(task_delay_ms) >= 1 ? pdMS_TO_TICKS(task_delay_ms) : 1);
|
||||||
|
xQueueReceive(lvgl_port_ctx.lvgl_queue, &event, wait);
|
||||||
|
|
||||||
if (lv_display_get_default() && lvgl_port_lock(0)) {
|
if (lv_display_get_default() && lvgl_port_lock(0)) {
|
||||||
|
|
||||||
|
/* Call read input devices */
|
||||||
|
if (event.type == LVGL_PORT_EVENT_TOUCH) {
|
||||||
|
xSemaphoreTake(lvgl_port_ctx.timer_mux, portMAX_DELAY);
|
||||||
|
if (event.param != NULL) {
|
||||||
|
lv_indev_read(event.param);
|
||||||
|
} else {
|
||||||
|
indev = lv_indev_get_next(NULL);
|
||||||
|
while (indev != NULL) {
|
||||||
|
lv_indev_read(indev);
|
||||||
|
indev = lv_indev_get_next(indev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
xSemaphoreGive(lvgl_port_ctx.timer_mux);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Handle LVGL */
|
||||||
task_delay_ms = lv_timer_handler();
|
task_delay_ms = lv_timer_handler();
|
||||||
lvgl_port_unlock();
|
lvgl_port_unlock();
|
||||||
|
} else {
|
||||||
|
task_delay_ms = 1; /*Keep trying*/
|
||||||
}
|
}
|
||||||
if ((task_delay_ms > lvgl_port_ctx.task_max_sleep_ms) || (1 == task_delay_ms)) {
|
|
||||||
|
if (task_delay_ms == LV_NO_TIMER_READY) {
|
||||||
task_delay_ms = lvgl_port_ctx.task_max_sleep_ms;
|
task_delay_ms = lvgl_port_ctx.task_max_sleep_ms;
|
||||||
} else if (task_delay_ms < 1) {
|
|
||||||
task_delay_ms = 1;
|
|
||||||
}
|
}
|
||||||
vTaskDelay(pdMS_TO_TICKS(task_delay_ms));
|
|
||||||
|
/* Minimal dealy for the task. When there is too much events, it takes time for other tasks and interrupts. */
|
||||||
|
vTaskDelay(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
lvgl_port_task_deinit();
|
/* Give semaphore back */
|
||||||
|
xSemaphoreGive(lvgl_port_ctx.task_init_mux);
|
||||||
|
|
||||||
/* Close task */
|
/* Close task */
|
||||||
vTaskDelete( NULL );
|
vTaskDelete( NULL );
|
||||||
@ -170,9 +270,18 @@ static void lvgl_port_task(void *arg)
|
|||||||
|
|
||||||
static void lvgl_port_task_deinit(void)
|
static void lvgl_port_task_deinit(void)
|
||||||
{
|
{
|
||||||
|
if (lvgl_port_ctx.timer_mux) {
|
||||||
|
vSemaphoreDelete(lvgl_port_ctx.timer_mux);
|
||||||
|
}
|
||||||
if (lvgl_port_ctx.lvgl_mux) {
|
if (lvgl_port_ctx.lvgl_mux) {
|
||||||
vSemaphoreDelete(lvgl_port_ctx.lvgl_mux);
|
vSemaphoreDelete(lvgl_port_ctx.lvgl_mux);
|
||||||
}
|
}
|
||||||
|
if (lvgl_port_ctx.task_init_mux) {
|
||||||
|
vSemaphoreDelete(lvgl_port_ctx.task_init_mux);
|
||||||
|
}
|
||||||
|
if (lvgl_port_ctx.lvgl_queue) {
|
||||||
|
vQueueDelete(lvgl_port_ctx.lvgl_queue);
|
||||||
|
}
|
||||||
memset(&lvgl_port_ctx, 0, sizeof(lvgl_port_ctx));
|
memset(&lvgl_port_ctx, 0, sizeof(lvgl_port_ctx));
|
||||||
#if LV_ENABLE_GC || !LV_MEM_CUSTOM
|
#if LV_ENABLE_GC || !LV_MEM_CUSTOM
|
||||||
/* Deinitialize LVGL */
|
/* Deinitialize LVGL */
|
||||||
@ -182,8 +291,10 @@ static void lvgl_port_task_deinit(void)
|
|||||||
|
|
||||||
static void lvgl_port_tick_increment(void *arg)
|
static void lvgl_port_tick_increment(void *arg)
|
||||||
{
|
{
|
||||||
|
xSemaphoreTake(lvgl_port_ctx.timer_mux, portMAX_DELAY);
|
||||||
/* Tell LVGL how many milliseconds have elapsed */
|
/* Tell LVGL how many milliseconds have elapsed */
|
||||||
lv_tick_inc(lvgl_port_ctx.timer_period_ms);
|
lv_tick_inc(lvgl_port_ctx.timer_period_ms);
|
||||||
|
xSemaphoreGive(lvgl_port_ctx.timer_mux);
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t lvgl_port_tick_init(void)
|
static esp_err_t lvgl_port_tick_init(void)
|
||||||
|
|||||||
@ -88,6 +88,7 @@ lv_indev_t *lvgl_port_add_navigation_buttons(const lvgl_port_nav_btns_cfg_t *but
|
|||||||
/* Register a touchpad input device */
|
/* Register a touchpad input device */
|
||||||
indev = lv_indev_create();
|
indev = lv_indev_create();
|
||||||
lv_indev_set_type(indev, LV_INDEV_TYPE_ENCODER);
|
lv_indev_set_type(indev, LV_INDEV_TYPE_ENCODER);
|
||||||
|
lv_indev_set_mode(indev, LV_INDEV_MODE_EVENT);
|
||||||
lv_indev_set_read_cb(indev, lvgl_port_navigation_buttons_read);
|
lv_indev_set_read_cb(indev, lvgl_port_navigation_buttons_read);
|
||||||
lv_indev_set_disp(indev, buttons_cfg->disp);
|
lv_indev_set_disp(indev, buttons_cfg->disp);
|
||||||
lv_indev_set_driver_data(indev, buttons_ctx);
|
lv_indev_set_driver_data(indev, buttons_ctx);
|
||||||
@ -110,7 +111,7 @@ err:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return buttons_ctx->indev;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t lvgl_port_remove_navigation_buttons(lv_indev_t *buttons)
|
esp_err_t lvgl_port_remove_navigation_buttons(lv_indev_t *buttons)
|
||||||
@ -180,6 +181,9 @@ static void lvgl_port_btn_down_handler(void *arg, void *arg2)
|
|||||||
ctx->btn_enter = true;
|
ctx->btn_enter = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Wake LVGL task, if needed */
|
||||||
|
lvgl_port_task_wake(LVGL_PORT_EVENT_TOUCH, ctx->indev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void lvgl_port_btn_up_handler(void *arg, void *arg2)
|
static void lvgl_port_btn_up_handler(void *arg, void *arg2)
|
||||||
@ -200,4 +204,7 @@ static void lvgl_port_btn_up_handler(void *arg, void *arg2)
|
|||||||
ctx->btn_enter = false;
|
ctx->btn_enter = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Wake LVGL task, if needed */
|
||||||
|
lvgl_port_task_wake(LVGL_PORT_EVENT_TOUCH, ctx->indev);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,13 +4,27 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
#include "esp_check.h"
|
#include "esp_check.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "freertos/semphr.h"
|
||||||
#include "esp_heap_caps.h"
|
#include "esp_heap_caps.h"
|
||||||
|
#include "esp_idf_version.h"
|
||||||
#include "esp_lcd_panel_io.h"
|
#include "esp_lcd_panel_io.h"
|
||||||
#include "esp_lcd_panel_ops.h"
|
#include "esp_lcd_panel_ops.h"
|
||||||
#include "esp_lvgl_port.h"
|
#include "esp_lvgl_port.h"
|
||||||
|
#include "esp_lvgl_port_priv.h"
|
||||||
|
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32S3 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||||
|
#include "esp_lcd_panel_rgb.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (CONFIG_IDF_TARGET_ESP32P4 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0))
|
||||||
|
#include "esp_lcd_mipi_dsi.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
#if (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 4, 4)) || (ESP_IDF_VERSION == ESP_IDF_VERSION_VAL(5, 0, 0))
|
#if (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 4, 4)) || (ESP_IDF_VERSION == ESP_IDF_VERSION_VAL(5, 0, 0))
|
||||||
#define LVGL_PORT_HANDLE_FLUSH_READY 0
|
#define LVGL_PORT_HANDLE_FLUSH_READY 0
|
||||||
@ -25,26 +39,43 @@ static const char *TAG = "LVGL";
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
lvgl_port_disp_type_t disp_type; /* Display type */
|
||||||
esp_lcd_panel_io_handle_t io_handle; /* LCD panel IO handle */
|
esp_lcd_panel_io_handle_t io_handle; /* LCD panel IO handle */
|
||||||
esp_lcd_panel_handle_t panel_handle; /* LCD panel handle */
|
esp_lcd_panel_handle_t panel_handle; /* LCD panel handle */
|
||||||
|
esp_lcd_panel_handle_t control_handle; /* LCD panel control handle */
|
||||||
lvgl_port_rotation_cfg_t rotation; /* Default values of the screen rotation */
|
lvgl_port_rotation_cfg_t rotation; /* Default values of the screen rotation */
|
||||||
lv_color16_t *draw_buffs[2]; /* Display draw buffers */
|
lv_color_t *draw_buffs[3]; /* Display draw buffers */
|
||||||
|
uint8_t *oled_buffer;
|
||||||
lv_display_t *disp_drv; /* LVGL display driver */
|
lv_display_t *disp_drv; /* LVGL display driver */
|
||||||
|
lv_display_rotation_t current_rotation;
|
||||||
|
SemaphoreHandle_t trans_sem; /* Idle transfer mutex */
|
||||||
struct {
|
struct {
|
||||||
unsigned int monochrome: 1; /* True, if display is monochrome and using 1bit for 1px */
|
unsigned int monochrome: 1; /* True, if display is monochrome and using 1bit for 1px */
|
||||||
unsigned int swap_bytes: 1; /* Swap bytes in RGB656 (16-bit) before send to LCD driver */
|
unsigned int swap_bytes: 1; /* Swap bytes in RGB656 (16-bit) before send to LCD driver */
|
||||||
|
unsigned int full_refresh: 1; /* Always make the whole screen redrawn */
|
||||||
|
unsigned int direct_mode: 1; /* Use screen-sized buffers and draw to absolute coordinates */
|
||||||
|
unsigned int sw_rotate: 1; /* Use software rotation (slower) or PPA if available */
|
||||||
} flags;
|
} flags;
|
||||||
} lvgl_port_display_ctx_t;
|
} lvgl_port_display_ctx_t;
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Function definitions
|
* Function definitions
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
static lv_display_t *lvgl_port_add_disp_priv(const lvgl_port_display_cfg_t *disp_cfg, const lvgl_port_disp_priv_cfg_t *priv_cfg);
|
||||||
#if LVGL_PORT_HANDLE_FLUSH_READY
|
#if LVGL_PORT_HANDLE_FLUSH_READY
|
||||||
static bool lvgl_port_flush_ready_callback(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx);
|
static bool lvgl_port_flush_io_ready_callback(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx);
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32S3 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||||
|
static bool lvgl_port_flush_rgb_vsync_ready_callback(esp_lcd_panel_handle_t panel_io, const esp_lcd_rgb_panel_event_data_t *edata, void *user_ctx);
|
||||||
|
#endif
|
||||||
|
#if (CONFIG_IDF_TARGET_ESP32P4 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0))
|
||||||
|
static bool lvgl_port_flush_dpi_panel_ready_callback(esp_lcd_panel_handle_t panel_io, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx);
|
||||||
|
static bool lvgl_port_flush_dpi_vsync_ready_callback(esp_lcd_panel_handle_t panel_io, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx);
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
static void lvgl_port_flush_callback(lv_display_t *drv, const lv_area_t *area, uint8_t *color_map);
|
static void lvgl_port_flush_callback(lv_display_t *drv, const lv_area_t *area, uint8_t *color_map);
|
||||||
static void lvgl_port_disp_size_update_callback(lv_event_t *e);
|
static void lvgl_port_disp_size_update_callback(lv_event_t *e);
|
||||||
|
static void lvgl_port_disp_rotation_update(lvgl_port_display_ctx_t *disp_ctx);
|
||||||
|
static void lvgl_port_display_invalidate_callback(lv_event_t *e);
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Public API functions
|
* Public API functions
|
||||||
@ -52,93 +83,106 @@ static void lvgl_port_disp_size_update_callback(lv_event_t *e);
|
|||||||
|
|
||||||
lv_display_t *lvgl_port_add_disp(const lvgl_port_display_cfg_t *disp_cfg)
|
lv_display_t *lvgl_port_add_disp(const lvgl_port_display_cfg_t *disp_cfg)
|
||||||
{
|
{
|
||||||
esp_err_t ret = ESP_OK;
|
|
||||||
lv_display_t *disp = NULL;
|
|
||||||
lv_color16_t *buf1 = NULL;
|
|
||||||
lv_color16_t *buf2 = NULL;
|
|
||||||
assert(disp_cfg != NULL);
|
|
||||||
assert(disp_cfg->io_handle != NULL);
|
|
||||||
assert(disp_cfg->panel_handle != NULL);
|
|
||||||
assert(disp_cfg->buffer_size > 0);
|
|
||||||
assert(disp_cfg->hres > 0);
|
|
||||||
assert(disp_cfg->vres > 0);
|
|
||||||
|
|
||||||
/* Display context */
|
|
||||||
lvgl_port_display_ctx_t *disp_ctx = malloc(sizeof(lvgl_port_display_ctx_t));
|
|
||||||
ESP_GOTO_ON_FALSE(disp_ctx, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for display context allocation!");
|
|
||||||
disp_ctx->io_handle = disp_cfg->io_handle;
|
|
||||||
disp_ctx->panel_handle = disp_cfg->panel_handle;
|
|
||||||
disp_ctx->rotation.swap_xy = disp_cfg->rotation.swap_xy;
|
|
||||||
disp_ctx->rotation.mirror_x = disp_cfg->rotation.mirror_x;
|
|
||||||
disp_ctx->rotation.mirror_y = disp_cfg->rotation.mirror_y;
|
|
||||||
disp_ctx->flags.swap_bytes = disp_cfg->flags.swap_bytes;
|
|
||||||
|
|
||||||
uint32_t buff_caps = MALLOC_CAP_DEFAULT;
|
|
||||||
if (disp_cfg->flags.buff_dma && disp_cfg->flags.buff_spiram) {
|
|
||||||
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "Alloc DMA capable buffer in SPIRAM is not supported!");
|
|
||||||
} else if (disp_cfg->flags.buff_dma) {
|
|
||||||
buff_caps = MALLOC_CAP_DMA;
|
|
||||||
} else if (disp_cfg->flags.buff_spiram) {
|
|
||||||
buff_caps = MALLOC_CAP_SPIRAM;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* alloc draw buffers used by LVGL */
|
|
||||||
/* it's recommended to choose the size of the draw buffer(s) to be at least 1/10 screen sized */
|
|
||||||
buf1 = heap_caps_malloc(disp_cfg->buffer_size * sizeof(lv_color16_t), buff_caps);
|
|
||||||
ESP_GOTO_ON_FALSE(buf1, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for LVGL buffer (buf1) allocation!");
|
|
||||||
if (disp_cfg->double_buffer) {
|
|
||||||
buf2 = heap_caps_malloc(disp_cfg->buffer_size * sizeof(lv_color16_t), buff_caps);
|
|
||||||
ESP_GOTO_ON_FALSE(buf2, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for LVGL buffer (buf2) allocation!");
|
|
||||||
}
|
|
||||||
|
|
||||||
disp_ctx->draw_buffs[0] = buf1;
|
|
||||||
disp_ctx->draw_buffs[1] = buf2;
|
|
||||||
|
|
||||||
lvgl_port_lock(0);
|
lvgl_port_lock(0);
|
||||||
disp = lv_display_create(disp_cfg->hres, disp_cfg->vres);
|
lv_disp_t *disp = lvgl_port_add_disp_priv(disp_cfg, NULL);
|
||||||
|
|
||||||
/* Monochrome display settings */
|
if (disp != NULL) {
|
||||||
if (disp_cfg->monochrome) {
|
lvgl_port_display_ctx_t *disp_ctx = (lvgl_port_display_ctx_t *)lv_display_get_driver_data(disp);
|
||||||
/* When using monochromatic display, there must be used full bufer! */
|
/* Set display type */
|
||||||
ESP_GOTO_ON_FALSE((disp_cfg->hres * disp_cfg->vres == disp_cfg->buffer_size), ESP_ERR_INVALID_ARG, err, TAG, "Monochromatic display must using full buffer!");
|
disp_ctx->disp_type = LVGL_PORT_DISP_TYPE_OTHER;
|
||||||
|
|
||||||
disp_ctx->flags.monochrome = 1;
|
assert(disp_cfg->io_handle != NULL);
|
||||||
|
|
||||||
//lv_display_set_color_format(disp, LV_COLOR_FORMAT_RGB565);
|
|
||||||
lv_display_set_buffers(disp, buf1, buf2, disp_cfg->buffer_size * sizeof(lv_color16_t), LV_DISPLAY_RENDER_MODE_FULL);
|
|
||||||
} else {
|
|
||||||
lv_display_set_buffers(disp, buf1, buf2, disp_cfg->buffer_size * sizeof(lv_color16_t), LV_DISPLAY_RENDER_MODE_PARTIAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
lv_display_set_flush_cb(disp, lvgl_port_flush_callback);
|
|
||||||
lv_display_add_event_cb(disp, lvgl_port_disp_size_update_callback, LV_EVENT_RESOLUTION_CHANGED, disp_ctx);
|
|
||||||
|
|
||||||
lv_display_set_driver_data(disp, disp_ctx);
|
|
||||||
disp_ctx->disp_drv = disp;
|
|
||||||
|
|
||||||
#if LVGL_PORT_HANDLE_FLUSH_READY
|
#if LVGL_PORT_HANDLE_FLUSH_READY
|
||||||
/* Register done callback */
|
const esp_lcd_panel_io_callbacks_t cbs = {
|
||||||
const esp_lcd_panel_io_callbacks_t cbs = {
|
.on_color_trans_done = lvgl_port_flush_io_ready_callback,
|
||||||
.on_color_trans_done = lvgl_port_flush_ready_callback,
|
};
|
||||||
};
|
/* Register done callback */
|
||||||
esp_lcd_panel_io_register_event_callbacks(disp_ctx->io_handle, &cbs, disp_ctx->disp_drv);
|
esp_lcd_panel_io_register_event_callbacks(disp_ctx->io_handle, &cbs, disp);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/* Apply rotation from initial display configuration */
|
||||||
|
lvgl_port_disp_rotation_update(disp_ctx);
|
||||||
|
}
|
||||||
lvgl_port_unlock();
|
lvgl_port_unlock();
|
||||||
|
|
||||||
err:
|
return disp;
|
||||||
if (ret != ESP_OK) {
|
}
|
||||||
if (buf1) {
|
|
||||||
free(buf1);
|
lv_display_t *lvgl_port_add_disp_dsi(const lvgl_port_display_cfg_t *disp_cfg, const lvgl_port_display_dsi_cfg_t *dsi_cfg)
|
||||||
}
|
{
|
||||||
if (buf2) {
|
assert(dsi_cfg != NULL);
|
||||||
free(buf2);
|
const lvgl_port_disp_priv_cfg_t priv_cfg = {
|
||||||
}
|
.avoid_tearing = dsi_cfg->flags.avoid_tearing,
|
||||||
if (disp_ctx) {
|
};
|
||||||
free(disp_ctx);
|
lvgl_port_lock(0);
|
||||||
|
lv_disp_t *disp = lvgl_port_add_disp_priv(disp_cfg, &priv_cfg);
|
||||||
|
|
||||||
|
if (disp != NULL) {
|
||||||
|
lvgl_port_display_ctx_t *disp_ctx = (lvgl_port_display_ctx_t *)lv_display_get_driver_data(disp);
|
||||||
|
/* Set display type */
|
||||||
|
disp_ctx->disp_type = LVGL_PORT_DISP_TYPE_DSI;
|
||||||
|
|
||||||
|
#if (CONFIG_IDF_TARGET_ESP32P4 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0))
|
||||||
|
esp_lcd_dpi_panel_event_callbacks_t cbs = {0};
|
||||||
|
if (dsi_cfg->flags.avoid_tearing) {
|
||||||
|
cbs.on_refresh_done = lvgl_port_flush_dpi_vsync_ready_callback;
|
||||||
|
} else {
|
||||||
|
cbs.on_color_trans_done = lvgl_port_flush_dpi_panel_ready_callback;
|
||||||
}
|
}
|
||||||
|
/* Register done callback */
|
||||||
|
esp_lcd_dpi_panel_register_event_callbacks(disp_ctx->panel_handle, &cbs, disp);
|
||||||
|
|
||||||
|
/* Apply rotation from initial display configuration */
|
||||||
|
lvgl_port_disp_rotation_update(disp_ctx);
|
||||||
|
#else
|
||||||
|
ESP_RETURN_ON_FALSE(false, NULL, TAG, "MIPI-DSI is supported only on ESP32P4 and from IDF 5.3!");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
lvgl_port_unlock();
|
||||||
|
|
||||||
|
return disp;
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_display_t *lvgl_port_add_disp_rgb(const lvgl_port_display_cfg_t *disp_cfg, const lvgl_port_display_rgb_cfg_t *rgb_cfg)
|
||||||
|
{
|
||||||
|
lvgl_port_lock(0);
|
||||||
|
assert(rgb_cfg != NULL);
|
||||||
|
const lvgl_port_disp_priv_cfg_t priv_cfg = {
|
||||||
|
.avoid_tearing = rgb_cfg->flags.avoid_tearing,
|
||||||
|
};
|
||||||
|
lv_disp_t *disp = lvgl_port_add_disp_priv(disp_cfg, &priv_cfg);
|
||||||
|
|
||||||
|
if (disp != NULL) {
|
||||||
|
lvgl_port_display_ctx_t *disp_ctx = (lvgl_port_display_ctx_t *)lv_display_get_driver_data(disp);
|
||||||
|
/* Set display type */
|
||||||
|
disp_ctx->disp_type = LVGL_PORT_DISP_TYPE_RGB;
|
||||||
|
|
||||||
|
#if (CONFIG_IDF_TARGET_ESP32S3 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0))
|
||||||
|
/* Register done callback */
|
||||||
|
const esp_lcd_rgb_panel_event_callbacks_t vsync_cbs = {
|
||||||
|
.on_vsync = lvgl_port_flush_rgb_vsync_ready_callback,
|
||||||
|
};
|
||||||
|
|
||||||
|
const esp_lcd_rgb_panel_event_callbacks_t bb_cbs = {
|
||||||
|
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 2)
|
||||||
|
.on_bounce_frame_finish = lvgl_port_flush_rgb_vsync_ready_callback,
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
if (rgb_cfg->flags.bb_mode && (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 2))) {
|
||||||
|
ESP_ERROR_CHECK(esp_lcd_rgb_panel_register_event_callbacks(disp_ctx->panel_handle, &bb_cbs, disp_ctx->disp_drv));
|
||||||
|
} else {
|
||||||
|
ESP_ERROR_CHECK(esp_lcd_rgb_panel_register_event_callbacks(disp_ctx->panel_handle, &vsync_cbs, disp_ctx->disp_drv));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
ESP_RETURN_ON_FALSE(false, NULL, TAG, "RGB is supported only on ESP32S3 and from IDF 5.0!");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Apply rotation from initial display configuration */
|
||||||
|
lvgl_port_disp_rotation_update(disp_ctx);
|
||||||
|
}
|
||||||
|
lvgl_port_unlock();
|
||||||
|
|
||||||
return disp;
|
return disp;
|
||||||
}
|
}
|
||||||
@ -160,6 +204,18 @@ esp_err_t lvgl_port_remove_disp(lv_display_t *disp)
|
|||||||
free(disp_ctx->draw_buffs[1]);
|
free(disp_ctx->draw_buffs[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (disp_ctx->draw_buffs[2]) {
|
||||||
|
free(disp_ctx->draw_buffs[2]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (disp_ctx->oled_buffer) {
|
||||||
|
free(disp_ctx->oled_buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (disp_ctx->trans_sem) {
|
||||||
|
vSemaphoreDelete(disp_ctx->trans_sem);
|
||||||
|
}
|
||||||
|
|
||||||
free(disp_ctx);
|
free(disp_ctx);
|
||||||
|
|
||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
@ -175,20 +231,237 @@ void lvgl_port_flush_ready(lv_display_t *disp)
|
|||||||
* Private functions
|
* Private functions
|
||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
|
static lv_display_t *lvgl_port_add_disp_priv(const lvgl_port_display_cfg_t *disp_cfg, const lvgl_port_disp_priv_cfg_t *priv_cfg)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
lv_display_t *disp = NULL;
|
||||||
|
lv_color_t *buf1 = NULL;
|
||||||
|
lv_color_t *buf2 = NULL;
|
||||||
|
uint32_t buffer_size = 0;
|
||||||
|
SemaphoreHandle_t trans_sem = NULL;
|
||||||
|
assert(disp_cfg != NULL);
|
||||||
|
assert(disp_cfg->panel_handle != NULL);
|
||||||
|
assert(disp_cfg->buffer_size > 0);
|
||||||
|
assert(disp_cfg->hres > 0);
|
||||||
|
assert(disp_cfg->vres > 0);
|
||||||
|
|
||||||
|
buffer_size = disp_cfg->buffer_size;
|
||||||
|
|
||||||
|
/* Check supported display color formats */
|
||||||
|
ESP_RETURN_ON_FALSE(disp_cfg->color_format == 0 || disp_cfg->color_format == LV_COLOR_FORMAT_RGB565 || disp_cfg->color_format == LV_COLOR_FORMAT_RGB888 || disp_cfg->color_format == LV_COLOR_FORMAT_XRGB8888 || disp_cfg->color_format == LV_COLOR_FORMAT_ARGB8888 || disp_cfg->color_format == LV_COLOR_FORMAT_I1, NULL, TAG, "Not supported display color format!");
|
||||||
|
|
||||||
|
lv_color_format_t display_color_format = (disp_cfg->color_format != 0 ? disp_cfg->color_format : LV_COLOR_FORMAT_RGB565);
|
||||||
|
if (disp_cfg->flags.swap_bytes) {
|
||||||
|
/* Swap bytes can be used only in RGB565 color format */
|
||||||
|
ESP_RETURN_ON_FALSE(display_color_format == LV_COLOR_FORMAT_RGB565, NULL, TAG, "Swap bytes can be used only in display color format RGB565!");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (disp_cfg->flags.buff_dma) {
|
||||||
|
/* DMA buffer can be used only in RGB565 color format */
|
||||||
|
ESP_RETURN_ON_FALSE(display_color_format == LV_COLOR_FORMAT_RGB565, NULL, TAG, "DMA buffer can be used only in display color format RGB565 (not alligned copy)!");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Display context */
|
||||||
|
lvgl_port_display_ctx_t *disp_ctx = malloc(sizeof(lvgl_port_display_ctx_t));
|
||||||
|
ESP_GOTO_ON_FALSE(disp_ctx, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for display context allocation!");
|
||||||
|
memset(disp_ctx, 0, sizeof(lvgl_port_display_ctx_t));
|
||||||
|
disp_ctx->io_handle = disp_cfg->io_handle;
|
||||||
|
disp_ctx->panel_handle = disp_cfg->panel_handle;
|
||||||
|
disp_ctx->control_handle = disp_cfg->control_handle;
|
||||||
|
disp_ctx->rotation.swap_xy = disp_cfg->rotation.swap_xy;
|
||||||
|
disp_ctx->rotation.mirror_x = disp_cfg->rotation.mirror_x;
|
||||||
|
disp_ctx->rotation.mirror_y = disp_cfg->rotation.mirror_y;
|
||||||
|
disp_ctx->flags.swap_bytes = disp_cfg->flags.swap_bytes;
|
||||||
|
disp_ctx->flags.sw_rotate = disp_cfg->flags.sw_rotate;
|
||||||
|
disp_ctx->current_rotation = LV_DISPLAY_ROTATION_0;
|
||||||
|
|
||||||
|
uint32_t buff_caps = 0;
|
||||||
|
#if SOC_PSRAM_DMA_CAPABLE == 0
|
||||||
|
if (disp_cfg->flags.buff_dma && disp_cfg->flags.buff_spiram) {
|
||||||
|
ESP_GOTO_ON_FALSE(false, ESP_ERR_NOT_SUPPORTED, err, TAG, "Alloc DMA capable buffer in SPIRAM is not supported!");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (disp_cfg->flags.buff_dma) {
|
||||||
|
buff_caps |= MALLOC_CAP_DMA;
|
||||||
|
}
|
||||||
|
if (disp_cfg->flags.buff_spiram) {
|
||||||
|
buff_caps |= MALLOC_CAP_SPIRAM;
|
||||||
|
}
|
||||||
|
if (buff_caps == 0) {
|
||||||
|
buff_caps |= MALLOC_CAP_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Use RGB internal buffers for avoid tearing effect */
|
||||||
|
if (priv_cfg && priv_cfg->avoid_tearing) {
|
||||||
|
#if CONFIG_IDF_TARGET_ESP32S3 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||||
|
buffer_size = disp_cfg->hres * disp_cfg->vres;
|
||||||
|
ESP_GOTO_ON_ERROR(esp_lcd_rgb_panel_get_frame_buffer(disp_cfg->panel_handle, 2, (void *)&buf1, (void *)&buf2), err, TAG, "Get RGB buffers failed");
|
||||||
|
#elif CONFIG_IDF_TARGET_ESP32P4 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
|
||||||
|
buffer_size = disp_cfg->hres * disp_cfg->vres;
|
||||||
|
ESP_GOTO_ON_ERROR(esp_lcd_dpi_panel_get_frame_buffer(disp_cfg->panel_handle, 2, (void *)&buf1, (void *)&buf2), err, TAG, "Get RGB buffers failed");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
trans_sem = xSemaphoreCreateCounting(1, 0);
|
||||||
|
ESP_GOTO_ON_FALSE(trans_sem, ESP_ERR_NO_MEM, err, TAG, "Failed to create transport counting Semaphore");
|
||||||
|
disp_ctx->trans_sem = trans_sem;
|
||||||
|
} else {
|
||||||
|
/* alloc draw buffers used by LVGL */
|
||||||
|
/* it's recommended to choose the size of the draw buffer(s) to be at least 1/10 screen sized */
|
||||||
|
buf1 = heap_caps_malloc(buffer_size * sizeof(lv_color_t), buff_caps);
|
||||||
|
ESP_GOTO_ON_FALSE(buf1, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for LVGL buffer (buf1) allocation!");
|
||||||
|
if (disp_cfg->double_buffer) {
|
||||||
|
buf2 = heap_caps_malloc(buffer_size * sizeof(lv_color_t), buff_caps);
|
||||||
|
ESP_GOTO_ON_FALSE(buf2, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for LVGL buffer (buf2) allocation!");
|
||||||
|
}
|
||||||
|
|
||||||
|
disp_ctx->draw_buffs[0] = buf1;
|
||||||
|
disp_ctx->draw_buffs[1] = buf2;
|
||||||
|
}
|
||||||
|
|
||||||
|
disp = lv_display_create(disp_cfg->hres, disp_cfg->vres);
|
||||||
|
|
||||||
|
/* Set display color format */
|
||||||
|
lv_display_set_color_format(disp, display_color_format);
|
||||||
|
|
||||||
|
/* Monochrome display settings */
|
||||||
|
if (disp_cfg->monochrome) {
|
||||||
|
#if CONFIG_LV_COLOR_DEPTH_1
|
||||||
|
#error please disable LV_COLOR_DEPTH_1 for using monochromatic screen
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Monochrome can be used only in RGB565 color format */
|
||||||
|
ESP_RETURN_ON_FALSE(display_color_format == LV_COLOR_FORMAT_RGB565 || display_color_format == LV_COLOR_FORMAT_I1, NULL, TAG, "Monochrome can be used only in display color format RGB565 or I1!");
|
||||||
|
|
||||||
|
/* When using monochromatic display, there must be used full bufer! */
|
||||||
|
ESP_GOTO_ON_FALSE((disp_cfg->hres * disp_cfg->vres == buffer_size), ESP_ERR_INVALID_ARG, err, TAG, "Monochromatic display must using full buffer!");
|
||||||
|
|
||||||
|
disp_ctx->flags.monochrome = 1;
|
||||||
|
lv_display_set_buffers(disp, buf1, buf2, buffer_size * sizeof(lv_color_t), LV_DISPLAY_RENDER_MODE_FULL);
|
||||||
|
|
||||||
|
if (display_color_format == LV_COLOR_FORMAT_I1) {
|
||||||
|
/* OLED monochrome buffer */
|
||||||
|
// To use LV_COLOR_FORMAT_I1, we need an extra buffer to hold the converted data
|
||||||
|
disp_ctx->oled_buffer = heap_caps_malloc(buffer_size, buff_caps);
|
||||||
|
ESP_GOTO_ON_FALSE(disp_ctx->oled_buffer, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for LVGL buffer (OLED buffer) allocation!");
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (disp_cfg->flags.direct_mode) {
|
||||||
|
/* When using direct_mode, there must be used full bufer! */
|
||||||
|
ESP_GOTO_ON_FALSE((disp_cfg->hres * disp_cfg->vres == buffer_size), ESP_ERR_INVALID_ARG, err, TAG, "Direct mode must using full buffer!");
|
||||||
|
|
||||||
|
disp_ctx->flags.direct_mode = 1;
|
||||||
|
lv_display_set_buffers(disp, buf1, buf2, buffer_size * sizeof(lv_color_t), LV_DISPLAY_RENDER_MODE_DIRECT);
|
||||||
|
} else if (disp_cfg->flags.full_refresh) {
|
||||||
|
/* When using full_refresh, there must be used full bufer! */
|
||||||
|
ESP_GOTO_ON_FALSE((disp_cfg->hres * disp_cfg->vres == buffer_size), ESP_ERR_INVALID_ARG, err, TAG, "Full refresh must using full buffer!");
|
||||||
|
|
||||||
|
disp_ctx->flags.full_refresh = 1;
|
||||||
|
lv_display_set_buffers(disp, buf1, buf2, buffer_size * sizeof(lv_color_t), LV_DISPLAY_RENDER_MODE_FULL);
|
||||||
|
} else {
|
||||||
|
lv_display_set_buffers(disp, buf1, buf2, buffer_size * sizeof(lv_color_t), LV_DISPLAY_RENDER_MODE_PARTIAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
lv_display_set_flush_cb(disp, lvgl_port_flush_callback);
|
||||||
|
lv_display_add_event_cb(disp, lvgl_port_disp_size_update_callback, LV_EVENT_RESOLUTION_CHANGED, disp_ctx);
|
||||||
|
lv_display_add_event_cb(disp, lvgl_port_display_invalidate_callback, LV_EVENT_INVALIDATE_AREA, disp_ctx);
|
||||||
|
lv_display_add_event_cb(disp, lvgl_port_display_invalidate_callback, LV_EVENT_REFR_REQUEST, disp_ctx);
|
||||||
|
|
||||||
|
lv_display_set_driver_data(disp, disp_ctx);
|
||||||
|
disp_ctx->disp_drv = disp;
|
||||||
|
|
||||||
|
/* Use SW rotation */
|
||||||
|
if (disp_cfg->flags.sw_rotate) {
|
||||||
|
disp_ctx->draw_buffs[2] = heap_caps_malloc(buffer_size * sizeof(lv_color_t), buff_caps);
|
||||||
|
ESP_GOTO_ON_FALSE(disp_ctx->draw_buffs[2], ESP_ERR_NO_MEM, err, TAG, "Not enough memory for LVGL buffer (rotation buffer) allocation!");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
err:
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
if (disp_ctx->draw_buffs[0]) {
|
||||||
|
free(disp_ctx->draw_buffs[0]);
|
||||||
|
}
|
||||||
|
if (disp_ctx->draw_buffs[1]) {
|
||||||
|
free(disp_ctx->draw_buffs[1]);
|
||||||
|
}
|
||||||
|
if (disp_ctx->draw_buffs[2]) {
|
||||||
|
free(disp_ctx->draw_buffs[2]);
|
||||||
|
}
|
||||||
|
if (disp_ctx->oled_buffer) {
|
||||||
|
free(disp_ctx->oled_buffer);
|
||||||
|
}
|
||||||
|
if (disp_ctx) {
|
||||||
|
free(disp_ctx);
|
||||||
|
}
|
||||||
|
if (trans_sem) {
|
||||||
|
vSemaphoreDelete(trans_sem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return disp;
|
||||||
|
}
|
||||||
|
|
||||||
#if LVGL_PORT_HANDLE_FLUSH_READY
|
#if LVGL_PORT_HANDLE_FLUSH_READY
|
||||||
static bool lvgl_port_flush_ready_callback(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx)
|
static bool lvgl_port_flush_io_ready_callback(esp_lcd_panel_io_handle_t panel_io, esp_lcd_panel_io_event_data_t *edata, void *user_ctx)
|
||||||
{
|
{
|
||||||
lv_display_t *disp_drv = (lv_display_t *)user_ctx;
|
lv_display_t *disp_drv = (lv_display_t *)user_ctx;
|
||||||
assert(disp_drv != NULL);
|
assert(disp_drv != NULL);
|
||||||
lv_disp_flush_ready(disp_drv);
|
lv_disp_flush_ready(disp_drv);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if (CONFIG_IDF_TARGET_ESP32P4 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0))
|
||||||
|
static bool lvgl_port_flush_dpi_panel_ready_callback(esp_lcd_panel_handle_t panel_io, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx)
|
||||||
|
{
|
||||||
|
lv_display_t *disp_drv = (lv_display_t *)user_ctx;
|
||||||
|
assert(disp_drv != NULL);
|
||||||
|
lv_disp_flush_ready(disp_drv);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool lvgl_port_flush_dpi_vsync_ready_callback(esp_lcd_panel_handle_t panel_io, esp_lcd_dpi_panel_event_data_t *edata, void *user_ctx)
|
||||||
|
{
|
||||||
|
BaseType_t need_yield = pdFALSE;
|
||||||
|
|
||||||
|
lv_display_t *disp_drv = (lv_display_t *)user_ctx;
|
||||||
|
assert(disp_drv != NULL);
|
||||||
|
lvgl_port_display_ctx_t *disp_ctx = (lvgl_port_display_ctx_t *)lv_display_get_driver_data(disp_drv);
|
||||||
|
assert(disp_ctx != NULL);
|
||||||
|
|
||||||
|
if (disp_ctx->trans_sem) {
|
||||||
|
xSemaphoreGiveFromISR(disp_ctx->trans_sem, &need_yield);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (need_yield == pdTRUE);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
static void _lvgl_port_transform_monochrome(lv_display_t *display, const lv_area_t *area, uint8_t *color_map)
|
#if CONFIG_IDF_TARGET_ESP32S3 && ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||||
|
static bool lvgl_port_flush_rgb_vsync_ready_callback(esp_lcd_panel_handle_t panel_io, const esp_lcd_rgb_panel_event_data_t *edata, void *user_ctx)
|
||||||
{
|
{
|
||||||
uint8_t *buf = color_map;
|
BaseType_t need_yield = pdFALSE;
|
||||||
lv_color16_t *color = (lv_color16_t *)color_map;
|
|
||||||
|
lv_display_t *disp_drv = (lv_display_t *)user_ctx;
|
||||||
|
assert(disp_drv != NULL);
|
||||||
|
lvgl_port_display_ctx_t *disp_ctx = (lvgl_port_display_ctx_t *)lv_display_get_driver_data(disp_drv);
|
||||||
|
assert(disp_ctx != NULL);
|
||||||
|
|
||||||
|
if (disp_ctx->trans_sem) {
|
||||||
|
xSemaphoreGiveFromISR(disp_ctx->trans_sem, &need_yield);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (need_yield == pdTRUE);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void _lvgl_port_transform_monochrome(lv_display_t *display, const lv_area_t *area, uint8_t **color_map)
|
||||||
|
{
|
||||||
|
assert(color_map);
|
||||||
|
assert(*color_map);
|
||||||
|
uint8_t *src = *color_map;
|
||||||
|
lv_color16_t *color = (lv_color16_t *)*color_map;
|
||||||
|
lvgl_port_display_ctx_t *disp_ctx = (lvgl_port_display_ctx_t *)lv_display_get_driver_data(display);
|
||||||
uint16_t hor_res = lv_display_get_physical_horizontal_resolution(display);
|
uint16_t hor_res = lv_display_get_physical_horizontal_resolution(display);
|
||||||
uint16_t ver_res = lv_display_get_physical_vertical_resolution(display);
|
uint16_t ver_res = lv_display_get_physical_vertical_resolution(display);
|
||||||
uint16_t res = hor_res;
|
uint16_t res = hor_res;
|
||||||
@ -199,10 +472,24 @@ static void _lvgl_port_transform_monochrome(lv_display_t *display, const lv_area
|
|||||||
int y1 = area->y1;
|
int y1 = area->y1;
|
||||||
int y2 = area->y2;
|
int y2 = area->y2;
|
||||||
|
|
||||||
|
lv_color_format_t color_format = lv_display_get_color_format(display);
|
||||||
|
if (color_format == LV_COLOR_FORMAT_I1) {
|
||||||
|
// This is necessary because LVGL reserves 2 x 4 bytes in the buffer, as these are assumed to be used as a palette. Skip the palette here
|
||||||
|
// More information about the monochrome, please refer to https://docs.lvgl.io/9.2/porting/display.html#monochrome-displays
|
||||||
|
src += 8;
|
||||||
|
/*Use oled_buffer as output */
|
||||||
|
*color_map = disp_ctx->oled_buffer;
|
||||||
|
}
|
||||||
|
|
||||||
int out_x, out_y;
|
int out_x, out_y;
|
||||||
for (int y = y1; y <= y2; y++) {
|
for (int y = y1; y <= y2; y++) {
|
||||||
for (int x = x1; x <= x2; x++) {
|
for (int x = x1; x <= x2; x++) {
|
||||||
bool chroma_color = (color[hor_res * y + x].blue > 16);
|
bool chroma_color = 0;
|
||||||
|
if (color_format == LV_COLOR_FORMAT_I1) {
|
||||||
|
chroma_color = (src[(hor_res >> 3) * y + (x >> 3)] & 1 << (7 - x % 8));
|
||||||
|
} else {
|
||||||
|
chroma_color = (color[hor_res * y + x].blue > 16);
|
||||||
|
}
|
||||||
|
|
||||||
if (swap_xy) {
|
if (swap_xy) {
|
||||||
out_x = y;
|
out_x = y;
|
||||||
@ -216,77 +503,174 @@ static void _lvgl_port_transform_monochrome(lv_display_t *display, const lv_area
|
|||||||
|
|
||||||
/* Write to the buffer as required for the display.
|
/* Write to the buffer as required for the display.
|
||||||
* It writes only 1-bit for monochrome displays mapped vertically.*/
|
* It writes only 1-bit for monochrome displays mapped vertically.*/
|
||||||
buf = color_map + res * (out_y >> 3) + (out_x);
|
uint8_t *outbuf = NULL;
|
||||||
|
outbuf = *color_map + res * (out_y >> 3) + (out_x);
|
||||||
if (chroma_color) {
|
if (chroma_color) {
|
||||||
(*buf) &= ~(1 << (out_y % 8));
|
(*outbuf) &= ~(1 << (out_y % 8));
|
||||||
} else {
|
} else {
|
||||||
(*buf) |= (1 << (out_y % 8));
|
(*outbuf) |= (1 << (out_y % 8));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void lvgl_port_rotate_area(lv_display_t *disp, lv_area_t *area)
|
||||||
|
{
|
||||||
|
lv_display_rotation_t rotation = lv_display_get_rotation(disp);
|
||||||
|
|
||||||
|
int32_t w = lv_area_get_width(area);
|
||||||
|
int32_t h = lv_area_get_height(area);
|
||||||
|
|
||||||
|
int32_t hres = lv_display_get_horizontal_resolution(disp);
|
||||||
|
int32_t vres = lv_display_get_vertical_resolution(disp);
|
||||||
|
if (rotation == LV_DISPLAY_ROTATION_90 || rotation == LV_DISPLAY_ROTATION_270) {
|
||||||
|
vres = lv_display_get_horizontal_resolution(disp);
|
||||||
|
hres = lv_display_get_vertical_resolution(disp);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (rotation) {
|
||||||
|
case LV_DISPLAY_ROTATION_0:
|
||||||
|
return;
|
||||||
|
case LV_DISPLAY_ROTATION_90:
|
||||||
|
area->y2 = vres - area->x1 - 1;
|
||||||
|
area->x1 = area->y1;
|
||||||
|
area->x2 = area->x1 + h - 1;
|
||||||
|
area->y1 = area->y2 - w + 1;
|
||||||
|
break;
|
||||||
|
case LV_DISPLAY_ROTATION_180:
|
||||||
|
area->y2 = vres - area->y1 - 1;
|
||||||
|
area->y1 = area->y2 - h + 1;
|
||||||
|
area->x2 = hres - area->x1 - 1;
|
||||||
|
area->x1 = area->x2 - w + 1;
|
||||||
|
break;
|
||||||
|
case LV_DISPLAY_ROTATION_270:
|
||||||
|
area->x1 = hres - area->y2 - 1;
|
||||||
|
area->y2 = area->x2;
|
||||||
|
area->x2 = area->x1 + h - 1;
|
||||||
|
area->y1 = area->y2 - w + 1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void lvgl_port_flush_callback(lv_display_t *drv, const lv_area_t *area, uint8_t *color_map)
|
static void lvgl_port_flush_callback(lv_display_t *drv, const lv_area_t *area, uint8_t *color_map)
|
||||||
{
|
{
|
||||||
assert(drv != NULL);
|
assert(drv != NULL);
|
||||||
|
assert(area != NULL);
|
||||||
|
assert(color_map != NULL);
|
||||||
lvgl_port_display_ctx_t *disp_ctx = (lvgl_port_display_ctx_t *)lv_display_get_driver_data(drv);
|
lvgl_port_display_ctx_t *disp_ctx = (lvgl_port_display_ctx_t *)lv_display_get_driver_data(drv);
|
||||||
assert(disp_ctx != NULL);
|
assert(disp_ctx != NULL);
|
||||||
|
|
||||||
//TODO: try to use SPI_SWAP_DATA_RX from https://docs.espressif.com/projects/esp-idf/en/v5.1/esp32s3/api-reference/peripherals/spi_master.html#c.SPI_SWAP_DATA_TX
|
int offsetx1 = area->x1;
|
||||||
if (disp_ctx->flags.swap_bytes) {
|
int offsetx2 = area->x2;
|
||||||
|
int offsety1 = area->y1;
|
||||||
|
int offsety2 = area->y2;
|
||||||
|
|
||||||
|
/* SW rotation enabled */
|
||||||
|
if (disp_ctx->flags.sw_rotate && (disp_ctx->current_rotation > LV_DISPLAY_ROTATION_0 || disp_ctx->flags.swap_bytes)) {
|
||||||
|
/* SW rotation */
|
||||||
|
if (disp_ctx->draw_buffs[2]) {
|
||||||
|
int32_t ww = lv_area_get_width(area);
|
||||||
|
int32_t hh = lv_area_get_height(area);
|
||||||
|
lv_color_format_t cf = lv_display_get_color_format(drv);
|
||||||
|
uint32_t w_stride = lv_draw_buf_width_to_stride(ww, cf);
|
||||||
|
uint32_t h_stride = lv_draw_buf_width_to_stride(hh, cf);
|
||||||
|
if (disp_ctx->current_rotation == LV_DISPLAY_ROTATION_180) {
|
||||||
|
lv_draw_sw_rotate(color_map, disp_ctx->draw_buffs[2], hh, ww, h_stride, h_stride, LV_DISPLAY_ROTATION_180, cf);
|
||||||
|
} else if (disp_ctx->current_rotation == LV_DISPLAY_ROTATION_90) {
|
||||||
|
lv_draw_sw_rotate(color_map, disp_ctx->draw_buffs[2], ww, hh, w_stride, h_stride, LV_DISPLAY_ROTATION_90, cf);
|
||||||
|
} else if (disp_ctx->current_rotation == LV_DISPLAY_ROTATION_270) {
|
||||||
|
lv_draw_sw_rotate(color_map, disp_ctx->draw_buffs[2], ww, hh, w_stride, h_stride, LV_DISPLAY_ROTATION_270, cf);
|
||||||
|
}
|
||||||
|
color_map = (uint8_t *)disp_ctx->draw_buffs[2];
|
||||||
|
lvgl_port_rotate_area(drv, (lv_area_t *)area);
|
||||||
|
offsetx1 = area->x1;
|
||||||
|
offsetx2 = area->x2;
|
||||||
|
offsety1 = area->y1;
|
||||||
|
offsety2 = area->y2;
|
||||||
|
}
|
||||||
|
} else if (disp_ctx->flags.swap_bytes) {
|
||||||
size_t len = lv_area_get_size(area);
|
size_t len = lv_area_get_size(area);
|
||||||
lv_draw_sw_rgb565_swap(color_map, len);
|
lv_draw_sw_rgb565_swap(color_map, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Transfor data in buffer for monochromatic screen */
|
/* Transfer data in buffer for monochromatic screen */
|
||||||
if (disp_ctx->flags.monochrome) {
|
if (disp_ctx->flags.monochrome) {
|
||||||
_lvgl_port_transform_monochrome(drv, area, color_map);
|
_lvgl_port_transform_monochrome(drv, area, &color_map);
|
||||||
}
|
}
|
||||||
|
|
||||||
const int offsetx1 = area->x1;
|
if ((disp_ctx->disp_type == LVGL_PORT_DISP_TYPE_RGB || disp_ctx->disp_type == LVGL_PORT_DISP_TYPE_DSI) && (disp_ctx->flags.direct_mode || disp_ctx->flags.full_refresh)) {
|
||||||
const int offsetx2 = area->x2;
|
if (lv_disp_flush_is_last(drv)) {
|
||||||
const int offsety1 = area->y1;
|
/* If the interface is I80 or SPI, this step cannot be used for drawing. */
|
||||||
const int offsety2 = area->y2;
|
esp_lcd_panel_draw_bitmap(disp_ctx->panel_handle, 0, 0, lv_disp_get_hor_res(drv), lv_disp_get_ver_res(drv), color_map);
|
||||||
// copy a buffer's content to a specific area of the display
|
/* Waiting for the last frame buffer to complete transmission */
|
||||||
esp_lcd_panel_draw_bitmap(disp_ctx->panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map);
|
xSemaphoreTake(disp_ctx->trans_sem, 0);
|
||||||
|
xSemaphoreTake(disp_ctx->trans_sem, portMAX_DELAY);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
esp_lcd_panel_draw_bitmap(disp_ctx->panel_handle, offsetx1, offsety1, offsetx2 + 1, offsety2 + 1, color_map);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (disp_ctx->disp_type == LVGL_PORT_DISP_TYPE_RGB || (disp_ctx->disp_type == LVGL_PORT_DISP_TYPE_DSI && (disp_ctx->flags.direct_mode || disp_ctx->flags.full_refresh))) {
|
||||||
|
lv_disp_flush_ready(drv);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lvgl_port_disp_rotation_update(lvgl_port_display_ctx_t *disp_ctx)
|
||||||
|
{
|
||||||
|
assert(disp_ctx != NULL);
|
||||||
|
|
||||||
|
disp_ctx->current_rotation = lv_display_get_rotation(disp_ctx->disp_drv);
|
||||||
|
if (disp_ctx->flags.sw_rotate) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_lcd_panel_handle_t control_handle = (disp_ctx->control_handle ? disp_ctx->control_handle : disp_ctx->panel_handle);
|
||||||
|
/* Solve rotation screen and touch */
|
||||||
|
switch (lv_display_get_rotation(disp_ctx->disp_drv)) {
|
||||||
|
case LV_DISPLAY_ROTATION_0:
|
||||||
|
/* Rotate LCD display */
|
||||||
|
esp_lcd_panel_swap_xy(control_handle, disp_ctx->rotation.swap_xy);
|
||||||
|
esp_lcd_panel_mirror(control_handle, disp_ctx->rotation.mirror_x, disp_ctx->rotation.mirror_y);
|
||||||
|
break;
|
||||||
|
case LV_DISPLAY_ROTATION_90:
|
||||||
|
/* Rotate LCD display */
|
||||||
|
esp_lcd_panel_swap_xy(control_handle, !disp_ctx->rotation.swap_xy);
|
||||||
|
if (disp_ctx->rotation.swap_xy) {
|
||||||
|
esp_lcd_panel_mirror(control_handle, !disp_ctx->rotation.mirror_x, disp_ctx->rotation.mirror_y);
|
||||||
|
} else {
|
||||||
|
esp_lcd_panel_mirror(control_handle, disp_ctx->rotation.mirror_x, !disp_ctx->rotation.mirror_y);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case LV_DISPLAY_ROTATION_180:
|
||||||
|
/* Rotate LCD display */
|
||||||
|
esp_lcd_panel_swap_xy(control_handle, disp_ctx->rotation.swap_xy);
|
||||||
|
esp_lcd_panel_mirror(control_handle, !disp_ctx->rotation.mirror_x, !disp_ctx->rotation.mirror_y);
|
||||||
|
break;
|
||||||
|
case LV_DISPLAY_ROTATION_270:
|
||||||
|
/* Rotate LCD display */
|
||||||
|
esp_lcd_panel_swap_xy(control_handle, !disp_ctx->rotation.swap_xy);
|
||||||
|
if (disp_ctx->rotation.swap_xy) {
|
||||||
|
esp_lcd_panel_mirror(control_handle, disp_ctx->rotation.mirror_x, !disp_ctx->rotation.mirror_y);
|
||||||
|
} else {
|
||||||
|
esp_lcd_panel_mirror(control_handle, !disp_ctx->rotation.mirror_x, disp_ctx->rotation.mirror_y);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wake LVGL task, if needed */
|
||||||
|
lvgl_port_task_wake(LVGL_PORT_EVENT_DISPLAY, disp_ctx->disp_drv);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void lvgl_port_disp_size_update_callback(lv_event_t *e)
|
static void lvgl_port_disp_size_update_callback(lv_event_t *e)
|
||||||
{
|
{
|
||||||
assert(e);
|
assert(e);
|
||||||
lvgl_port_display_ctx_t *disp_ctx = (lvgl_port_display_ctx_t *)e->user_data;
|
lvgl_port_display_ctx_t *disp_ctx = (lvgl_port_display_ctx_t *)lv_event_get_user_data(e);
|
||||||
assert(disp_ctx != NULL);
|
lvgl_port_disp_rotation_update(disp_ctx);
|
||||||
esp_lcd_panel_handle_t panel_handle = disp_ctx->panel_handle;
|
}
|
||||||
|
|
||||||
/* Solve rotation screen and touch */
|
static void lvgl_port_display_invalidate_callback(lv_event_t *e)
|
||||||
switch (lv_display_get_rotation(disp_ctx->disp_drv)) {
|
{
|
||||||
case LV_DISPLAY_ROTATION_0:
|
/* Wake LVGL task, if needed */
|
||||||
/* Rotate LCD display */
|
lvgl_port_task_wake(LVGL_PORT_EVENT_DISPLAY, NULL);
|
||||||
esp_lcd_panel_swap_xy(panel_handle, disp_ctx->rotation.swap_xy);
|
|
||||||
esp_lcd_panel_mirror(panel_handle, disp_ctx->rotation.mirror_x, disp_ctx->rotation.mirror_y);
|
|
||||||
break;
|
|
||||||
case LV_DISPLAY_ROTATION_90:
|
|
||||||
/* Rotate LCD display */
|
|
||||||
esp_lcd_panel_swap_xy(panel_handle, !disp_ctx->rotation.swap_xy);
|
|
||||||
if (disp_ctx->rotation.swap_xy) {
|
|
||||||
esp_lcd_panel_mirror(panel_handle, !disp_ctx->rotation.mirror_x, disp_ctx->rotation.mirror_y);
|
|
||||||
} else {
|
|
||||||
esp_lcd_panel_mirror(panel_handle, disp_ctx->rotation.mirror_x, !disp_ctx->rotation.mirror_y);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case LV_DISPLAY_ROTATION_180:
|
|
||||||
/* Rotate LCD display */
|
|
||||||
esp_lcd_panel_swap_xy(panel_handle, disp_ctx->rotation.swap_xy);
|
|
||||||
esp_lcd_panel_mirror(panel_handle, !disp_ctx->rotation.mirror_x, !disp_ctx->rotation.mirror_y);
|
|
||||||
break;
|
|
||||||
case LV_DISPLAY_ROTATION_270:
|
|
||||||
/* Rotate LCD display */
|
|
||||||
esp_lcd_panel_swap_xy(panel_handle, !disp_ctx->rotation.swap_xy);
|
|
||||||
if (disp_ctx->rotation.swap_xy) {
|
|
||||||
esp_lcd_panel_mirror(panel_handle, disp_ctx->rotation.mirror_x, !disp_ctx->rotation.mirror_y);
|
|
||||||
} else {
|
|
||||||
esp_lcd_panel_mirror(panel_handle, !disp_ctx->rotation.mirror_x, disp_ctx->rotation.mirror_y);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -29,6 +29,7 @@ typedef struct {
|
|||||||
static void lvgl_port_encoder_read(lv_indev_t *indev_drv, lv_indev_data_t *data);
|
static void lvgl_port_encoder_read(lv_indev_t *indev_drv, lv_indev_data_t *data);
|
||||||
static void lvgl_port_encoder_btn_down_handler(void *arg, void *arg2);
|
static void lvgl_port_encoder_btn_down_handler(void *arg, void *arg2);
|
||||||
static void lvgl_port_encoder_btn_up_handler(void *arg, void *arg2);
|
static void lvgl_port_encoder_btn_up_handler(void *arg, void *arg2);
|
||||||
|
static void lvgl_port_encoder_knob_handler(void *arg, void *arg2);
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Public API functions
|
* Public API functions
|
||||||
@ -52,6 +53,9 @@ lv_indev_t *lvgl_port_add_encoder(const lvgl_port_encoder_cfg_t *encoder_cfg)
|
|||||||
if (encoder_cfg->encoder_a_b != NULL) {
|
if (encoder_cfg->encoder_a_b != NULL) {
|
||||||
encoder_ctx->knob_handle = iot_knob_create(encoder_cfg->encoder_a_b);
|
encoder_ctx->knob_handle = iot_knob_create(encoder_cfg->encoder_a_b);
|
||||||
ESP_GOTO_ON_FALSE(encoder_ctx->knob_handle, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for knob create!");
|
ESP_GOTO_ON_FALSE(encoder_ctx->knob_handle, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for knob create!");
|
||||||
|
|
||||||
|
ESP_ERROR_CHECK(iot_knob_register_cb(encoder_ctx->knob_handle, KNOB_LEFT, lvgl_port_encoder_knob_handler, encoder_ctx));
|
||||||
|
ESP_ERROR_CHECK(iot_knob_register_cb(encoder_ctx->knob_handle, KNOB_RIGHT, lvgl_port_encoder_knob_handler, encoder_ctx));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Encoder Enter */
|
/* Encoder Enter */
|
||||||
@ -69,12 +73,15 @@ lv_indev_t *lvgl_port_add_encoder(const lvgl_port_encoder_cfg_t *encoder_cfg)
|
|||||||
/* Register a encoder input device */
|
/* Register a encoder input device */
|
||||||
indev = lv_indev_create();
|
indev = lv_indev_create();
|
||||||
lv_indev_set_type(indev, LV_INDEV_TYPE_ENCODER);
|
lv_indev_set_type(indev, LV_INDEV_TYPE_ENCODER);
|
||||||
|
lv_indev_set_mode(indev, LV_INDEV_MODE_EVENT);
|
||||||
lv_indev_set_read_cb(indev, lvgl_port_encoder_read);
|
lv_indev_set_read_cb(indev, lvgl_port_encoder_read);
|
||||||
lv_indev_set_disp(indev, encoder_cfg->disp);
|
lv_indev_set_disp(indev, encoder_cfg->disp);
|
||||||
lv_indev_set_driver_data(indev, encoder_ctx);
|
lv_indev_set_driver_data(indev, encoder_ctx);
|
||||||
encoder_ctx->indev = indev;
|
encoder_ctx->indev = indev;
|
||||||
lvgl_port_unlock();
|
lvgl_port_unlock();
|
||||||
|
|
||||||
|
return indev;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
if (ret != ESP_OK) {
|
if (ret != ESP_OK) {
|
||||||
if (encoder_ctx->knob_handle != NULL) {
|
if (encoder_ctx->knob_handle != NULL) {
|
||||||
@ -89,7 +96,7 @@ err:
|
|||||||
free(encoder_ctx);
|
free(encoder_ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return encoder_ctx->indev;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
esp_err_t lvgl_port_remove_encoder(lv_indev_t *encoder)
|
esp_err_t lvgl_port_remove_encoder(lv_indev_t *encoder)
|
||||||
@ -150,6 +157,9 @@ static void lvgl_port_encoder_btn_down_handler(void *arg, void *arg2)
|
|||||||
ctx->btn_enter = true;
|
ctx->btn_enter = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Wake LVGL task, if needed */
|
||||||
|
lvgl_port_task_wake(LVGL_PORT_EVENT_TOUCH, ctx->indev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void lvgl_port_encoder_btn_up_handler(void *arg, void *arg2)
|
static void lvgl_port_encoder_btn_up_handler(void *arg, void *arg2)
|
||||||
@ -162,4 +172,14 @@ static void lvgl_port_encoder_btn_up_handler(void *arg, void *arg2)
|
|||||||
ctx->btn_enter = false;
|
ctx->btn_enter = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Wake LVGL task, if needed */
|
||||||
|
lvgl_port_task_wake(LVGL_PORT_EVENT_TOUCH, ctx->indev);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lvgl_port_encoder_knob_handler(void *arg, void *arg2)
|
||||||
|
{
|
||||||
|
lvgl_port_encoder_ctx_t *ctx = (lvgl_port_encoder_ctx_t *) arg2;
|
||||||
|
/* Wake LVGL task, if needed */
|
||||||
|
lvgl_port_task_wake(LVGL_PORT_EVENT_TOUCH, ctx->indev);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,6 +26,7 @@ typedef struct {
|
|||||||
*******************************************************************************/
|
*******************************************************************************/
|
||||||
|
|
||||||
static void lvgl_port_touchpad_read(lv_indev_t *indev_drv, lv_indev_data_t *data);
|
static void lvgl_port_touchpad_read(lv_indev_t *indev_drv, lv_indev_data_t *data);
|
||||||
|
static void lvgl_port_touch_interrupt_callback(esp_lcd_touch_handle_t tp);
|
||||||
|
|
||||||
/*******************************************************************************
|
/*******************************************************************************
|
||||||
* Public API functions
|
* Public API functions
|
||||||
@ -33,7 +34,8 @@ static void lvgl_port_touchpad_read(lv_indev_t *indev_drv, lv_indev_data_t *data
|
|||||||
|
|
||||||
lv_indev_t *lvgl_port_add_touch(const lvgl_port_touch_cfg_t *touch_cfg)
|
lv_indev_t *lvgl_port_add_touch(const lvgl_port_touch_cfg_t *touch_cfg)
|
||||||
{
|
{
|
||||||
lv_indev_t *indev;
|
esp_err_t ret = ESP_OK;
|
||||||
|
lv_indev_t *indev = NULL;
|
||||||
assert(touch_cfg != NULL);
|
assert(touch_cfg != NULL);
|
||||||
assert(touch_cfg->disp != NULL);
|
assert(touch_cfg->disp != NULL);
|
||||||
assert(touch_cfg->handle != NULL);
|
assert(touch_cfg->handle != NULL);
|
||||||
@ -46,16 +48,33 @@ lv_indev_t *lvgl_port_add_touch(const lvgl_port_touch_cfg_t *touch_cfg)
|
|||||||
}
|
}
|
||||||
touch_ctx->handle = touch_cfg->handle;
|
touch_ctx->handle = touch_cfg->handle;
|
||||||
|
|
||||||
|
if (touch_ctx->handle->config.int_gpio_num != GPIO_NUM_NC) {
|
||||||
|
/* Register touch interrupt callback */
|
||||||
|
ret = esp_lcd_touch_register_interrupt_callback_with_data(touch_ctx->handle, lvgl_port_touch_interrupt_callback, touch_ctx);
|
||||||
|
ESP_GOTO_ON_ERROR(ret, err, TAG, "Error in register touch interrupt.");
|
||||||
|
}
|
||||||
|
|
||||||
lvgl_port_lock(0);
|
lvgl_port_lock(0);
|
||||||
/* Register a touchpad input device */
|
/* Register a touchpad input device */
|
||||||
indev = lv_indev_create();
|
indev = lv_indev_create();
|
||||||
lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
|
lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
|
||||||
|
/* Event mode can be set only, when touch interrupt enabled */
|
||||||
|
if (touch_ctx->handle->config.int_gpio_num != GPIO_NUM_NC) {
|
||||||
|
lv_indev_set_mode(indev, LV_INDEV_MODE_EVENT);
|
||||||
|
}
|
||||||
lv_indev_set_read_cb(indev, lvgl_port_touchpad_read);
|
lv_indev_set_read_cb(indev, lvgl_port_touchpad_read);
|
||||||
lv_indev_set_disp(indev, touch_cfg->disp);
|
lv_indev_set_disp(indev, touch_cfg->disp);
|
||||||
lv_indev_set_driver_data(indev, touch_ctx);
|
lv_indev_set_driver_data(indev, touch_ctx);
|
||||||
touch_ctx->indev = indev;
|
touch_ctx->indev = indev;
|
||||||
lvgl_port_unlock();
|
lvgl_port_unlock();
|
||||||
|
|
||||||
|
err:
|
||||||
|
if (ret != ESP_OK) {
|
||||||
|
if (touch_ctx) {
|
||||||
|
free(touch_ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return indev;
|
return indev;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,6 +88,11 @@ esp_err_t lvgl_port_remove_touch(lv_indev_t *touch)
|
|||||||
lv_indev_delete(touch);
|
lv_indev_delete(touch);
|
||||||
lvgl_port_unlock();
|
lvgl_port_unlock();
|
||||||
|
|
||||||
|
if (touch_ctx->handle->config.int_gpio_num != GPIO_NUM_NC) {
|
||||||
|
/* Unregister touch interrupt callback */
|
||||||
|
esp_lcd_touch_register_interrupt_callback(touch_ctx->handle, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
if (touch_ctx) {
|
if (touch_ctx) {
|
||||||
free(touch_ctx);
|
free(touch_ctx);
|
||||||
}
|
}
|
||||||
@ -105,3 +129,11 @@ static void lvgl_port_touchpad_read(lv_indev_t *indev_drv, lv_indev_data_t *data
|
|||||||
data->state = LV_INDEV_STATE_RELEASED;
|
data->state = LV_INDEV_STATE_RELEASED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void IRAM_ATTR lvgl_port_touch_interrupt_callback(esp_lcd_touch_handle_t tp)
|
||||||
|
{
|
||||||
|
lvgl_port_touch_ctx_t *touch_ctx = (lvgl_port_touch_ctx_t *) tp->config.user_data;
|
||||||
|
|
||||||
|
/* Wake LVGL task, if needed */
|
||||||
|
lvgl_port_task_wake(LVGL_PORT_EVENT_TOUCH, touch_ctx->indev);
|
||||||
|
}
|
||||||
|
|||||||
@ -4,10 +4,14 @@
|
|||||||
* SPDX-License-Identifier: Apache-2.0
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
#include "esp_check.h"
|
#include "esp_check.h"
|
||||||
#include "esp_lvgl_port.h"
|
#include "esp_lvgl_port.h"
|
||||||
|
#include "freertos/FreeRTOS.h"
|
||||||
|
#include "freertos/task.h"
|
||||||
|
#include "freertos/semphr.h"
|
||||||
|
|
||||||
#include "usb/hid_host.h"
|
#include "usb/hid_host.h"
|
||||||
#include "usb/hid_usage_keyboard.h"
|
#include "usb/hid_usage_keyboard.h"
|
||||||
@ -91,6 +95,7 @@ lv_indev_t *lvgl_port_add_usb_hid_mouse_input(const lvgl_port_hid_mouse_cfg_t *m
|
|||||||
/* Register a mouse input device */
|
/* Register a mouse input device */
|
||||||
indev = lv_indev_create();
|
indev = lv_indev_create();
|
||||||
lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
|
lv_indev_set_type(indev, LV_INDEV_TYPE_POINTER);
|
||||||
|
lv_indev_set_mode(indev, LV_INDEV_MODE_EVENT);
|
||||||
lv_indev_set_read_cb(indev, lvgl_port_usb_hid_read_mouse);
|
lv_indev_set_read_cb(indev, lvgl_port_usb_hid_read_mouse);
|
||||||
lv_indev_set_disp(indev, mouse_cfg->disp);
|
lv_indev_set_disp(indev, mouse_cfg->disp);
|
||||||
lv_indev_set_driver_data(indev, hid_ctx);
|
lv_indev_set_driver_data(indev, hid_ctx);
|
||||||
@ -124,6 +129,7 @@ lv_indev_t *lvgl_port_add_usb_hid_keyboard_input(const lvgl_port_hid_keyboard_cf
|
|||||||
/* Register a mouse input device */
|
/* Register a mouse input device */
|
||||||
indev = lv_indev_create();
|
indev = lv_indev_create();
|
||||||
lv_indev_set_type(indev, LV_INDEV_TYPE_KEYPAD);
|
lv_indev_set_type(indev, LV_INDEV_TYPE_KEYPAD);
|
||||||
|
lv_indev_set_mode(indev, LV_INDEV_MODE_EVENT);
|
||||||
lv_indev_set_read_cb(indev, lvgl_port_usb_hid_read_kb);
|
lv_indev_set_read_cb(indev, lvgl_port_usb_hid_read_kb);
|
||||||
lv_indev_set_disp(indev, keyboard_cfg->disp);
|
lv_indev_set_disp(indev, keyboard_cfg->disp);
|
||||||
lv_indev_set_driver_data(indev, hid_ctx);
|
lv_indev_set_driver_data(indev, hid_ctx);
|
||||||
@ -325,6 +331,8 @@ static void lvgl_port_usb_hid_host_interface_callback(hid_host_device_handle_t h
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Wake LVGL task, if needed */
|
||||||
|
lvgl_port_task_wake(LVGL_PORT_EVENT_TOUCH, hid_ctx->kb.indev);
|
||||||
} else if (dev.proto == HID_PROTOCOL_MOUSE) {
|
} else if (dev.proto == HID_PROTOCOL_MOUSE) {
|
||||||
hid_mouse_input_report_boot_t *mouse = (hid_mouse_input_report_boot_t *)data;
|
hid_mouse_input_report_boot_t *mouse = (hid_mouse_input_report_boot_t *)data;
|
||||||
if (data_length < sizeof(hid_mouse_input_report_boot_t)) {
|
if (data_length < sizeof(hid_mouse_input_report_boot_t)) {
|
||||||
@ -333,6 +341,9 @@ static void lvgl_port_usb_hid_host_interface_callback(hid_host_device_handle_t h
|
|||||||
hid_ctx->mouse.left_button = mouse->buttons.button1;
|
hid_ctx->mouse.left_button = mouse->buttons.button1;
|
||||||
hid_ctx->mouse.x += mouse->x_displacement;
|
hid_ctx->mouse.x += mouse->x_displacement;
|
||||||
hid_ctx->mouse.y += mouse->y_displacement;
|
hid_ctx->mouse.y += mouse->y_displacement;
|
||||||
|
|
||||||
|
/* Wake LVGL task, if needed */
|
||||||
|
lvgl_port_task_wake(LVGL_PORT_EVENT_TOUCH, hid_ctx->mouse.indev);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case HID_HOST_INTERFACE_EVENT_TRANSFER_ERROR:
|
case HID_HOST_INTERFACE_EVENT_TRANSFER_ERROR:
|
||||||
|
|||||||
@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This is LVGL ARGB8888 simple fill for ESP32 processor
|
||||||
|
|
||||||
|
.section .text
|
||||||
|
.align 4
|
||||||
|
.global lv_color_blend_to_argb8888_esp
|
||||||
|
.type lv_color_blend_to_argb8888_esp,@function
|
||||||
|
|
||||||
|
// The function implements the following C code:
|
||||||
|
// void lv_color_blend_to_argb8888(_lv_draw_sw_blend_fill_dsc_t * dsc);
|
||||||
|
|
||||||
|
// Input params
|
||||||
|
//
|
||||||
|
// dsc - a2
|
||||||
|
|
||||||
|
// typedef struct {
|
||||||
|
// uint32_t opa; l32i 0
|
||||||
|
// void * dst_buf; l32i 4
|
||||||
|
// uint32_t dst_w; l32i 8
|
||||||
|
// uint32_t dst_h; l32i 12
|
||||||
|
// uint32_t dst_stride; l32i 16
|
||||||
|
// const void * src_buf; l32i 20
|
||||||
|
// uint32_t src_stride; l32i 24
|
||||||
|
// const lv_opa_t * mask_buf; l32i 28
|
||||||
|
// uint32_t mask_stride; l32i 32
|
||||||
|
// } asm_dsc_t;
|
||||||
|
|
||||||
|
lv_color_blend_to_argb8888_esp:
|
||||||
|
|
||||||
|
entry a1, 32
|
||||||
|
|
||||||
|
l32i.n a3, a2, 4 // a3 - dest_buff
|
||||||
|
l32i.n a4, a2, 8 // a4 - dest_w in uint32_t
|
||||||
|
l32i.n a5, a2, 12 // a5 - dest_h in uint32_t
|
||||||
|
l32i.n a6, a2, 16 // a6 - dest_stride in bytes
|
||||||
|
l32i.n a7, a2, 20 // a7 - src_buff (color)
|
||||||
|
l32i.n a8, a7, 0 // a8 - color as value
|
||||||
|
slli a11, a4, 2 // a11 - dest_w_bytes = sizeof(uint32_t) * dest_w
|
||||||
|
|
||||||
|
movi a7, 0xff000000 // oppactiy mask
|
||||||
|
or a10, a7, a8 // apply oppacity
|
||||||
|
|
||||||
|
srli a9, a4, 2 // a9 - loop_len = dest_w / 4
|
||||||
|
sub a6, a6, a11 // dest_stride = dest_stride - dest_w_bytes
|
||||||
|
|
||||||
|
.outer_loop:
|
||||||
|
|
||||||
|
// Run main loop which sets 16 bytes in one loop run
|
||||||
|
loopnez a9, ._main_loop
|
||||||
|
s32i.n a10, a3, 0 // save 32 bits from a10 to dest_buff a3
|
||||||
|
s32i.n a10, a3, 4 // save 32 bits from a10 to dest_buff a3
|
||||||
|
s32i.n a10, a3, 8 // save 32 bits from a10 to dest_buff a3
|
||||||
|
s32i.n a10, a3, 12 // save 32 bits from a10 to dest_buff a3
|
||||||
|
addi.n a3, a3, 16 // increment dest_buff pointer by 16 bytes
|
||||||
|
._main_loop:
|
||||||
|
|
||||||
|
// Finish the remaining bytes out of the loop
|
||||||
|
// Check modulo 8 of the dest_w_bytes, if - then set 8 bytes
|
||||||
|
bbci a11, 3, _mod_8_check // branch if 2-nd bit of dest_w_bytes is clear
|
||||||
|
s32i.n a10, a3, 0 // save 32 bits from a10 to dest_buff a3, offset 0 bytes
|
||||||
|
s32i.n a10, a3, 4 // save 32 bits from a10 to dest_buff a3, offset 0 bytes
|
||||||
|
addi.n a3, a3, 8 // increment dest_buff pointer by 8 bytes
|
||||||
|
_mod_8_check:
|
||||||
|
|
||||||
|
// Check modulo 4 of the dest_w_bytes, if - then set 4 bytes
|
||||||
|
bbci a11, 2, _mod_4_check // branch if 2-nd bit of dest_w_bytes is clear
|
||||||
|
s32i.n a10, a3, 0 // save 32 bits from a10 to dest_buff a3, offset 0 bytes
|
||||||
|
addi.n a3, a3, 4 // increment dest_buff pointer by 4 bytes
|
||||||
|
_mod_4_check:
|
||||||
|
|
||||||
|
add a3, a3, a6 // dest_buff + dest_stride
|
||||||
|
addi.n a5, a5, -1 // decrease the outer loop
|
||||||
|
bnez a5, .outer_loop
|
||||||
|
|
||||||
|
movi.n a2, 1 // return LV_RESULT_OK = 1
|
||||||
|
retw.n // return
|
||||||
@ -0,0 +1,326 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This is LVGL ARGB8888 simple fill for ESP32S3 processor
|
||||||
|
|
||||||
|
.section .text
|
||||||
|
.align 4
|
||||||
|
.global lv_color_blend_to_argb8888_esp
|
||||||
|
.type lv_color_blend_to_argb8888_esp,@function
|
||||||
|
// The function implements the following C code:
|
||||||
|
// void lv_color_blend_to_argb8888(_lv_draw_sw_blend_fill_dsc_t * dsc);
|
||||||
|
|
||||||
|
// Input params
|
||||||
|
//
|
||||||
|
// dsc - a2
|
||||||
|
|
||||||
|
// typedef struct {
|
||||||
|
// uint32_t opa; l32i 0
|
||||||
|
// void * dst_buf; l32i 4
|
||||||
|
// uint32_t dst_w; l32i 8
|
||||||
|
// uint32_t dst_h; l32i 12
|
||||||
|
// uint32_t dst_stride; l32i 16
|
||||||
|
// const void * src_buf; l32i 20
|
||||||
|
// uint32_t src_stride; l32i 24
|
||||||
|
// const lv_opa_t * mask_buf; l32i 28
|
||||||
|
// uint32_t mask_stride; l32i 32
|
||||||
|
// } asm_dsc_t;
|
||||||
|
|
||||||
|
|
||||||
|
lv_color_blend_to_argb8888_esp:
|
||||||
|
|
||||||
|
entry a1, 32
|
||||||
|
ee.zero.q q0 // dummy TIE instruction, to enable the TIE
|
||||||
|
|
||||||
|
l32i.n a3, a2, 4 // a3 - dest_buff
|
||||||
|
l32i.n a4, a2, 8 // a4 - dest_w in uint32_t
|
||||||
|
l32i.n a5, a2, 12 // a5 - dest_h in uint32_t
|
||||||
|
l32i.n a6, a2, 16 // a6 - dest_stride in bytes
|
||||||
|
l32i.n a7, a2, 20 // a7 - src_buff (color)
|
||||||
|
l32i.n a8, a7, 0 // a8 - color as value
|
||||||
|
slli a11, a4, 2 // a11 - dest_w_bytes = sizeof(uint32_t) * dest_w
|
||||||
|
|
||||||
|
movi a7, 0xff000000 // oppactiy mask
|
||||||
|
or a10, a7, a8 // apply oppacity
|
||||||
|
|
||||||
|
// Check for short lengths
|
||||||
|
// dest_w should be at least 8, othewise it's not worth using esp32s3 TIE
|
||||||
|
bgei a4, 8, _esp32s3_implementation // Branch if dest_w is greater than or equal to 8
|
||||||
|
j .lv_color_blend_to_argb8888_esp32_body // Jump to esp32 implementation
|
||||||
|
|
||||||
|
_esp32s3_implementation:
|
||||||
|
|
||||||
|
ee.movi.32.q q0, a10, 0 // fill q0 register from a10 by 32 bits
|
||||||
|
ee.movi.32.q q0, a10, 1
|
||||||
|
ee.movi.32.q q0, a10, 2
|
||||||
|
ee.movi.32.q q0, a10, 3
|
||||||
|
|
||||||
|
// Check dest_buff alignment
|
||||||
|
movi.n a7, 0xf // 0xf alignment mask (16-byte alignment)
|
||||||
|
and a15, a7, a3 // 16-byte alignment mask AND dest_buff pointer
|
||||||
|
bnez a15, _unaligned_by_4byte // branch if a15 not equals to zero
|
||||||
|
|
||||||
|
// Check dest_stride alignment
|
||||||
|
and a15, a7, a6 // 16-byte alignment mask AND dest_stride
|
||||||
|
bnez a15, _unaligned_by_4byte // branch if a15 not equals to zero
|
||||||
|
|
||||||
|
// Check dest_w_bytes alignment
|
||||||
|
and a15, a7, a11 // 16-byte alignment mask AND dest_w_bytes
|
||||||
|
bnez a15, _unaligned_by_4byte // branch if a15 not equals to zero
|
||||||
|
|
||||||
|
//**********************************************************************************************************************
|
||||||
|
|
||||||
|
// all aligned, the most ideal case
|
||||||
|
|
||||||
|
// dest_buff (a3) - 16-byte aligned
|
||||||
|
// dest_stride (a6) - 16-byte multiple
|
||||||
|
// dest_w (a4) - 16-byte multiple
|
||||||
|
|
||||||
|
srli a9, a4, 2 // a9 - loop_len = dest_w / 4
|
||||||
|
sub a6, a6, a11 // dest_stride = dest_stride - dest_w_bytes
|
||||||
|
|
||||||
|
.outer_loop_aligned:
|
||||||
|
|
||||||
|
loopnez a9, ._main_loop_aligned // 16 bytes (4 argb8888) in one loop
|
||||||
|
ee.vst.128.ip q0, a3, 16 // store 16 bytes from q0 to dest_buff a3
|
||||||
|
._main_loop_aligned:
|
||||||
|
|
||||||
|
add a3, a3, a6 // dest_buff + dest_stride
|
||||||
|
addi.n a5, a5, -1 // decrease the outer loop
|
||||||
|
bnez a5, .outer_loop_aligned
|
||||||
|
|
||||||
|
movi.n a2, 1 // return LV_RESULT_OK = 1
|
||||||
|
retw.n // return
|
||||||
|
|
||||||
|
_unaligned_by_4byte:
|
||||||
|
|
||||||
|
// Check dest_buff alignment
|
||||||
|
movi.n a7, 0x3 // 0x3 alignment mask (4-byte alignment)
|
||||||
|
and a15, a7, a3 // 4-byte alignment mask AND dest_buff pointer
|
||||||
|
bnez a15, _unaligned_by_1byte // branch if a15 not equals to zero
|
||||||
|
|
||||||
|
// Check dest_stride alignment
|
||||||
|
and a15, a7, a6 // 4-byte alignment mask AND dest_stride pointer
|
||||||
|
bnez a15, _unaligned_by_1byte // branch if a15 not equals to zero
|
||||||
|
|
||||||
|
//**********************************************************************************************************************
|
||||||
|
|
||||||
|
// either dest_buff or dest_stride is not 16-byte aligned
|
||||||
|
// dest_w is always 4-byte multiple
|
||||||
|
// all of the following are 4-byte aligned
|
||||||
|
|
||||||
|
// dest_buff (a3) - 16-byte, or 4-byte aligned
|
||||||
|
// dest_stride (a6) - 16-byte, or 4-byte multiple
|
||||||
|
// dest_w (a4) - 4-byte multiple
|
||||||
|
|
||||||
|
sub a6, a6, a11 // dest_stride = dest_stride - dest_w_bytes
|
||||||
|
movi.n a7, 0xf // 0xf alignment mask
|
||||||
|
|
||||||
|
.outer_loop_aligned_by_4byte:
|
||||||
|
|
||||||
|
// alignment check
|
||||||
|
and a15, a7, a3 // 0xf (alignment mask) AND dest_buff pointer
|
||||||
|
mov a12, a11 // a12 - local_dest_w_bytes = dest_w_bytes
|
||||||
|
beqz a15, _dest_buff_aligned_by_4byte // branch if a15 equals to zero
|
||||||
|
|
||||||
|
|
||||||
|
movi.n a14, 16 // a14 - 16
|
||||||
|
sub a15, a14, a15 // a15 = 16 - unalignment (lower 4 bits of dest_buff address)
|
||||||
|
sub a12, a12, a15 // local_dest_w_bytes = len - (16 - unalignment)
|
||||||
|
|
||||||
|
// keep setting until dest_buff is aligned
|
||||||
|
// Check modulo 8 of the unalignment, if - then set 8 bytes
|
||||||
|
bbci a15, 3, _aligning_mod_8_check_4byte // branch if 3-rd bit of unalignment a15 is clear
|
||||||
|
s32i.n a10, a3, 0 // save 32 bits from a10 to dest_buff a3, offset 0 bytes
|
||||||
|
s32i.n a10, a3, 4 // save 32 bits from a10 to dest_buff a3, offset 4 bytes
|
||||||
|
addi.n a3, a3, 8 // increment dest_buff pointer by 8 bytes
|
||||||
|
_aligning_mod_8_check_4byte:
|
||||||
|
|
||||||
|
// Check modulo 4 of the unalignment, if - then set 4 bytes
|
||||||
|
bbci a15, 2, _aligning_mod_4_check_4byte // branch if 2-nd bit unalignment a15 is clear
|
||||||
|
s32i.n a10, a3, 0 // save 32 bits from a10 to dest_buff a3, offset 0 bytes
|
||||||
|
addi.n a3, a3, 4 // increment dest_buff pointer by 4 bytes
|
||||||
|
_aligning_mod_4_check_4byte:
|
||||||
|
|
||||||
|
_dest_buff_aligned_by_4byte:
|
||||||
|
// Calculate main loop_len
|
||||||
|
srli a9, a12, 4 // a9 - loop_len = local_dest_w_bytes / 16
|
||||||
|
|
||||||
|
// Main loop
|
||||||
|
loopnez a9, ._main_loop_unaligned_by_4byte // 16 bytes (4 argb8888) in one loop
|
||||||
|
ee.vst.128.ip q0, a3, 16 // store 16 bytes from q0 to dest_buff a3
|
||||||
|
._main_loop_unaligned_by_4byte:
|
||||||
|
|
||||||
|
// Check modulo 8 of the dest_w, if - then set 8 bytes
|
||||||
|
bbci a12, 3, _aligned_mod_8_check_4byte // branch if 3-rd bit of local_dest_w_bytes a12 is clear
|
||||||
|
ee.vst.l.64.ip q0, a3, 8 // save lower 64 bits from q0 to dest_buff a3, increase dest_buff pointer by 8 bytes
|
||||||
|
_aligned_mod_8_check_4byte:
|
||||||
|
|
||||||
|
// Check modulo 4 of the dest_w, if - then set 4 bytes
|
||||||
|
bbci a12, 2, _aligned_mod_4_check_4byte // branch if 2-nd bit of local_dest_w_bytes a12 is clear
|
||||||
|
s32i.n a10, a3, 0 // save 32 bits from a10 to dest_buff a3, offset 0 bytes
|
||||||
|
addi.n a3, a3, 4 // increment dest_buff pointer by 4 bytes
|
||||||
|
_aligned_mod_4_check_4byte:
|
||||||
|
|
||||||
|
add a3, a3, a6 // dest_buff + dest_stride
|
||||||
|
addi.n a5, a5, -1 // decrease the outer loop
|
||||||
|
bnez a5, .outer_loop_aligned_by_4byte
|
||||||
|
|
||||||
|
movi.n a2, 1 // return LV_RESULT_OK = 1
|
||||||
|
retw.n // return
|
||||||
|
|
||||||
|
_unaligned_by_1byte:
|
||||||
|
|
||||||
|
//**********************************************************************************************************************
|
||||||
|
|
||||||
|
// either dest_buff or dest_stride is not 4-byte aligned
|
||||||
|
// dest_w is always 4-byte multiple
|
||||||
|
|
||||||
|
// dest_buff (a3) - 4-byte, or 1-byte aligned
|
||||||
|
// dest_stride (a6) - 4-byte, or 1-byte multiple
|
||||||
|
// dest_w (a4) - 4-byte multiple
|
||||||
|
|
||||||
|
|
||||||
|
ee.zero.q q1 // clear q1
|
||||||
|
ee.orq q1, q1, q0 // copy q0 to q1
|
||||||
|
sub a6, a6, a11 // dest_stride = dest_stride - dest_w_bytes
|
||||||
|
movi.n a7, 0xf // 0xf alignment mask
|
||||||
|
|
||||||
|
.outer_loop_aligned_by_1byte:
|
||||||
|
|
||||||
|
// alignment check
|
||||||
|
and a15, a7, a3 // 0xf (alignment mask) AND dest_buff pointer
|
||||||
|
mov a12, a11 // a12 - local_dest_w_bytes = dest_w_bytes
|
||||||
|
beqz a15, _dest_buff_aligned_by_1byte // branch if a15 equals to zero
|
||||||
|
|
||||||
|
|
||||||
|
movi.n a14, 16 // a14 - 16
|
||||||
|
sub a15, a14, a15 // a15 = 16 - unalignment (lower 4 bits of dest_buff address)
|
||||||
|
sub a12, a12, a15 // local_dest_w_bytes = len - (16 - unalignment)
|
||||||
|
|
||||||
|
// keep setting until dest_buff is aligned
|
||||||
|
// Check modulo 8 of the unalignment, if - then set 8 bytes
|
||||||
|
bbci a15, 3, _aligning_mod_8_check_1byte// branch if 3-rd bit of unalignment a15 is clear
|
||||||
|
s32i.n a10, a3, 0 // save 32 bits from a10 to dest_buff a3, offset 0 bytes
|
||||||
|
s32i.n a10, a3, 4 // save 32 bits from a10 to dest_buff a3, offset 4 bytes
|
||||||
|
addi.n a3, a3, 8 // increment dest_buff pointer by 8 bytes
|
||||||
|
_aligning_mod_8_check_1byte:
|
||||||
|
|
||||||
|
// Check modulo 4 of the unalignment, if - then set 4 bytes
|
||||||
|
bbci a15, 2, _aligning_mod_4_check_1byte // branch if 2-nd bit unalignment a15 is clear
|
||||||
|
s32i.n a10, a3, 0 // save 32 bits from a10 to dest_buff a3, offset 0 bytes
|
||||||
|
addi.n a3, a3, 4 // increment dest_buff pointer by 4 bytes
|
||||||
|
_aligning_mod_4_check_1byte:
|
||||||
|
|
||||||
|
// Check modulo 2 and 1 (the following 2 ifs do the same correction)
|
||||||
|
// modulo 2 and modulo 1 requires the same action, just once
|
||||||
|
bbci a15, 1, _aligning_mod_2_check_1byte
|
||||||
|
s32i.n a10, a3, 0 // save 32 bits from a10 to dest_buff a3, offset 0 bytes
|
||||||
|
addi.n a3, a3, 4 // increment dest_buff pointer by 4 bytes
|
||||||
|
j _dest_buff_aligned_by_1byte
|
||||||
|
_aligning_mod_2_check_1byte:
|
||||||
|
|
||||||
|
bbci a15, 0, _dest_buff_aligned_by_1byte
|
||||||
|
s32i.n a10, a3, 0 // save 32 bits from a10 to dest_buff a3, offset 0 bytes
|
||||||
|
addi.n a3, a3, 4 // increment dest_buff pointer by 4 bytes
|
||||||
|
_dest_buff_aligned_by_1byte:
|
||||||
|
|
||||||
|
// Shift q reg, allowing to set 16-byte unaligned adata
|
||||||
|
wur.sar_byte a15 // apply unalignment to the SAR_BYTE
|
||||||
|
ee.src.q q2, q0, q1 // shift concat. of q0 and q1 to q2 by SAR_BYTE amount
|
||||||
|
|
||||||
|
// Calculate main loop_len
|
||||||
|
srli a9, a12, 4 // a9 - loop_len = local_dest_w_bytes / 16
|
||||||
|
|
||||||
|
// Main loop
|
||||||
|
loopnez a9, ._main_loop_unaligned_by_1byte // 16 bytes (4 argb8888) in one loop
|
||||||
|
ee.vst.128.ip q2, a3, 16 // store 16 bytes from q0 to dest_buff a3
|
||||||
|
._main_loop_unaligned_by_1byte:
|
||||||
|
|
||||||
|
// Firstly check mod 1 and mod 2 - correcting the aligned memory access
|
||||||
|
// Go back in one Byte, allow to correct after ee.vst.128.ip aligned access
|
||||||
|
addi a3, a3, -4
|
||||||
|
|
||||||
|
// Check modulo 2 of the dest_w, if - then set 2 bytes
|
||||||
|
// set SSSS in 0xSSSS0000
|
||||||
|
bbci a12, 1, _aligned_mod_2_check_1byte // branch if 1-st bit of dest_w a12 is clear
|
||||||
|
srli a14, a10, 16 // shift a10 in 16, allowing s16i (saving of lower 16 bits)
|
||||||
|
s16i a14, a3, 2 // save 16 bits from a10 to dest_buff a3, offset 2 bytes
|
||||||
|
|
||||||
|
// Check modulo 1 of the dest_w, if - then set 1 byte
|
||||||
|
// additionally set SS in 0x0000SS00
|
||||||
|
bbci a12, 0, _aligned_end // branch if 0-th bit of dest_w a12 is clear
|
||||||
|
srli a14, a10, 8 // shift a10 in 8, allowing s8i
|
||||||
|
s8i a14, a3, 1 // save 8 bits from a10 to dest_buff a3, offset 1 byte
|
||||||
|
j _aligned_end
|
||||||
|
_aligned_mod_2_check_1byte:
|
||||||
|
|
||||||
|
// Check modulo 1 of the dest_w, if - then set 1 byte
|
||||||
|
// set SS in 0xSS000000
|
||||||
|
bbci a12, 0, _aligned_end // branch if 0-th bit of dest_w a12 is clear
|
||||||
|
srli a14, a10, 24 // shift a10 in 24, allowing s8i (saving of lower 8 bits)
|
||||||
|
s8i a14, a3, 3 // save 8 bits from a10 to dest_buff a3, offset 3 bytes
|
||||||
|
_aligned_end:
|
||||||
|
|
||||||
|
addi a3, a3, 4 // Increase the pointer back, correction for addi a3, a3, -4
|
||||||
|
|
||||||
|
// Check modulo 8 of the dest_w, if - then set 8 bytes
|
||||||
|
bbci a12, 3, _aligned_mod_8_check_1byte // branch if 3-rd bit of local_dest_w_bytes a12 is clear
|
||||||
|
s32i.n a10, a3, 0 // save 32 bits from a10 to dest_buff a3, offset 0 bytes
|
||||||
|
s32i.n a10, a3, 4 // save 32 bits from a10 to dest_buff a3, offset 0 bytes
|
||||||
|
addi.n a3, a3, 8 // increment dest_buff pointer by 4 bytes
|
||||||
|
//ee.vst.l.64.ip q2, a3, 8 // save lower 64 bits from q0 to dest_buff a3, increase dest_buff pointer by 8 bytes
|
||||||
|
_aligned_mod_8_check_1byte:
|
||||||
|
|
||||||
|
// Check modulo 4 of the dest_w, if - then set 4 bytes
|
||||||
|
bbci a12, 2, _aligned_mod_4_check_1byte // branch if 2-nd bit of local_dest_w_bytes a12 is clear
|
||||||
|
s32i.n a10, a3, 0 // save 32 bits from a10 to dest_buff a3, offset 0 bytes
|
||||||
|
addi.n a3, a3, 4 // increment dest_buff pointer by 4 bytes
|
||||||
|
_aligned_mod_4_check_1byte:
|
||||||
|
|
||||||
|
add a3, a3, a6 // dest_buff + dest_stride
|
||||||
|
addi.n a5, a5, -1 // decrease the outer loop
|
||||||
|
bnez a5, .outer_loop_aligned_by_1byte
|
||||||
|
|
||||||
|
movi.n a2, 1 // return LV_RESULT_OK = 1
|
||||||
|
retw.n // return
|
||||||
|
|
||||||
|
.lv_color_blend_to_argb8888_esp32_body:
|
||||||
|
|
||||||
|
srli a9, a4, 2 // a9 - loop_len = dest_w / 4
|
||||||
|
sub a6, a6, a11 // dest_stride = dest_stride - dest_w_bytes
|
||||||
|
|
||||||
|
.outer_loop:
|
||||||
|
|
||||||
|
// Run main loop which sets 16 bytes in one loop run
|
||||||
|
loopnez a9, ._main_loop
|
||||||
|
s32i.n a10, a3, 0 // save 32 bits from a10 to dest_buff a3
|
||||||
|
s32i.n a10, a3, 4 // save 32 bits from a10 to dest_buff a3
|
||||||
|
s32i.n a10, a3, 8 // save 32 bits from a10 to dest_buff a3
|
||||||
|
s32i.n a10, a3, 12 // save 32 bits from a10 to dest_buff a3
|
||||||
|
addi.n a3, a3, 16 // increment dest_buff pointer by 16 bytes
|
||||||
|
._main_loop:
|
||||||
|
|
||||||
|
// Finish the remaining bytes out of the loop
|
||||||
|
// Check modulo 8 of the dest_w_bytes, if - then set 8 bytes
|
||||||
|
bbci a11, 3, _mod_8_check // branch if 2-nd bit of dest_w_bytes is clear
|
||||||
|
s32i.n a10, a3, 0 // save 32 bits from a10 to dest_buff a3, offset 0 bytes
|
||||||
|
s32i.n a10, a3, 4 // save 32 bits from a10 to dest_buff a3, offset 0 bytes
|
||||||
|
addi.n a3, a3, 8 // increment dest_buff pointer by 8 bytes
|
||||||
|
_mod_8_check:
|
||||||
|
|
||||||
|
// Check modulo 4 of the dest_w_bytes, if - then set 4 bytes
|
||||||
|
bbci a11, 2, _mod_4_check // branch if 2-nd bit of dest_w_bytes is clear
|
||||||
|
s32i.n a10, a3, 0 // save 32 bits from a10 to dest_buff a3, offset 0 bytes
|
||||||
|
addi.n a3, a3, 4 // increment dest_buff pointer by 4 bytes
|
||||||
|
_mod_4_check:
|
||||||
|
|
||||||
|
add a3, a3, a6 // dest_buff + dest_stride
|
||||||
|
addi.n a5, a5, -1 // decrease the outer loop
|
||||||
|
bnez a5, .outer_loop
|
||||||
|
|
||||||
|
movi.n a2, 1 // return LV_RESULT_OK = 1
|
||||||
|
retw.n // return
|
||||||
@ -0,0 +1,149 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This is LVGL RGB565 simple fill for ESP32 processor
|
||||||
|
|
||||||
|
.section .text
|
||||||
|
.align 4
|
||||||
|
.global lv_color_blend_to_rgb565_esp
|
||||||
|
.type lv_color_blend_to_rgb565_esp,@function
|
||||||
|
// The function implements the following C code:
|
||||||
|
// void lv_color_blend_to_rgb565(_lv_draw_sw_blend_fill_dsc_t * dsc);
|
||||||
|
|
||||||
|
// Input params
|
||||||
|
//
|
||||||
|
// dsc - a2
|
||||||
|
|
||||||
|
// typedef struct {
|
||||||
|
// uint32_t opa; l32i 0
|
||||||
|
// void * dst_buf; l32i 4
|
||||||
|
// uint32_t dst_w; l32i 8
|
||||||
|
// uint32_t dst_h; l32i 12
|
||||||
|
// uint32_t dst_stride; l32i 16
|
||||||
|
// const void * src_buf; l32i 20
|
||||||
|
// uint32_t src_stride; l32i 24
|
||||||
|
// const lv_opa_t * mask_buf; l32i 28
|
||||||
|
// uint32_t mask_stride; l32i 32
|
||||||
|
// } asm_dsc_t;
|
||||||
|
|
||||||
|
lv_color_blend_to_rgb565_esp:
|
||||||
|
|
||||||
|
entry a1, 32
|
||||||
|
|
||||||
|
l32i.n a3, a2, 4 // a3 - dest_buff
|
||||||
|
l32i.n a4, a2, 8 // a4 - dest_w in uint16_t
|
||||||
|
l32i.n a5, a2, 12 // a5 - dest_h in uint16_t
|
||||||
|
l32i.n a6, a2, 16 // a6 - dest_stride in bytes
|
||||||
|
l32i.n a7, a2, 20 // a7 - src_buff (color)
|
||||||
|
l32i.n a8, a7, 0 // a8 - color as value
|
||||||
|
slli a11, a4, 1 // a11 - dest_w_bytes = sizeof(uint16_t) * dest_w
|
||||||
|
|
||||||
|
// Convert color to rgb656
|
||||||
|
l8ui a15, a7, 2 // red
|
||||||
|
movi.n a14, 0xf8
|
||||||
|
and a13, a15, a14
|
||||||
|
slli a10, a13, 8
|
||||||
|
|
||||||
|
l8ui a15, a7, 0 // blue
|
||||||
|
and a13, a15, a14
|
||||||
|
srli a12, a13, 3
|
||||||
|
add a10, a10, a12
|
||||||
|
|
||||||
|
l8ui a15, a7, 1 // green
|
||||||
|
movi.n a14, 0xfc
|
||||||
|
and a13, a15, a14
|
||||||
|
slli a12, a13, 3
|
||||||
|
add a12, a10, a12 // a12 = 16-bit color
|
||||||
|
|
||||||
|
slli a10, a12, 16
|
||||||
|
movi.n a13, 0xFFFF0000
|
||||||
|
and a10, a10, a13
|
||||||
|
or a10, a10, a12 // a10 = 32-bit color (16bit + (16bit << 16))
|
||||||
|
|
||||||
|
movi.n a8, 0x3 // a8 = 0x3, dest_buff align mask
|
||||||
|
sub a6, a6, a11 // dest_stride = dest_stride - dest_w_bytes
|
||||||
|
|
||||||
|
// cache init
|
||||||
|
// Prepare main loop length and dest_w_bytes
|
||||||
|
srli a9, a4, 4 // a9 = loop_len = dest_w / 8, calculate main loop_len for original dest_w
|
||||||
|
slli a11, a4, 1 // a11 = dest_w_bytes = sizeof(uint16_t) * dest_w
|
||||||
|
addi a4, a4, -1 // a4-- (decrement a4)
|
||||||
|
s32i.n a9, a1, 0 // cache.orig.loop_len
|
||||||
|
s32i.n a11, a1, 4 // cache.orig.dest_w_bytes
|
||||||
|
|
||||||
|
// Prepare decreased main loop length and dest_w_bytes
|
||||||
|
srli a9, a4, 4 // a9 = loop_len = dest_w / 8, calculate main loop_len for dest_w - 1
|
||||||
|
slli a11, a4, 1 // a11 = dest_w_bytes = sizeof(uint16_t) * (dest_w - 1)
|
||||||
|
s32i.n a9, a1, 8 // cache.decr.loop_len
|
||||||
|
s32i.n a11, a1, 12 // cache.decr.dest_w_bytes
|
||||||
|
and a7, a8, a3 // a7 = dest_buff AND 0x3 (chck if the address is 4-byte aligned)
|
||||||
|
|
||||||
|
.outer_loop:
|
||||||
|
|
||||||
|
// Check if the des_buff is 2-byte aligned
|
||||||
|
beqz a7, _dest_buff_2_byte_aligned // branch if a7 is equal to zero
|
||||||
|
s16i a12, a3, 0 // save 16 bits from 16-bit color a12 to dest_buff a3, offset 0
|
||||||
|
l32i.n a9, a1, 8 // a9 = load cache.decr.loop_len
|
||||||
|
l32i.n a11, a1, 12 // a11 = load cache.decr.dest_w_bytes
|
||||||
|
addi.n a3, a3, 2 // increment dest_buff pointer by 2
|
||||||
|
j _dest_buff_unaligned
|
||||||
|
_dest_buff_2_byte_aligned:
|
||||||
|
|
||||||
|
l32i.n a9, a1, 0 // a11 = load cache.orig.loop_len
|
||||||
|
l32i.n a11, a1, 4 // a11 = load cache.orig.dest_w_bytes
|
||||||
|
|
||||||
|
_dest_buff_unaligned:
|
||||||
|
|
||||||
|
// Run main loop which sets 16 bytes in one loop run
|
||||||
|
loopnez a9, ._main_loop
|
||||||
|
s32i.n a10, a3, 0 // save 32 bits from 32-bit color a10 to dest_buff a3, offset 0
|
||||||
|
s32i.n a10, a3, 4 // save 32 bits from 32-bit color a10 to dest_buff a3, offset 4
|
||||||
|
s32i.n a10, a3, 8 // save 32 bits from 32-bit color a10 to dest_buff a3, offset 8
|
||||||
|
s32i.n a10, a3, 12 // save 32 bits from 32-bit color a10 to dest_buff a3, offset 12
|
||||||
|
s32i.n a10, a3, 16 // save 32 bits from 32-bit color a10 to dest_buff a3, offset 16
|
||||||
|
s32i.n a10, a3, 20 // save 32 bits from 32-bit color a10 to dest_buff a3, offset 20
|
||||||
|
s32i.n a10, a3, 24 // save 32 bits from 32-bit color a10 to dest_buff a3, offset 24
|
||||||
|
s32i.n a10, a3, 28 // save 32 bits from 32-bit color a10 to dest_buff a3, offset 28
|
||||||
|
addi.n a3, a3, 32 // increment dest_buff pointer by 32
|
||||||
|
._main_loop:
|
||||||
|
|
||||||
|
// Finish the remaining bytes out of the loop
|
||||||
|
// Check modulo 8 of the dest_w_bytes, if - then set 16 bytes
|
||||||
|
bbci a11, 4, _mod_16_check // branch if 2-nd bit of dest_w_bytes is clear
|
||||||
|
s32i.n a10, a3, 0 // save 32 bits from 32-bit color a10 to dest_buff a3, offset 0
|
||||||
|
s32i.n a10, a3, 4 // save 32 bits from 32-bit color a10 to dest_buff a3, offset 4
|
||||||
|
s32i.n a10, a3, 8 // save 32 bits from 32-bit color a10 to dest_buff a3, offset 8
|
||||||
|
s32i.n a10, a3, 12 // save 32 bits from 32-bit color a10 to dest_buff a3, offset 12
|
||||||
|
addi.n a3, a3, 16 // increment dest_buff pointer by 16
|
||||||
|
_mod_16_check:
|
||||||
|
|
||||||
|
// Finish the remaining bytes out of the loop
|
||||||
|
// Check modulo 8 of the dest_w_bytes, if - then set 8 bytes
|
||||||
|
bbci a11, 3, _mod_8_check // branch if 2-nd bit of dest_w_bytes is clear
|
||||||
|
s32i.n a10, a3, 0 // save 32 bits from 32-bit color a10 to dest_buff a3, offset 0
|
||||||
|
s32i.n a10, a3, 4 // save 32 bits from 32-bit color a10 to dest_buff a3, offset 4
|
||||||
|
addi.n a3, a3, 8 // increment dest_buff pointer by 8 bytes
|
||||||
|
_mod_8_check:
|
||||||
|
|
||||||
|
// Check modulo 4 of the dest_w_bytes, if - then set 4 bytes
|
||||||
|
bbci a11, 2, _mod_4_check // branch if 2-nd bit of dest_w_bytes is clear
|
||||||
|
s32i.n a10, a3, 0 // save 32 bits from 32-bit color a10 to dest_buff a3, offset 0
|
||||||
|
addi.n a3, a3, 4 // increment dest_buff pointer by 4
|
||||||
|
_mod_4_check:
|
||||||
|
|
||||||
|
// Check modulo 2 of the dest_w_bytes, if - then set 2 bytes
|
||||||
|
bbci a11, 1, _mod_2_check // branch if 1-st bit of dest_w_bytes is clear
|
||||||
|
s16i a12, a3, 0 // save 16 bits from 16-bit color a12 to dest_buff a3, offset 0
|
||||||
|
addi.n a3, a3, 2 // increment dest_buff pointer by 2 bytes
|
||||||
|
_mod_2_check:
|
||||||
|
|
||||||
|
add a3, a3, a6 // dest_buff + dest_stride
|
||||||
|
addi.n a5, a5, -1 // decrease the outer loop
|
||||||
|
and a7, a8, a3 // a7 = dest_buff AND 0x3 (chck if the address is 4-byte aligned)
|
||||||
|
bnez a5, .outer_loop
|
||||||
|
|
||||||
|
movi.n a2, 1 // return LV_RESULT_OK = 1
|
||||||
|
retw.n // return
|
||||||
@ -0,0 +1,405 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
// This is LVGL RGB565 simple fill for ESP32S3 processor
|
||||||
|
|
||||||
|
.section .text
|
||||||
|
.align 4
|
||||||
|
.global lv_color_blend_to_rgb565_esp
|
||||||
|
.type lv_color_blend_to_rgb565_esp,@function
|
||||||
|
// The function implements the following C code:
|
||||||
|
// void lv_color_blend_to_rgb565(_lv_draw_sw_blend_fill_dsc_t * dsc);
|
||||||
|
|
||||||
|
// Input params
|
||||||
|
//
|
||||||
|
// dsc - a2
|
||||||
|
|
||||||
|
// typedef struct {
|
||||||
|
// uint32_t opa; l32i 0
|
||||||
|
// void * dst_buf; l32i 4
|
||||||
|
// uint32_t dst_w; l32i 8
|
||||||
|
// uint32_t dst_h; l32i 12
|
||||||
|
// uint32_t dst_stride; l32i 16
|
||||||
|
// const void * src_buf; l32i 20
|
||||||
|
// uint32_t src_stride; l32i 24
|
||||||
|
// const lv_opa_t * mask_buf; l32i 28
|
||||||
|
// uint32_t mask_stride; l32i 32
|
||||||
|
// } asm_dsc_t;
|
||||||
|
|
||||||
|
lv_color_blend_to_rgb565_esp:
|
||||||
|
|
||||||
|
entry a1, 32
|
||||||
|
ee.zero.q q0 // dummy TIE instruction, to enable the TIE
|
||||||
|
|
||||||
|
l32i.n a3, a2, 4 // a3 - dest_buff
|
||||||
|
l32i.n a4, a2, 8 // a4 - dest_w in uint16_t
|
||||||
|
l32i.n a5, a2, 12 // a5 - dest_h in uint16_t
|
||||||
|
l32i.n a6, a2, 16 // a6 - dest_stride in bytes
|
||||||
|
l32i.n a7, a2, 20 // a7 - src_buff (color)
|
||||||
|
l32i.n a8, a7, 0 // a8 - color as value
|
||||||
|
slli a11, a4, 1 // a11 - dest_w_bytes = sizeof(uint16_t) * dest_w
|
||||||
|
|
||||||
|
// Convert color to rgb656
|
||||||
|
l8ui a15, a7, 2 // red
|
||||||
|
movi.n a14, 0xf8
|
||||||
|
and a13, a15, a14
|
||||||
|
slli a10, a13, 8
|
||||||
|
|
||||||
|
l8ui a15, a7, 0 // blue
|
||||||
|
and a13, a15, a14
|
||||||
|
srli a12, a13, 3
|
||||||
|
add a10, a10, a12
|
||||||
|
|
||||||
|
l8ui a15, a7, 1 // green
|
||||||
|
movi.n a14, 0xfc
|
||||||
|
and a13, a15, a14
|
||||||
|
slli a12, a13, 3
|
||||||
|
add a12, a10, a12 // a12 = 16-bit color
|
||||||
|
|
||||||
|
slli a10, a12, 16
|
||||||
|
movi.n a13, 0xFFFF0000
|
||||||
|
and a10, a10, a13
|
||||||
|
or a10, a10, a12 // a10 = 32-bit color (16bit + (16bit << 16))
|
||||||
|
|
||||||
|
// Check for short lengths
|
||||||
|
// dest_w should be at least 16, othewise it's not worth using esp32s3 TIE
|
||||||
|
bgei a4, 16, _esp32s3_implementation // Branch if dest_w is greater than or equal to 16
|
||||||
|
j .lv_color_blend_to_rgb565_esp32_body // Jump to esp32 implementation
|
||||||
|
|
||||||
|
_esp32s3_implementation:
|
||||||
|
|
||||||
|
ee.movi.32.q q0, a10, 0 // fill q0 register from a10 by 32 bits
|
||||||
|
ee.movi.32.q q0, a10, 1
|
||||||
|
ee.movi.32.q q0, a10, 2
|
||||||
|
ee.movi.32.q q0, a10, 3
|
||||||
|
|
||||||
|
// Check dest_buff alignment
|
||||||
|
movi.n a7, 0xf // 0xf alignment mask (16-byte alignment)
|
||||||
|
and a15, a7, a3 // 16-byte alignment mask AND dest_buff pointer
|
||||||
|
bnez a15, _unaligned_by_4byte // branch if a15 not equals to zero
|
||||||
|
|
||||||
|
// Check dest_stride alignment
|
||||||
|
and a15, a7, a6 // 16-byte alignment mask AND dest_stride
|
||||||
|
bnez a15, _unaligned_by_4byte // branch if a15 not equals to zero
|
||||||
|
|
||||||
|
// Check dest_w_bytes alignment
|
||||||
|
and a15, a7, a11 // 16-byte alignment mask AND dest_w_bytes
|
||||||
|
bnez a15, _unaligned_by_4byte // branch if a15 not equals to zero
|
||||||
|
|
||||||
|
//**********************************************************************************************************************
|
||||||
|
|
||||||
|
// all aligned, the most ideal case
|
||||||
|
|
||||||
|
// dest_buff (a3) - 16-byte aligned
|
||||||
|
// dest_stride (a6) - 16-byte multiple
|
||||||
|
// dest_w (a4) - 16-byte multiple
|
||||||
|
|
||||||
|
srli a9, a4, 3 // a9 - loop_len = dest_w / 8
|
||||||
|
sub a6, a6, a11 // dest_stride = dest_stride - dest_w_bytes
|
||||||
|
|
||||||
|
.outer_loop_aligned:
|
||||||
|
|
||||||
|
loopnez a9, ._main_loop_aligned // 16 bytes (8 rgb565) in one loop
|
||||||
|
ee.vst.128.ip q0, a3, 16 // store 16 bytes from q0 to dest_buff a3
|
||||||
|
._main_loop_aligned:
|
||||||
|
|
||||||
|
add a3, a3, a6 // dest_buff + dest_stride
|
||||||
|
addi.n a5, a5, -1 // decrease the outer loop
|
||||||
|
bnez a5, .outer_loop_aligned
|
||||||
|
|
||||||
|
movi.n a2, 1 // return LV_RESULT_OK = 1
|
||||||
|
retw.n // return
|
||||||
|
|
||||||
|
_unaligned_by_4byte:
|
||||||
|
|
||||||
|
// Check dest_buff alignment
|
||||||
|
movi.n a7, 0x3 // 0x3 alignment mask (4-byte alignment)
|
||||||
|
and a15, a7, a3 // 4-byte alignment mask AND dest_buff pointer
|
||||||
|
bnez a15, _unaligned_by_1byte // branch if a15 not equals to zero
|
||||||
|
|
||||||
|
// Check dest_stride alignment
|
||||||
|
and a15, a7, a6 // 4-byte alignment mask AND dest_stride pointer
|
||||||
|
bnez a15, _unaligned_by_1byte // branch if a15 not equals to zero
|
||||||
|
|
||||||
|
//**********************************************************************************************************************
|
||||||
|
|
||||||
|
// either dest_buff or dest_stride is not 16-byte aligned
|
||||||
|
// dest_w is always 4-byte multiple
|
||||||
|
// all of the following are 4-byte aligned
|
||||||
|
|
||||||
|
// dest_buff (a3) - 16-byte, or 4-byte aligned
|
||||||
|
// dest_stride (a6) - 16-byte, or 4-byte multiple
|
||||||
|
// dest_w (a4) - 4-byte multiple
|
||||||
|
|
||||||
|
sub a6, a6, a11 // dest_stride = dest_stride - dest_w_bytes
|
||||||
|
movi.n a7, 0xf // 0xf alignment mask
|
||||||
|
|
||||||
|
.outer_loop_aligned_by_4byte:
|
||||||
|
|
||||||
|
// alignment check
|
||||||
|
and a15, a7, a3 // 0xf (alignment mask) AND dest_buff pointer
|
||||||
|
mov a12, a11 // a12 - local_dest_w_bytes = dest_w_bytes
|
||||||
|
beqz a15, _dest_buff_aligned_by_4byte // branch if a15 equals to zero
|
||||||
|
|
||||||
|
|
||||||
|
movi.n a14, 16 // a14 - 16
|
||||||
|
sub a15, a14, a15 // a15 = 16 - unalignment (lower 4 bits of dest_buff address)
|
||||||
|
sub a12, a12, a15 // local_dest_w_bytes = len - (16 - unalignment)
|
||||||
|
|
||||||
|
// keep setting until dest_buff is aligned
|
||||||
|
// Check modulo 8 of the unalignment, if - then set 8 bytes
|
||||||
|
bbci a15, 3, _aligning_mod_8_check_4byte // branch if 3-rd bit of unalignment a15 is clear
|
||||||
|
s32i.n a10, a3, 0 // save 32 bits from a10 to dest_buff a3, offset 0 bytes
|
||||||
|
s32i.n a10, a3, 4 // save 32 bits from a10 to dest_buff a3, offset 4 bytes
|
||||||
|
addi.n a3, a3, 8 // increment dest_buff pointer by 8 bytes
|
||||||
|
_aligning_mod_8_check_4byte:
|
||||||
|
|
||||||
|
// Check modulo 4 of the unalignment, if - then set 4 bytes
|
||||||
|
bbci a15, 2, _aligning_mod_4_check_4byte // branch if 2-nd bit unalignment a15 is clear
|
||||||
|
s32i.n a10, a3, 0 // save 32 bits from a10 to dest_buff a3, offset 0 bytes
|
||||||
|
addi.n a3, a3, 4 // increment dest_buff pointer by 4 bytes
|
||||||
|
_aligning_mod_4_check_4byte:
|
||||||
|
|
||||||
|
_dest_buff_aligned_by_4byte:
|
||||||
|
// Calculate main loop_len
|
||||||
|
srli a9, a12, 4 // a9 - loop_len = local_dest_w_bytes / 16
|
||||||
|
|
||||||
|
// Main loop
|
||||||
|
loopnez a9, ._main_loop_unaligned_by_4byte // 16 bytes (8 rgb565) in one loop
|
||||||
|
ee.vst.128.ip q0, a3, 16 // store 16 bytes from q0 to dest_buff a3
|
||||||
|
._main_loop_unaligned_by_4byte:
|
||||||
|
|
||||||
|
// Check modulo 8 of the dest_w, if - then set 8 bytes
|
||||||
|
bbci a12, 3, _aligned_mod_8_check_4byte // branch if 3-rd bit of local_dest_w_bytes a12 is clear
|
||||||
|
ee.vst.l.64.ip q0, a3, 8 // save lower 64 bits from q0 to dest_buff a3, increase dest_buff pointer by 8 bytes
|
||||||
|
_aligned_mod_8_check_4byte:
|
||||||
|
|
||||||
|
// Check modulo 4 of the dest_w, if - then set 4 bytes
|
||||||
|
bbci a12, 2, _aligned_mod_4_check_4byte // branch if 2-nd bit of local_dest_w_bytes a12 is clear
|
||||||
|
s32i.n a10, a3, 0 // save 32 bits from a10 to dest_buff a3, offset 0 bytes
|
||||||
|
addi.n a3, a3, 4 // increment dest_buff pointer by 4 bytes
|
||||||
|
_aligned_mod_4_check_4byte:
|
||||||
|
|
||||||
|
// Check modulo 2 of the dest_w, if - then set 2 bytes
|
||||||
|
bbci a12, 1, _aligned_mod_2_check_4byte // branch if 1-st bit of local_dest_w_bytes a12 is clear
|
||||||
|
s16i a10, a3, 0 // save 16 bits from a10 to dest_buff a3, offset 0 bytes
|
||||||
|
addi.n a3, a3, 2 // increment dest_buff pointer by 2 bytes
|
||||||
|
_aligned_mod_2_check_4byte:
|
||||||
|
|
||||||
|
|
||||||
|
add a3, a3, a6 // dest_buff + dest_stride
|
||||||
|
addi.n a5, a5, -1 // decrease the outer loop
|
||||||
|
bnez a5, .outer_loop_aligned_by_4byte
|
||||||
|
|
||||||
|
movi.n a2, 1 // return LV_RESULT_OK = 1
|
||||||
|
retw.n // return
|
||||||
|
|
||||||
|
_unaligned_by_1byte:
|
||||||
|
|
||||||
|
//**********************************************************************************************************************
|
||||||
|
|
||||||
|
// either dest_buff or dest_stride is not 4-byte aligned
|
||||||
|
// dest_w is always 4-byte multiple
|
||||||
|
|
||||||
|
// dest_buff (a3) - 4-byte, or 1-byte aligned
|
||||||
|
// dest_stride (a6) - 4-byte, or 1-byte multiple
|
||||||
|
// dest_w (a4) - 4-byte multiple
|
||||||
|
|
||||||
|
|
||||||
|
ee.zero.q q1 // clear q1
|
||||||
|
ee.orq q1, q1, q0 // copy q0 to q1
|
||||||
|
sub a6, a6, a11 // dest_stride = dest_stride - dest_w_bytes
|
||||||
|
movi.n a7, 0xf // 0xf alignment mask
|
||||||
|
|
||||||
|
.outer_loop_aligned_by_1byte:
|
||||||
|
|
||||||
|
// alignment check
|
||||||
|
and a15, a7, a3 // 0xf (alignment mask) AND dest_buff pointer
|
||||||
|
mov a12, a11 // a12 - local_dest_w_bytes = dest_w_bytes
|
||||||
|
beqz a15, _dest_buff_aligned_by_1byte // branch if a15 equals to zero
|
||||||
|
|
||||||
|
|
||||||
|
movi.n a14, 16 // a14 - 16
|
||||||
|
sub a15, a14, a15 // a15 = 16 - unalignment (lower 4 bits of dest_buff address)
|
||||||
|
sub a12, a12, a15 // local_dest_w_bytes = len - (16 - unalignment)
|
||||||
|
|
||||||
|
// keep setting until dest_buff is aligned
|
||||||
|
// Check modulo 8 of the unalignment, if - then set 8 bytes
|
||||||
|
bbci a15, 3, _aligning_mod_8_check_1byte// branch if 3-rd bit of unalignment a15 is clear
|
||||||
|
s32i.n a10, a3, 0 // save 32 bits from a10 to dest_buff a3, offset 0 bytes
|
||||||
|
s32i.n a10, a3, 4 // save 32 bits from a10 to dest_buff a3, offset 4 bytes
|
||||||
|
addi.n a3, a3, 8 // increment dest_buff pointer by 8 bytes
|
||||||
|
_aligning_mod_8_check_1byte:
|
||||||
|
|
||||||
|
// Check modulo 4 of the unalignment, if - then set 4 bytes
|
||||||
|
bbci a15, 2, _aligning_mod_4_check_1byte // branch if 2-nd bit unalignment a15 is clear
|
||||||
|
s32i.n a10, a3, 0 // save 32 bits from a10 to dest_buff a3, offset 0 bytes
|
||||||
|
addi.n a3, a3, 4 // increment dest_buff pointer by 4 bytes
|
||||||
|
_aligning_mod_4_check_1byte:
|
||||||
|
|
||||||
|
// Check modulo 2 and 1
|
||||||
|
// modulo 2 and modulo 1 requires the same action
|
||||||
|
bbci a15, 1, _aligning_mod_2_check_1byte // branch if 1-st bit unalignment a15 is clear
|
||||||
|
s16i a10, a3, 0 // save 16 bits from a10 to dest_buff a3, offset 0 bytes
|
||||||
|
addi.n a3, a3, 2 // increment dest_buff pointer by 2 bytes
|
||||||
|
_aligning_mod_2_check_1byte:
|
||||||
|
|
||||||
|
bbci a15, 0, _dest_buff_aligned_by_1byte // branch if 0-st bit unalignment a15 is clear
|
||||||
|
s16i a10, a3, 0 // save 16 bits from a10 to dest_buff a3, offset 0 bytes
|
||||||
|
addi.n a3, a3, 2 // increment dest_buff pointer by 2 bytes
|
||||||
|
_dest_buff_aligned_by_1byte:
|
||||||
|
|
||||||
|
// Shift q reg, allowing to set 16-byte unaligned adata
|
||||||
|
wur.sar_byte a15 // apply unalignment to the SAR_BYTE
|
||||||
|
ee.src.q q2, q0, q1 // shift concat. of q0 and q1 to q2 by SAR_BYTE amount
|
||||||
|
|
||||||
|
// Calculate main loop_len
|
||||||
|
srli a9, a12, 4 // a9 - loop_len = local_dest_w_bytes / 16
|
||||||
|
|
||||||
|
// Main loop
|
||||||
|
loopnez a9, ._main_loop_unaligned_by_1byte // 16 bytes (8 rgb565) in one loop
|
||||||
|
ee.vst.128.ip q2, a3, 16 // store 16 bytes from q0 to dest_buff a3
|
||||||
|
._main_loop_unaligned_by_1byte:
|
||||||
|
|
||||||
|
// Firstly check mod 1 and mod 2 - correcting the aligned memory access
|
||||||
|
// Go back in one Byte, allow to correct after ee.vst.128.ip aligned access
|
||||||
|
addi a3, a3, -4
|
||||||
|
|
||||||
|
// Check modulo 2 of the dest_w, if - then set 2 bytes
|
||||||
|
// set SSSS in 0xSSSS0000
|
||||||
|
bbci a12, 1, _aligned_mod_2_check_1byte_corr // branch if 1-st bit of dest_w a12 is clear
|
||||||
|
srli a14, a10, 16 // shift a10 in 16, allowing s16i (saving of lower 16 bits)
|
||||||
|
s16i a14, a3, 2 // save 16 bits from a10 to dest_buff a3, offset 2 bytes
|
||||||
|
|
||||||
|
// Check modulo 1 of the dest_w, if - then set 1 byte
|
||||||
|
// additionally set SS in 0x0000SS00
|
||||||
|
bbci a12, 0, _aligned_end // branch if 0-th bit of dest_w a12 is clear
|
||||||
|
srli a14, a10, 8 // shift a10 in 8, allowing s8i
|
||||||
|
s8i a14, a3, 1 // save 8 bits from a10 to dest_buff a3, offset 1 byte
|
||||||
|
j _aligned_end
|
||||||
|
_aligned_mod_2_check_1byte_corr:
|
||||||
|
|
||||||
|
// Check modulo 1 of the dest_w, if - then set 1 byte
|
||||||
|
// set SS in 0xSS000000
|
||||||
|
bbci a12, 0, _aligned_end // branch if 0-th bit of dest_w a12 is clear
|
||||||
|
srli a14, a10, 24 // shift a10 in 24, allowing s8i (saving of lower 8 bits)
|
||||||
|
s8i a14, a3, 3 // save 8 bits from a10 to dest_buff a3, offset 3 bytes
|
||||||
|
_aligned_end:
|
||||||
|
|
||||||
|
addi a3, a3, 4 // Increase the pointer back, correction for addi a3, a3, -4
|
||||||
|
|
||||||
|
// Check modulo 8 of the dest_w, if - then set 8 bytes
|
||||||
|
bbci a12, 3, _aligned_mod_8_check_1byte // branch if 3-rd bit of local_dest_w_bytes a12 is clear
|
||||||
|
s32i.n a10, a3, 0 // save 32 bits from a10 to dest_buff a3, offset 0 bytes
|
||||||
|
s32i.n a10, a3, 4 // save 32 bits from a10 to dest_buff a3, offset 0 bytes
|
||||||
|
addi.n a3, a3, 8 // increment dest_buff pointer by 4 bytes
|
||||||
|
_aligned_mod_8_check_1byte:
|
||||||
|
|
||||||
|
// Check modulo 4 of the dest_w, if - then set 4 bytes
|
||||||
|
bbci a12, 2, _aligned_mod_4_check_1byte // branch if 2-nd bit of local_dest_w_bytes a12 is clear
|
||||||
|
s32i.n a10, a3, 0 // save 32 bits from a10 to dest_buff a3, offset 0 bytes
|
||||||
|
addi.n a3, a3, 4 // increment dest_buff pointer by 4 bytes
|
||||||
|
_aligned_mod_4_check_1byte:
|
||||||
|
|
||||||
|
// Check modulo 2 of the dest_w, if - then set 2 bytes
|
||||||
|
bbci a12, 1, _aligned_mod_2_check_1byte // branch if 1-st bit of local_dest_w_bytes a12 is clear
|
||||||
|
s16i a10, a3, 0 // save 16 bits from a10 to dest_buff a3, offset 0 bytes
|
||||||
|
addi.n a3, a3, 2 // increment dest_buff pointer by 2 bytes
|
||||||
|
_aligned_mod_2_check_1byte:
|
||||||
|
|
||||||
|
add a3, a3, a6 // dest_buff + dest_stride
|
||||||
|
addi.n a5, a5, -1 // decrease the outer loop
|
||||||
|
bnez a5, .outer_loop_aligned_by_1byte
|
||||||
|
|
||||||
|
movi.n a2, 1 // return LV_RESULT_OK = 1
|
||||||
|
retw.n // return
|
||||||
|
|
||||||
|
.lv_color_blend_to_rgb565_esp32_body:
|
||||||
|
|
||||||
|
movi.n a8, 0x3 // a8 = 0x3, dest_buff align mask
|
||||||
|
sub a6, a6, a11 // dest_stride = dest_stride - dest_w_bytes
|
||||||
|
|
||||||
|
// cache init
|
||||||
|
// Prepare main loop length and dest_w_bytes
|
||||||
|
srli a9, a4, 4 // a9 = loop_len = dest_w / 8, calculate main loop_len for original dest_w
|
||||||
|
slli a11, a4, 1 // a11 = dest_w_bytes = sizeof(uint16_t) * dest_w
|
||||||
|
addi a4, a4, -1 // a4-- (decrement a4)
|
||||||
|
s32i.n a9, a1, 0 // cache.orig.loop_len
|
||||||
|
s32i.n a11, a1, 4 // cache.orig.dest_w_bytes
|
||||||
|
|
||||||
|
// Prepare decreased main loop length and dest_w_bytes
|
||||||
|
srli a9, a4, 4 // a9 = loop_len = dest_w / 8, calculate main loop_len for dest_w - 1
|
||||||
|
slli a11, a4, 1 // a11 = dest_w_bytes = sizeof(uint16_t) * (dest_w - 1)
|
||||||
|
s32i.n a9, a1, 8 // cache.decr.loop_len
|
||||||
|
s32i.n a11, a1, 12 // cache.decr.dest_w_bytes
|
||||||
|
and a7, a8, a3 // a7 = dest_buff AND 0x3 (chck if the address is 4-byte aligned)
|
||||||
|
|
||||||
|
.outer_loop:
|
||||||
|
|
||||||
|
// Check if the des_buff is 2-byte aligned
|
||||||
|
beqz a7, _dest_buff_2_byte_aligned // branch if a7 is equal to zero
|
||||||
|
s16i a12, a3, 0 // save 16 bits from 16-bit color a12 to dest_buff a3, offset 0
|
||||||
|
l32i.n a9, a1, 8 // a9 = load cache.decr.loop_len
|
||||||
|
l32i.n a11, a1, 12 // a11 = load cache.decr.dest_w_bytes
|
||||||
|
addi.n a3, a3, 2 // increment dest_buff pointer by 2
|
||||||
|
j _dest_buff_unaligned
|
||||||
|
_dest_buff_2_byte_aligned:
|
||||||
|
|
||||||
|
l32i.n a9, a1, 0 // a11 = load cache.orig.loop_len
|
||||||
|
l32i.n a11, a1, 4 // a11 = load cache.orig.dest_w_bytes
|
||||||
|
|
||||||
|
_dest_buff_unaligned:
|
||||||
|
|
||||||
|
// Run main loop which sets 16 bytes in one loop run
|
||||||
|
loopnez a9, ._main_loop
|
||||||
|
s32i.n a10, a3, 0 // save 32 bits from 32-bit color a10 to dest_buff a3, offset 0
|
||||||
|
s32i.n a10, a3, 4 // save 32 bits from 32-bit color a10 to dest_buff a3, offset 4
|
||||||
|
s32i.n a10, a3, 8 // save 32 bits from 32-bit color a10 to dest_buff a3, offset 8
|
||||||
|
s32i.n a10, a3, 12 // save 32 bits from 32-bit color a10 to dest_buff a3, offset 12
|
||||||
|
s32i.n a10, a3, 16 // save 32 bits from 32-bit color a10 to dest_buff a3, offset 16
|
||||||
|
s32i.n a10, a3, 20 // save 32 bits from 32-bit color a10 to dest_buff a3, offset 20
|
||||||
|
s32i.n a10, a3, 24 // save 32 bits from 32-bit color a10 to dest_buff a3, offset 24
|
||||||
|
s32i.n a10, a3, 28 // save 32 bits from 32-bit color a10 to dest_buff a3, offset 28
|
||||||
|
addi.n a3, a3, 32 // increment dest_buff pointer by 32
|
||||||
|
._main_loop:
|
||||||
|
|
||||||
|
// Finish the remaining bytes out of the loop
|
||||||
|
// Check modulo 8 of the dest_w_bytes, if - then set 16 bytes
|
||||||
|
bbci a11, 4, _mod_16_check // branch if 2-nd bit of dest_w_bytes is clear
|
||||||
|
s32i.n a10, a3, 0 // save 32 bits from 32-bit color a10 to dest_buff a3, offset 0
|
||||||
|
s32i.n a10, a3, 4 // save 32 bits from 32-bit color a10 to dest_buff a3, offset 4
|
||||||
|
s32i.n a10, a3, 8 // save 32 bits from 32-bit color a10 to dest_buff a3, offset 8
|
||||||
|
s32i.n a10, a3, 12 // save 32 bits from 32-bit color a10 to dest_buff a3, offset 12
|
||||||
|
addi.n a3, a3, 16 // increment dest_buff pointer by 16
|
||||||
|
_mod_16_check:
|
||||||
|
|
||||||
|
// Finish the remaining bytes out of the loop
|
||||||
|
// Check modulo 8 of the dest_w_bytes, if - then set 8 bytes
|
||||||
|
bbci a11, 3, _mod_8_check // branch if 2-nd bit of dest_w_bytes is clear
|
||||||
|
s32i.n a10, a3, 0 // save 32 bits from 32-bit color a10 to dest_buff a3, offset 0
|
||||||
|
s32i.n a10, a3, 4 // save 32 bits from 32-bit color a10 to dest_buff a3, offset 4
|
||||||
|
addi.n a3, a3, 8 // increment dest_buff pointer by 8 bytes
|
||||||
|
_mod_8_check:
|
||||||
|
|
||||||
|
// Check modulo 4 of the dest_w_bytes, if - then set 4 bytes
|
||||||
|
bbci a11, 2, _mod_4_check // branch if 2-nd bit of dest_w_bytes is clear
|
||||||
|
s32i.n a10, a3, 0 // save 32 bits from 32-bit color a10 to dest_buff a3, offset 0
|
||||||
|
addi.n a3, a3, 4 // increment dest_buff pointer by 4
|
||||||
|
_mod_4_check:
|
||||||
|
|
||||||
|
// Check modulo 2 of the dest_w_bytes, if - then set 2 bytes
|
||||||
|
bbci a11, 1, _mod_2_check // branch if 1-st bit of dest_w_bytes is clear
|
||||||
|
s16i a12, a3, 0 // save 16 bits from 16-bit color a12 to dest_buff a3, offset 0
|
||||||
|
addi.n a3, a3, 2 // increment dest_buff pointer by 2 bytes
|
||||||
|
_mod_2_check:
|
||||||
|
|
||||||
|
add a3, a3, a6 // dest_buff + dest_stride
|
||||||
|
addi.n a5, a5, -1 // decrease the outer loop
|
||||||
|
and a7, a8, a3 // a7 = dest_buff AND 0x3 (chck if the address is 4-byte aligned)
|
||||||
|
bnez a5, .outer_loop
|
||||||
|
|
||||||
|
movi.n a2, 1 // return LV_RESULT_OK = 1
|
||||||
|
retw.n // return
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||||
|
# in this exact order for cmake to work correctly
|
||||||
|
cmake_minimum_required(VERSION 3.5)
|
||||||
|
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components")
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
project(test_esp_lvgl_port)
|
||||||
@ -0,0 +1 @@
|
|||||||
|
idf_component_register(SRCS "test.c")
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
## IDF Component Manager Manifest File
|
||||||
|
dependencies:
|
||||||
|
idf: ">=4.4"
|
||||||
|
esp_lcd_touch_tt21100:
|
||||||
|
version: "^1"
|
||||||
|
override_path: "../../../../lcd_touch/esp_lcd_touch_tt21100/"
|
||||||
|
esp_lvgl_port:
|
||||||
|
version: "*"
|
||||||
|
override_path: "../../../"
|
||||||
356
Libraries/esp_lvgl_port/test_apps/lvgl_port/main/test.c
Normal file
356
Libraries/esp_lvgl_port/test_apps/lvgl_port/main/test.c
Normal file
@ -0,0 +1,356 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: CC0-1.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "esp_check.h"
|
||||||
|
#include "driver/i2c.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
#include "driver/spi_master.h"
|
||||||
|
#include "esp_lcd_panel_io.h"
|
||||||
|
#include "esp_lcd_panel_vendor.h"
|
||||||
|
#include "esp_lcd_panel_ops.h"
|
||||||
|
#include "esp_lvgl_port.h"
|
||||||
|
|
||||||
|
#include "esp_lcd_touch_tt21100.h"
|
||||||
|
|
||||||
|
#include "unity.h"
|
||||||
|
|
||||||
|
/* LCD size */
|
||||||
|
#define EXAMPLE_LCD_H_RES (320)
|
||||||
|
#define EXAMPLE_LCD_V_RES (240)
|
||||||
|
|
||||||
|
/* LCD settings */
|
||||||
|
#define EXAMPLE_LCD_SPI_NUM (SPI3_HOST)
|
||||||
|
#define EXAMPLE_LCD_PIXEL_CLK_HZ (40 * 1000 * 1000)
|
||||||
|
#define EXAMPLE_LCD_CMD_BITS (8)
|
||||||
|
#define EXAMPLE_LCD_PARAM_BITS (8)
|
||||||
|
#define EXAMPLE_LCD_COLOR_SPACE (ESP_LCD_COLOR_SPACE_BGR)
|
||||||
|
#define EXAMPLE_LCD_BITS_PER_PIXEL (16)
|
||||||
|
#define EXAMPLE_LCD_DRAW_BUFF_DOUBLE (1)
|
||||||
|
#define EXAMPLE_LCD_DRAW_BUFF_HEIGHT (50)
|
||||||
|
#define EXAMPLE_LCD_BL_ON_LEVEL (1)
|
||||||
|
|
||||||
|
/* LCD pins */
|
||||||
|
#define EXAMPLE_LCD_GPIO_SCLK (GPIO_NUM_7)
|
||||||
|
#define EXAMPLE_LCD_GPIO_MOSI (GPIO_NUM_6)
|
||||||
|
#define EXAMPLE_LCD_GPIO_RST (GPIO_NUM_48)
|
||||||
|
#define EXAMPLE_LCD_GPIO_DC (GPIO_NUM_4)
|
||||||
|
#define EXAMPLE_LCD_GPIO_CS (GPIO_NUM_5)
|
||||||
|
#define EXAMPLE_LCD_GPIO_BL (GPIO_NUM_45)
|
||||||
|
|
||||||
|
/* Touch settings */
|
||||||
|
#define EXAMPLE_TOUCH_I2C_NUM (0)
|
||||||
|
#define EXAMPLE_TOUCH_I2C_CLK_HZ (400000)
|
||||||
|
|
||||||
|
/* LCD touch pins */
|
||||||
|
#define EXAMPLE_TOUCH_I2C_SCL (GPIO_NUM_18)
|
||||||
|
#define EXAMPLE_TOUCH_I2C_SDA (GPIO_NUM_8)
|
||||||
|
#define EXAMPLE_TOUCH_GPIO_INT (GPIO_NUM_3)
|
||||||
|
|
||||||
|
static char *TAG = "test";
|
||||||
|
|
||||||
|
/* LCD IO and panel */
|
||||||
|
static esp_lcd_panel_io_handle_t lcd_io = NULL;
|
||||||
|
static esp_lcd_panel_handle_t lcd_panel = NULL;
|
||||||
|
static esp_lcd_panel_io_handle_t tp_io_handle = NULL;
|
||||||
|
static esp_lcd_touch_handle_t touch_handle = NULL;
|
||||||
|
|
||||||
|
/* LVGL display and touch */
|
||||||
|
static lv_display_t *lvgl_disp = NULL;
|
||||||
|
static lv_indev_t *lvgl_touch_indev = NULL;
|
||||||
|
|
||||||
|
static esp_err_t app_lcd_init(void)
|
||||||
|
{
|
||||||
|
esp_err_t ret = ESP_OK;
|
||||||
|
|
||||||
|
/* LCD backlight */
|
||||||
|
gpio_config_t bk_gpio_config = {
|
||||||
|
.mode = GPIO_MODE_OUTPUT,
|
||||||
|
.pin_bit_mask = 1ULL << EXAMPLE_LCD_GPIO_BL
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));
|
||||||
|
|
||||||
|
/* LCD initialization */
|
||||||
|
ESP_LOGD(TAG, "Initialize SPI bus");
|
||||||
|
const spi_bus_config_t buscfg = {
|
||||||
|
.sclk_io_num = EXAMPLE_LCD_GPIO_SCLK,
|
||||||
|
.mosi_io_num = EXAMPLE_LCD_GPIO_MOSI,
|
||||||
|
.miso_io_num = GPIO_NUM_NC,
|
||||||
|
.quadwp_io_num = GPIO_NUM_NC,
|
||||||
|
.quadhd_io_num = GPIO_NUM_NC,
|
||||||
|
.max_transfer_sz = EXAMPLE_LCD_H_RES * EXAMPLE_LCD_DRAW_BUFF_HEIGHT * sizeof(uint16_t),
|
||||||
|
};
|
||||||
|
ESP_RETURN_ON_ERROR(spi_bus_initialize(EXAMPLE_LCD_SPI_NUM, &buscfg, SPI_DMA_CH_AUTO), TAG, "SPI init failed");
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "Install panel IO");
|
||||||
|
const esp_lcd_panel_io_spi_config_t io_config = {
|
||||||
|
.dc_gpio_num = EXAMPLE_LCD_GPIO_DC,
|
||||||
|
.cs_gpio_num = EXAMPLE_LCD_GPIO_CS,
|
||||||
|
.pclk_hz = EXAMPLE_LCD_PIXEL_CLK_HZ,
|
||||||
|
.lcd_cmd_bits = EXAMPLE_LCD_CMD_BITS,
|
||||||
|
.lcd_param_bits = EXAMPLE_LCD_PARAM_BITS,
|
||||||
|
.spi_mode = 0,
|
||||||
|
.trans_queue_depth = 10,
|
||||||
|
};
|
||||||
|
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)EXAMPLE_LCD_SPI_NUM, &io_config, &lcd_io), err, TAG, "New panel IO failed");
|
||||||
|
|
||||||
|
ESP_LOGD(TAG, "Install LCD driver");
|
||||||
|
const esp_lcd_panel_dev_config_t panel_config = {
|
||||||
|
.reset_gpio_num = EXAMPLE_LCD_GPIO_RST,
|
||||||
|
.color_space = EXAMPLE_LCD_COLOR_SPACE,
|
||||||
|
.bits_per_pixel = EXAMPLE_LCD_BITS_PER_PIXEL,
|
||||||
|
};
|
||||||
|
ESP_GOTO_ON_ERROR(esp_lcd_new_panel_st7789(lcd_io, &panel_config, &lcd_panel), err, TAG, "New panel failed");
|
||||||
|
|
||||||
|
esp_lcd_panel_reset(lcd_panel);
|
||||||
|
esp_lcd_panel_init(lcd_panel);
|
||||||
|
esp_lcd_panel_mirror(lcd_panel, true, true);
|
||||||
|
esp_lcd_panel_disp_on_off(lcd_panel, true);
|
||||||
|
|
||||||
|
/* LCD backlight on */
|
||||||
|
ESP_ERROR_CHECK(gpio_set_level(EXAMPLE_LCD_GPIO_BL, EXAMPLE_LCD_BL_ON_LEVEL));
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
err:
|
||||||
|
if (lcd_panel) {
|
||||||
|
esp_lcd_panel_del(lcd_panel);
|
||||||
|
}
|
||||||
|
if (lcd_io) {
|
||||||
|
esp_lcd_panel_io_del(lcd_io);
|
||||||
|
}
|
||||||
|
spi_bus_free(EXAMPLE_LCD_SPI_NUM);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t app_lcd_deinit(void)
|
||||||
|
{
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_del(lcd_panel), TAG, "LCD panel deinit failed");
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_del(lcd_io), TAG, "LCD IO deinit failed");
|
||||||
|
ESP_RETURN_ON_ERROR(spi_bus_free(EXAMPLE_LCD_SPI_NUM), TAG, "SPI BUS free failed");
|
||||||
|
ESP_RETURN_ON_ERROR(gpio_reset_pin(EXAMPLE_LCD_GPIO_BL), TAG, "Reset BL pin failed");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t app_touch_init(void)
|
||||||
|
{
|
||||||
|
/* Initilize I2C */
|
||||||
|
const i2c_config_t i2c_conf = {
|
||||||
|
.mode = I2C_MODE_MASTER,
|
||||||
|
.sda_io_num = EXAMPLE_TOUCH_I2C_SDA,
|
||||||
|
.sda_pullup_en = GPIO_PULLUP_DISABLE,
|
||||||
|
.scl_io_num = EXAMPLE_TOUCH_I2C_SCL,
|
||||||
|
.scl_pullup_en = GPIO_PULLUP_DISABLE,
|
||||||
|
.master.clk_speed = EXAMPLE_TOUCH_I2C_CLK_HZ
|
||||||
|
};
|
||||||
|
ESP_RETURN_ON_ERROR(i2c_param_config(EXAMPLE_TOUCH_I2C_NUM, &i2c_conf), TAG, "I2C configuration failed");
|
||||||
|
ESP_RETURN_ON_ERROR(i2c_driver_install(EXAMPLE_TOUCH_I2C_NUM, i2c_conf.mode, 0, 0, 0), TAG, "I2C initialization failed");
|
||||||
|
|
||||||
|
/* Initialize touch HW */
|
||||||
|
const esp_lcd_touch_config_t tp_cfg = {
|
||||||
|
.x_max = EXAMPLE_LCD_H_RES,
|
||||||
|
.y_max = EXAMPLE_LCD_V_RES,
|
||||||
|
.rst_gpio_num = GPIO_NUM_NC, // Shared with LCD reset
|
||||||
|
.int_gpio_num = EXAMPLE_TOUCH_GPIO_INT,
|
||||||
|
.levels = {
|
||||||
|
.reset = 0,
|
||||||
|
.interrupt = 0,
|
||||||
|
},
|
||||||
|
.flags = {
|
||||||
|
.swap_xy = 0,
|
||||||
|
.mirror_x = 1,
|
||||||
|
.mirror_y = 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const esp_lcd_panel_io_i2c_config_t tp_io_config = ESP_LCD_TOUCH_IO_I2C_TT21100_CONFIG();
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_new_panel_io_i2c((esp_lcd_i2c_bus_handle_t)EXAMPLE_TOUCH_I2C_NUM, &tp_io_config, &tp_io_handle), TAG, "");
|
||||||
|
return esp_lcd_touch_new_i2c_tt21100(tp_io_handle, &tp_cfg, &touch_handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t app_touch_deinit(void)
|
||||||
|
{
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_touch_del(touch_handle), TAG, "Touch deinit failed");
|
||||||
|
ESP_RETURN_ON_ERROR(esp_lcd_panel_io_del(tp_io_handle), TAG, "Touch IO deinit failed");
|
||||||
|
ESP_RETURN_ON_ERROR(i2c_driver_delete(EXAMPLE_TOUCH_I2C_NUM), TAG, "I2C deinit failed");
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t app_lvgl_init(void)
|
||||||
|
{
|
||||||
|
/* Initialize LVGL */
|
||||||
|
const lvgl_port_cfg_t lvgl_cfg = {
|
||||||
|
.task_priority = 4, /* LVGL task priority */
|
||||||
|
.task_stack = 4096, /* LVGL task stack size */
|
||||||
|
.task_affinity = -1, /* LVGL task pinned to core (-1 is no affinity) */
|
||||||
|
.task_max_sleep_ms = 500, /* Maximum sleep in LVGL task */
|
||||||
|
.timer_period_ms = 5 /* LVGL timer tick period in ms */
|
||||||
|
};
|
||||||
|
ESP_RETURN_ON_ERROR(lvgl_port_init(&lvgl_cfg), TAG, "LVGL port initialization failed");
|
||||||
|
|
||||||
|
/* Add LCD screen */
|
||||||
|
ESP_LOGD(TAG, "Add LCD screen");
|
||||||
|
const lvgl_port_display_cfg_t disp_cfg = {
|
||||||
|
.io_handle = lcd_io,
|
||||||
|
.panel_handle = lcd_panel,
|
||||||
|
.buffer_size = EXAMPLE_LCD_H_RES * EXAMPLE_LCD_DRAW_BUFF_HEIGHT * sizeof(uint16_t),
|
||||||
|
.double_buffer = EXAMPLE_LCD_DRAW_BUFF_DOUBLE,
|
||||||
|
.hres = EXAMPLE_LCD_H_RES,
|
||||||
|
.vres = EXAMPLE_LCD_V_RES,
|
||||||
|
.monochrome = false,
|
||||||
|
/* Rotation values must be same as used in esp_lcd for initial settings of the screen */
|
||||||
|
.rotation = {
|
||||||
|
.swap_xy = false,
|
||||||
|
.mirror_x = true,
|
||||||
|
.mirror_y = true,
|
||||||
|
},
|
||||||
|
.flags = {
|
||||||
|
.buff_dma = true,
|
||||||
|
#if LVGL_VERSION_MAJOR >= 9
|
||||||
|
.swap_bytes = true,
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
};
|
||||||
|
lvgl_disp = lvgl_port_add_disp(&disp_cfg);
|
||||||
|
|
||||||
|
/* Add touch input (for selected screen) */
|
||||||
|
const lvgl_port_touch_cfg_t touch_cfg = {
|
||||||
|
.disp = lvgl_disp,
|
||||||
|
.handle = touch_handle,
|
||||||
|
};
|
||||||
|
lvgl_touch_indev = lvgl_port_add_touch(&touch_cfg);
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t app_lvgl_deinit(void)
|
||||||
|
{
|
||||||
|
ESP_RETURN_ON_ERROR(lvgl_port_remove_touch(lvgl_touch_indev), TAG, "LVGL touch removing failed");
|
||||||
|
gpio_uninstall_isr_service();
|
||||||
|
|
||||||
|
ESP_RETURN_ON_ERROR(lvgl_port_remove_disp(lvgl_disp), TAG, "LVGL disp removing failed");
|
||||||
|
ESP_RETURN_ON_ERROR(lvgl_port_deinit(), TAG, "LVGL deinit failed");
|
||||||
|
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _app_button_cb(lv_event_t *e)
|
||||||
|
{
|
||||||
|
lv_disp_rotation_t rotation = lv_disp_get_rotation(lvgl_disp);
|
||||||
|
rotation++;
|
||||||
|
if (rotation > LV_DISPLAY_ROTATION_270) {
|
||||||
|
rotation = LV_DISPLAY_ROTATION_0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* LCD HW rotation */
|
||||||
|
lv_disp_set_rotation(lvgl_disp, rotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void app_main_display(void)
|
||||||
|
{
|
||||||
|
lv_obj_t *scr = lv_scr_act();
|
||||||
|
|
||||||
|
/* Task lock */
|
||||||
|
lvgl_port_lock(0);
|
||||||
|
|
||||||
|
/* Your LVGL objects code here .... */
|
||||||
|
|
||||||
|
/* Label */
|
||||||
|
lv_obj_t *label = lv_label_create(scr);
|
||||||
|
lv_obj_set_width(label, EXAMPLE_LCD_H_RES);
|
||||||
|
lv_obj_set_style_text_align(label, LV_TEXT_ALIGN_CENTER, 0);
|
||||||
|
#if LVGL_VERSION_MAJOR == 8
|
||||||
|
lv_label_set_recolor(label, true);
|
||||||
|
lv_label_set_text(label, "#FF0000 "LV_SYMBOL_BELL" Hello world Espressif and LVGL "LV_SYMBOL_BELL"#\n#FF9400 "LV_SYMBOL_WARNING" For simplier initialization, use BSP "LV_SYMBOL_WARNING" #");
|
||||||
|
#else
|
||||||
|
lv_label_set_text(label, LV_SYMBOL_BELL" Hello world Espressif and LVGL "LV_SYMBOL_BELL"\n "LV_SYMBOL_WARNING" For simplier initialization, use BSP "LV_SYMBOL_WARNING);
|
||||||
|
#endif
|
||||||
|
lv_obj_align(label, LV_ALIGN_CENTER, 0, -30);
|
||||||
|
|
||||||
|
/* Button */
|
||||||
|
lv_obj_t *btn = lv_btn_create(scr);
|
||||||
|
label = lv_label_create(btn);
|
||||||
|
lv_label_set_text_static(label, "Rotate screen");
|
||||||
|
lv_obj_align(btn, LV_ALIGN_BOTTOM_MID, 0, -30);
|
||||||
|
lv_obj_add_event_cb(btn, _app_button_cb, LV_EVENT_CLICKED, NULL);
|
||||||
|
|
||||||
|
/* Task unlock */
|
||||||
|
lvgl_port_unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some resources are lazy allocated in the LCD driver, the threadhold is left for that case
|
||||||
|
#define TEST_MEMORY_LEAK_THRESHOLD (50)
|
||||||
|
|
||||||
|
static void check_leak(size_t start_free, size_t end_free, const char *type)
|
||||||
|
{
|
||||||
|
ssize_t delta = start_free - end_free;
|
||||||
|
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, start_free, end_free, delta);
|
||||||
|
TEST_ASSERT_GREATER_OR_EQUAL_MESSAGE (delta, TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Main test LVGL port", "[lvgl port]")
|
||||||
|
{
|
||||||
|
size_t start_freemem_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||||
|
size_t start_freemem_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Initilize LCD.");
|
||||||
|
|
||||||
|
/* LCD HW initialization */
|
||||||
|
TEST_ASSERT_EQUAL(app_lcd_init(), ESP_OK);
|
||||||
|
|
||||||
|
/* Touch initialization */
|
||||||
|
TEST_ASSERT_EQUAL(app_touch_init(), ESP_OK);
|
||||||
|
|
||||||
|
size_t start_lvgl_freemem_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||||
|
size_t start_lvgl_freemem_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "Initilize LVGL.");
|
||||||
|
|
||||||
|
/* LVGL initialization */
|
||||||
|
TEST_ASSERT_EQUAL(app_lvgl_init(), ESP_OK);
|
||||||
|
|
||||||
|
/* Show LVGL objects */
|
||||||
|
app_main_display();
|
||||||
|
|
||||||
|
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||||
|
|
||||||
|
/* LVGL deinit */
|
||||||
|
TEST_ASSERT_EQUAL(app_lvgl_deinit(), ESP_OK);
|
||||||
|
|
||||||
|
/* When using LVGL8, it takes some time to release all memory */
|
||||||
|
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "LVGL deinitialized.");
|
||||||
|
|
||||||
|
size_t end_lvgl_freemem_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||||
|
size_t end_lvgl_freemem_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||||
|
check_leak(start_lvgl_freemem_8bit, end_lvgl_freemem_8bit, "8BIT LVGL");
|
||||||
|
check_leak(start_lvgl_freemem_32bit, end_lvgl_freemem_32bit, "32BIT LVGL");
|
||||||
|
|
||||||
|
/* Touch deinit */
|
||||||
|
TEST_ASSERT_EQUAL(app_touch_deinit(), ESP_OK);
|
||||||
|
|
||||||
|
/* LCD deinit */
|
||||||
|
TEST_ASSERT_EQUAL(app_lcd_deinit(), ESP_OK);
|
||||||
|
|
||||||
|
vTaskDelay(1000 / portTICK_PERIOD_MS);
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "LCD deinitilized.");
|
||||||
|
|
||||||
|
size_t end_freemem_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||||
|
size_t end_freemem_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||||
|
check_leak(start_freemem_8bit, end_freemem_8bit, "8BIT");
|
||||||
|
check_leak(start_freemem_32bit, end_freemem_32bit, "32BIT");
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
printf("TEST ESP LVGL port\n\r");
|
||||||
|
unity_run_menu();
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
# sdkconfig to enable the SIMD in the lvgl_port
|
||||||
|
|
||||||
|
# Set custom ASM render and provide a header file with function prototypes
|
||||||
|
CONFIG_LV_DRAW_SW_ASM_CUSTOM=y
|
||||||
|
CONFIG_LV_USE_DRAW_SW_ASM=255
|
||||||
|
CONFIG_LV_DRAW_SW_ASM_CUSTOM_INCLUDE="esp_lvgl_port_lv_blend.h"
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
CONFIG_IDF_TARGET="esp32s3"
|
||||||
|
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
|
||||||
|
CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
|
||||||
|
CONFIG_LV_COLOR_16_SWAP=y
|
||||||
|
CONFIG_FREERTOS_HZ=1000
|
||||||
|
CONFIG_ESP_TASK_WDT_EN=n
|
||||||
|
CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y
|
||||||
7
Libraries/esp_lvgl_port/test_apps/simd/CMakeLists.txt
Normal file
7
Libraries/esp_lvgl_port/test_apps/simd/CMakeLists.txt
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
# The following lines of boilerplate have to be in your project's
|
||||||
|
# CMakeLists in this exact order for cmake to work correctly
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||||
|
|
||||||
|
project(test_lvgl_simd)
|
||||||
120
Libraries/esp_lvgl_port/test_apps/simd/README.md
Normal file
120
Libraries/esp_lvgl_port/test_apps/simd/README.md
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
# HW Acceleration using SIMD assembly instructions
|
||||||
|
|
||||||
|
Test app accommodates two types of tests: [`functionality test`](#Functionality-test) and [`benchmark test`](#Benchmark-test). Both tests are provided per each function written in assembly (typically per each assembly file). Both test apps use a hard copy of LVGL blending API, representing an ANSI implementation of the LVGL blending functions. The hard copy is present in [`lv_blend`](main/lv_blend/) folder.
|
||||||
|
|
||||||
|
Assembly source files could be found in the [`lvgl_port`](../../src/lvgl9/simd/) component. Header file with the assembly function prototypes is provided into the LVGL using Kconfig option `LV_DRAW_SW_ASM_CUSTOM_INCLUDE` and can be found in the [`lvgl_port/include`](../../include/esp_lvgl_port_lv_blend.h)
|
||||||
|
|
||||||
|
## Benchmark results
|
||||||
|
|
||||||
|
| Color format | Matrix size | Memory alignment | ASM version | ANSI C version |
|
||||||
|
| :----------- | :---------- | :--------------- | :------------- | :------------- |
|
||||||
|
| ARGB8888 | 128x128 | 16 byte | 0.327 | 1.600 |
|
||||||
|
| | 127x127 | 1 byte | 0.488 | 1.597 |
|
||||||
|
| RGB565 | 128x128 | 16 byte | 0.196 | 1.146 |
|
||||||
|
| | 127x127 | 1 byte | 0.497 | 1.124 |
|
||||||
|
* this data was obtained by running [benchmark tests](#benchmark-test) on 128x128 16 byte aligned matrix (ideal case) and 127x127 1 byte aligned matrix (worst case)
|
||||||
|
* the values represent cycles per sample to perform simple fill of the matrix on esp32s3
|
||||||
|
|
||||||
|
## Functionality test
|
||||||
|
* Tests, whether the HW accelerated assembly version of an LVGL function provides the same results as the ANSI version
|
||||||
|
* A top-level flow of the functionality test:
|
||||||
|
* generate a test matrix with test parameters (matrix width, matrix height, memory alignment.. )
|
||||||
|
* run an ANSI version of a DUT function with the generated input parameters
|
||||||
|
* run an assembly version of a DUT function with the same input parameters
|
||||||
|
* compare the results given by the ANSI and the assembly DUTs
|
||||||
|
* the results shall be the same
|
||||||
|
* repeat all the steps for a set of different input parameters, checking different matrix heights, widths..
|
||||||
|
|
||||||
|
## Benchmark test
|
||||||
|
* Tests, whether the HW accelerated assembly version of an LVGL function provides a performance increase over the ANSI version
|
||||||
|
* A top-level flow of the functionality test:
|
||||||
|
* generate a test matrix with test parameters (matrix width, matrix height, memory alignment.. )
|
||||||
|
* run an ANSI version of a DUT function with the generated input parameters multiple times (1000 times for example), while counting CPU cycles
|
||||||
|
* run an assembly version of a DUT function with the generated input parameters multiple times (1000 times for example), while counting CPU cycles
|
||||||
|
* compare the results given by the ANSI and the assembly DUTs
|
||||||
|
* the assembly version of the DUT function shall be faster than the ANSI version of the DUT function
|
||||||
|
|
||||||
|
## Run the test app
|
||||||
|
|
||||||
|
The test app is intended to be used only with esp32 and esp32s3
|
||||||
|
|
||||||
|
idf.py build
|
||||||
|
|
||||||
|
## Example output
|
||||||
|
|
||||||
|
```
|
||||||
|
I (302) main_task: Started on CPU0
|
||||||
|
I (322) main_task: Calling app_main()
|
||||||
|
______ _____ ______ _ _
|
||||||
|
| _ \/ ___|| ___ \ | | | |
|
||||||
|
| | | |\ `--. | |_/ / | |_ ___ ___ | |_
|
||||||
|
| | | | `--. \| __/ | __| / _ \/ __|| __|
|
||||||
|
| |/ / /\__/ /| | | |_ | __/\__ \| |_
|
||||||
|
|___/ \____/ \_| \__| \___||___/ \__|
|
||||||
|
|
||||||
|
|
||||||
|
Press ENTER to see the list of tests.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Here's the test menu, pick your combo:
|
||||||
|
(1) "Test fill functionality ARGB8888" [fill][functionality][ARGB8888]
|
||||||
|
(2) "Test fill functionality RGB565" [fill][functionality][RGB565]
|
||||||
|
(3) "LV Fill benchmark ARGB8888" [fill][benchmark][ARGB8888]
|
||||||
|
(4) "LV Fill benchmark RGB565" [fill][benchmark][RGB565]
|
||||||
|
|
||||||
|
Enter test for running.
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example of a functionality test run
|
||||||
|
|
||||||
|
```
|
||||||
|
Running Test fill functionality ARGB8888...
|
||||||
|
I (81512) LV Fill Functionality: running test for ARGB8888 color format
|
||||||
|
I (84732) LV Fill Functionality: test combinations: 31824
|
||||||
|
|
||||||
|
MALLOC_CAP_8BIT usage: Free memory delta: 0 Leak threshold: -800
|
||||||
|
MALLOC_CAP_32BIT usage: Free memory delta: 0 Leak threshold: -800
|
||||||
|
./main/test_lv_fill_functionality.c:102:Test fill functionality ARGB8888:PASS
|
||||||
|
Test ran in 3242ms
|
||||||
|
```
|
||||||
|
The test gives a simple FAIL/PASS result after comparison of the two DUTs results.
|
||||||
|
Also gives us an information about how many combinations (input parameters) the functionality test run with, `31824` in this case.
|
||||||
|
|
||||||
|
### Example of a benchmark test run
|
||||||
|
|
||||||
|
```
|
||||||
|
Running LV Fill benchmark ARGB8888...
|
||||||
|
I (163492) LV Fill Benchmark: running test for ARGB8888 color format
|
||||||
|
I (163522) LV Fill Benchmark: ASM ideal case: 5363.123 cycles for 128x128 matrix, 0.327 cycles per sample
|
||||||
|
I (163572) LV Fill Benchmark: ASM corner case: 7868.724 cycles for 127x127 matrix, 0.488 cycles per sample
|
||||||
|
|
||||||
|
I (163732) LV Fill Benchmark: ANSI ideal case: 26219.137 cycles for 128x128 matrix, 1.600 cycles per sample
|
||||||
|
I (163902) LV Fill Benchmark: ANSI corner case: 25762.178 cycles for 127x127 matrix, 1.597 cycles per sample
|
||||||
|
|
||||||
|
MALLOC_CAP_8BIT usage: Free memory delta: -220 Leak threshold: -800
|
||||||
|
MALLOC_CAP_8BIT potential leak: Before 393820 bytes free, After 393600 bytes free (delta 220)
|
||||||
|
MALLOC_CAP_32BIT usage: Free memory delta: -220 Leak threshold: -800
|
||||||
|
MALLOC_CAP_32BIT potential leak: Before 393820 bytes free, After 393600 bytes free (delta 220)
|
||||||
|
./main/test_lv_fill_benchmark.c:69:LV Fill benchmark ARGB8888:PASS
|
||||||
|
Test ran in 458ms
|
||||||
|
```
|
||||||
|
|
||||||
|
The test provides couple of information:
|
||||||
|
* Total number of CPU cycles for the whole DUT function
|
||||||
|
* `5363.123` cycles for the assembly DUT function
|
||||||
|
* `26219.137` cycles for the ANSI DUT function
|
||||||
|
* Number of CPU cycles per sample, which is basically the total number of CPU cycles divided by the test matrix area
|
||||||
|
* `0.327` cycles per sample for the assembly DUT
|
||||||
|
* `1.6` cycles per sample for the ANSI DUT
|
||||||
|
* In this case, the assembly implementation has achieved a performance increase in around 4.9-times, comparing to the ANSI implementation.
|
||||||
|
* Range of the CPU cycles (a best case and a corner case scenarios) into which, the DUT functions are expected to fit into
|
||||||
|
* The execution time of those function highly depends on the input parameters, thus a boundary scenarios for input parameters shall be set
|
||||||
|
* An example of such a boundaries is in a table below
|
||||||
|
* The benchmark boundary would help us to get an performance expectations of the real scenarios
|
||||||
|
|
||||||
|
Example of an best and corner case input parameters for benchmark test, for a color format `ARGB8888`
|
||||||
|
| Test matrix params | Memory alignment | Width | Height | Stride |
|
||||||
|
| :----------------- | :--------------- | :------------- | :------------- | :------------- |
|
||||||
|
| Best case | 16-byte aligned | Multiple of 8 | Multiple of 8 | Multiple of 8 |
|
||||||
|
| Corner case | 1-byte aligned | Not power of 2 | Not power of 2 | Not power of 2 |
|
||||||
21
Libraries/esp_lvgl_port/test_apps/simd/main/CMakeLists.txt
Normal file
21
Libraries/esp_lvgl_port/test_apps/simd/main/CMakeLists.txt
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# Include SIMD assembly source code for rendering
|
||||||
|
if(CONFIG_IDF_TARGET_ESP32 OR CONFIG_IDF_TARGET_ESP32S3)
|
||||||
|
message(VERBOSE "Compiling SIMD")
|
||||||
|
set(PORT_PATH "../../../src/lvgl9")
|
||||||
|
|
||||||
|
if(CONFIG_IDF_TARGET_ESP32S3)
|
||||||
|
file(GLOB_RECURSE ASM_SOURCES ${PORT_PATH}/simd/*_esp32s3.S) # Select only esp32s3 related files
|
||||||
|
else()
|
||||||
|
file(GLOB_RECURSE ASM_SOURCES ${PORT_PATH}/simd/*_esp32.S) # Select only esp32 related files
|
||||||
|
endif()
|
||||||
|
else()
|
||||||
|
message(WARNING "This test app is intended only for esp32 and esp32s3")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
# Hard copy of LV files
|
||||||
|
file(GLOB_RECURSE BLEND_SRCS lv_blend/src/*.c)
|
||||||
|
|
||||||
|
idf_component_register(SRCS "test_app_main.c" "test_lv_fill_functionality.c" "test_lv_fill_benchmark.c" ${BLEND_SRCS} ${ASM_SOURCES}
|
||||||
|
INCLUDE_DIRS "lv_blend/include" "../../../include"
|
||||||
|
REQUIRES unity
|
||||||
|
WHOLE_ARCHIVE)
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
# Creating CONFIG_LV_DRAW_SW_ASM_CUSTOM avaliable in lvgl Kconfig to enable assembler source files by deafult
|
||||||
|
|
||||||
|
config LV_DRAW_SW_ASM_CUSTOM
|
||||||
|
bool
|
||||||
|
default y
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* This file is derived from the LVGL project.
|
||||||
|
* See https://github.com/lvgl/lvgl for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file lv_assert.h
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LV_ASSERT_H
|
||||||
|
#define LV_ASSERT_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* INCLUDES
|
||||||
|
*********************/
|
||||||
|
#include "lv_log.h"
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* DEFINES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* TYPEDEFS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* GLOBAL PROTOTYPES
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* MACROS
|
||||||
|
**********************/
|
||||||
|
#define LV_ASSERT_HANDLER while(1); /*Halt by default*/
|
||||||
|
|
||||||
|
#define LV_ASSERT(expr) \
|
||||||
|
do { \
|
||||||
|
if(!(expr)) { \
|
||||||
|
LV_LOG_ERROR("Asserted at expression: %s", #expr); \
|
||||||
|
LV_ASSERT_HANDLER \
|
||||||
|
} \
|
||||||
|
} while(0)
|
||||||
|
|
||||||
|
/*-----------------
|
||||||
|
* ASSERTS
|
||||||
|
*-----------------*/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /*extern "C"*/
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /*LV_ASSERT_H*/
|
||||||
@ -0,0 +1,272 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* This file is derived from the LVGL project.
|
||||||
|
* See https://github.com/lvgl/lvgl for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file lv_color.h
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LV_COLOR_H
|
||||||
|
#define LV_COLOR_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* INCLUDES
|
||||||
|
*********************/
|
||||||
|
#include "stdint.h"
|
||||||
|
#include "stdbool.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* DEFINES
|
||||||
|
*********************/
|
||||||
|
#define LV_ATTRIBUTE_FAST_MEM
|
||||||
|
|
||||||
|
#ifndef LV_COLOR_MIX_ROUND_OFS
|
||||||
|
#ifdef CONFIG_LV_COLOR_MIX_ROUND_OFS
|
||||||
|
#define LV_COLOR_MIX_ROUND_OFS CONFIG_LV_COLOR_MIX_ROUND_OFS
|
||||||
|
#else
|
||||||
|
#define LV_COLOR_MIX_ROUND_OFS 0
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opacity percentages.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
LV_OPA_TRANSP = 0,
|
||||||
|
LV_OPA_0 = 0,
|
||||||
|
LV_OPA_10 = 25,
|
||||||
|
LV_OPA_20 = 51,
|
||||||
|
LV_OPA_30 = 76,
|
||||||
|
LV_OPA_40 = 102,
|
||||||
|
LV_OPA_50 = 127,
|
||||||
|
LV_OPA_60 = 153,
|
||||||
|
LV_OPA_70 = 178,
|
||||||
|
LV_OPA_80 = 204,
|
||||||
|
LV_OPA_90 = 229,
|
||||||
|
LV_OPA_100 = 255,
|
||||||
|
LV_OPA_COVER = 255,
|
||||||
|
} lv_opa_t;
|
||||||
|
|
||||||
|
#define LV_OPA_MIN 2 /*Opacities below this will be transparent*/
|
||||||
|
#define LV_OPA_MAX 253 /*Opacities above this will fully cover*/
|
||||||
|
|
||||||
|
#define LV_COLOR_FORMAT_GET_BPP(cf) ( \
|
||||||
|
(cf) == LV_COLOR_FORMAT_I1 ? 1 : \
|
||||||
|
(cf) == LV_COLOR_FORMAT_A1 ? 1 : \
|
||||||
|
(cf) == LV_COLOR_FORMAT_I2 ? 2 : \
|
||||||
|
(cf) == LV_COLOR_FORMAT_A2 ? 2 : \
|
||||||
|
(cf) == LV_COLOR_FORMAT_I4 ? 4 : \
|
||||||
|
(cf) == LV_COLOR_FORMAT_A4 ? 4 : \
|
||||||
|
(cf) == LV_COLOR_FORMAT_L8 ? 8 : \
|
||||||
|
(cf) == LV_COLOR_FORMAT_A8 ? 8 : \
|
||||||
|
(cf) == LV_COLOR_FORMAT_I8 ? 8 : \
|
||||||
|
(cf) == LV_COLOR_FORMAT_AL88 ? 16 : \
|
||||||
|
(cf) == LV_COLOR_FORMAT_RGB565 ? 16 : \
|
||||||
|
(cf) == LV_COLOR_FORMAT_RGB565A8 ? 16 : \
|
||||||
|
(cf) == LV_COLOR_FORMAT_ARGB8565 ? 24 : \
|
||||||
|
(cf) == LV_COLOR_FORMAT_RGB888 ? 24 : \
|
||||||
|
(cf) == LV_COLOR_FORMAT_ARGB8888 ? 32 : \
|
||||||
|
(cf) == LV_COLOR_FORMAT_XRGB8888 ? 32 : \
|
||||||
|
0 \
|
||||||
|
)
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* TYPEDEFS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t blue;
|
||||||
|
uint8_t green;
|
||||||
|
uint8_t red;
|
||||||
|
} lv_color_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t blue : 5;
|
||||||
|
uint16_t green : 6;
|
||||||
|
uint16_t red : 5;
|
||||||
|
} lv_color16_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t blue;
|
||||||
|
uint8_t green;
|
||||||
|
uint8_t red;
|
||||||
|
uint8_t alpha;
|
||||||
|
} lv_color32_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint16_t h;
|
||||||
|
uint8_t s;
|
||||||
|
uint8_t v;
|
||||||
|
} lv_color_hsv_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint8_t lumi;
|
||||||
|
uint8_t alpha;
|
||||||
|
} lv_color16a_t;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
LV_COLOR_FORMAT_UNKNOWN = 0,
|
||||||
|
|
||||||
|
LV_COLOR_FORMAT_RAW = 0x01,
|
||||||
|
LV_COLOR_FORMAT_RAW_ALPHA = 0x02,
|
||||||
|
|
||||||
|
/*<=1 byte (+alpha) formats*/
|
||||||
|
LV_COLOR_FORMAT_L8 = 0x06,
|
||||||
|
LV_COLOR_FORMAT_I1 = 0x07,
|
||||||
|
LV_COLOR_FORMAT_I2 = 0x08,
|
||||||
|
LV_COLOR_FORMAT_I4 = 0x09,
|
||||||
|
LV_COLOR_FORMAT_I8 = 0x0A,
|
||||||
|
LV_COLOR_FORMAT_A8 = 0x0E,
|
||||||
|
|
||||||
|
/*2 byte (+alpha) formats*/
|
||||||
|
LV_COLOR_FORMAT_RGB565 = 0x12,
|
||||||
|
LV_COLOR_FORMAT_ARGB8565 = 0x13, /**< Not supported by sw renderer yet. */
|
||||||
|
LV_COLOR_FORMAT_RGB565A8 = 0x14, /**< Color array followed by Alpha array*/
|
||||||
|
LV_COLOR_FORMAT_AL88 = 0x15, /**< L8 with alpha >*/
|
||||||
|
|
||||||
|
/*3 byte (+alpha) formats*/
|
||||||
|
LV_COLOR_FORMAT_RGB888 = 0x0F,
|
||||||
|
LV_COLOR_FORMAT_ARGB8888 = 0x10,
|
||||||
|
LV_COLOR_FORMAT_XRGB8888 = 0x11,
|
||||||
|
|
||||||
|
/*Formats not supported by software renderer but kept here so GPU can use it*/
|
||||||
|
LV_COLOR_FORMAT_A1 = 0x0B,
|
||||||
|
LV_COLOR_FORMAT_A2 = 0x0C,
|
||||||
|
LV_COLOR_FORMAT_A4 = 0x0D,
|
||||||
|
|
||||||
|
/* reference to https://wiki.videolan.org/YUV/ */
|
||||||
|
/*YUV planar formats*/
|
||||||
|
LV_COLOR_FORMAT_YUV_START = 0x20,
|
||||||
|
LV_COLOR_FORMAT_I420 = LV_COLOR_FORMAT_YUV_START, /*YUV420 planar(3 plane)*/
|
||||||
|
LV_COLOR_FORMAT_I422 = 0x21, /*YUV422 planar(3 plane)*/
|
||||||
|
LV_COLOR_FORMAT_I444 = 0x22, /*YUV444 planar(3 plane)*/
|
||||||
|
LV_COLOR_FORMAT_I400 = 0x23, /*YUV400 no chroma channel*/
|
||||||
|
LV_COLOR_FORMAT_NV21 = 0x24, /*YUV420 planar(2 plane), UV plane in 'V, U, V, U'*/
|
||||||
|
LV_COLOR_FORMAT_NV12 = 0x25, /*YUV420 planar(2 plane), UV plane in 'U, V, U, V'*/
|
||||||
|
|
||||||
|
/*YUV packed formats*/
|
||||||
|
LV_COLOR_FORMAT_YUY2 = 0x26, /*YUV422 packed like 'Y U Y V'*/
|
||||||
|
LV_COLOR_FORMAT_UYVY = 0x27, /*YUV422 packed like 'U Y V Y'*/
|
||||||
|
|
||||||
|
LV_COLOR_FORMAT_YUV_END = LV_COLOR_FORMAT_UYVY,
|
||||||
|
|
||||||
|
/*Color formats in which LVGL can render*/
|
||||||
|
#if LV_COLOR_DEPTH == 8
|
||||||
|
LV_COLOR_FORMAT_NATIVE = LV_COLOR_FORMAT_L8,
|
||||||
|
LV_COLOR_FORMAT_NATIVE_WITH_ALPHA = LV_COLOR_FORMAT_AL88,
|
||||||
|
#elif LV_COLOR_DEPTH == 16
|
||||||
|
LV_COLOR_FORMAT_NATIVE = LV_COLOR_FORMAT_RGB565,
|
||||||
|
LV_COLOR_FORMAT_NATIVE_WITH_ALPHA = LV_COLOR_FORMAT_RGB565A8,
|
||||||
|
#elif LV_COLOR_DEPTH == 24
|
||||||
|
LV_COLOR_FORMAT_NATIVE = LV_COLOR_FORMAT_RGB888,
|
||||||
|
LV_COLOR_FORMAT_NATIVE_WITH_ALPHA = LV_COLOR_FORMAT_ARGB8888,
|
||||||
|
#elif LV_COLOR_DEPTH == 32
|
||||||
|
LV_COLOR_FORMAT_NATIVE = LV_COLOR_FORMAT_XRGB8888,
|
||||||
|
LV_COLOR_FORMAT_NATIVE_WITH_ALPHA = LV_COLOR_FORMAT_ARGB8888,
|
||||||
|
#endif
|
||||||
|
} lv_color_format_t;
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* MACROS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
#define LV_COLOR_MAKE(r8, g8, b8) {b8, g8, r8}
|
||||||
|
|
||||||
|
#define LV_OPA_MIX2(a1, a2) (((int32_t)(a1) * (a2)) >> 8)
|
||||||
|
#define LV_OPA_MIX3(a1, a2, a3) (((int32_t)(a1) * (a2) * (a3)) >> 16)
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* GLOBAL PROTOTYPES
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an ARGB8888 color from RGB888 + alpha
|
||||||
|
* @param color an RGB888 color
|
||||||
|
* @param opa the alpha value
|
||||||
|
* @return the ARGB8888 color
|
||||||
|
*/
|
||||||
|
lv_color32_t lv_color_to_32(lv_color_t color, lv_opa_t opa);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert am RGB888 color to RGB565 stored in `uint16_t`
|
||||||
|
* @param color and RGB888 color
|
||||||
|
* @return `color` as RGB565 on `uin16_t`
|
||||||
|
*/
|
||||||
|
uint16_t lv_color_to_u16(lv_color_t color);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convert am RGB888 color to XRGB8888 stored in `uint32_t`
|
||||||
|
* @param color and RGB888 color
|
||||||
|
* @return `color` as XRGB8888 on `uin32_t` (the alpha channel is always set to 0xFF)
|
||||||
|
*/
|
||||||
|
uint32_t lv_color_to_u32(lv_color_t color);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mix two RGB565 colors
|
||||||
|
* @param c1 the first color (typically the foreground color)
|
||||||
|
* @param c2 the second color (typically the background color)
|
||||||
|
* @param mix 0..255, or LV_OPA_0/10/20...
|
||||||
|
* @return mix == 0: c2
|
||||||
|
* mix == 255: c1
|
||||||
|
* mix == 128: 0.5 x c1 + 0.5 x c2
|
||||||
|
*/
|
||||||
|
static inline uint16_t LV_ATTRIBUTE_FAST_MEM lv_color_16_16_mix(uint16_t c1, uint16_t c2, uint8_t mix)
|
||||||
|
{
|
||||||
|
if (mix == 255) {
|
||||||
|
return c1;
|
||||||
|
}
|
||||||
|
if (mix == 0) {
|
||||||
|
return c2;
|
||||||
|
}
|
||||||
|
if (c1 == c2) {
|
||||||
|
return c1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t ret;
|
||||||
|
|
||||||
|
/* Source: https://stackoverflow.com/a/50012418/1999969*/
|
||||||
|
mix = (uint32_t)((uint32_t)mix + 4) >> 3;
|
||||||
|
|
||||||
|
/*0x7E0F81F = 0b00000111111000001111100000011111*/
|
||||||
|
uint32_t bg = (uint32_t)(c2 | ((uint32_t)c2 << 16)) & 0x7E0F81F;
|
||||||
|
uint32_t fg = (uint32_t)(c1 | ((uint32_t)c1 << 16)) & 0x7E0F81F;
|
||||||
|
uint32_t result = ((((fg - bg) * mix) >> 5) + bg) & 0x7E0F81F;
|
||||||
|
ret = (uint16_t)(result >> 16) | result;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if two ARGB8888 color are equal
|
||||||
|
* @param c1 the first color
|
||||||
|
* @param c2 the second color
|
||||||
|
* @return true: equal
|
||||||
|
*/
|
||||||
|
static inline bool lv_color32_eq(lv_color32_t c1, lv_color32_t c2)
|
||||||
|
{
|
||||||
|
return *((uint32_t *)&c1) == *((uint32_t *)&c2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* MACROS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
#include "lv_color_op.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /*extern "C"*/
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /*LV_COLOR_H*/
|
||||||
@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* This file is derived from the LVGL project.
|
||||||
|
* See https://github.com/lvgl/lvgl for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file lv_color_op.h
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LV_COLOR_OP_H
|
||||||
|
#define LV_COLOR_OP_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* INCLUDES
|
||||||
|
*********************/
|
||||||
|
#include "lv_math.h"
|
||||||
|
#include "lv_color.h"
|
||||||
|
#include "lv_types.h"
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* DEFINES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* TYPEDEFS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* GLOBAL PROTOTYPES
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mix two colors with a given ratio.
|
||||||
|
* @param c1 the first color to mix (usually the foreground)
|
||||||
|
* @param c2 the second color to mix (usually the background)
|
||||||
|
* @param mix The ratio of the colors. 0: full `c2`, 255: full `c1`, 127: half `c1` and half`c2`
|
||||||
|
* @return the mixed color
|
||||||
|
*/
|
||||||
|
static inline lv_color_t LV_ATTRIBUTE_FAST_MEM lv_color_mix(lv_color_t c1, lv_color_t c2, uint8_t mix)
|
||||||
|
{
|
||||||
|
lv_color_t ret;
|
||||||
|
|
||||||
|
ret.red = LV_UDIV255((uint16_t)c1.red * mix + c2.red * (255 - mix) + LV_COLOR_MIX_ROUND_OFS);
|
||||||
|
ret.green = LV_UDIV255((uint16_t)c1.green * mix + c2.green * (255 - mix) + LV_COLOR_MIX_ROUND_OFS);
|
||||||
|
ret.blue = LV_UDIV255((uint16_t)c1.blue * mix + c2.blue * (255 - mix) + LV_COLOR_MIX_ROUND_OFS);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param fg
|
||||||
|
* @param bg
|
||||||
|
* @return
|
||||||
|
* @note Use bg.alpha in the return value
|
||||||
|
* @note Use fg.alpha as mix ratio
|
||||||
|
*/
|
||||||
|
static inline lv_color32_t lv_color_mix32(lv_color32_t fg, lv_color32_t bg)
|
||||||
|
{
|
||||||
|
if (fg.alpha >= LV_OPA_MAX) {
|
||||||
|
fg.alpha = bg.alpha;
|
||||||
|
return fg;
|
||||||
|
}
|
||||||
|
if (fg.alpha <= LV_OPA_MIN) {
|
||||||
|
return bg;
|
||||||
|
}
|
||||||
|
bg.red = (uint32_t)((uint32_t)fg.red * fg.alpha + (uint32_t)bg.red * (255 - fg.alpha)) >> 8;
|
||||||
|
bg.green = (uint32_t)((uint32_t)fg.green * fg.alpha + (uint32_t)bg.green * (255 - fg.alpha)) >> 8;
|
||||||
|
bg.blue = (uint32_t)((uint32_t)fg.blue * fg.alpha + (uint32_t)bg.blue * (255 - fg.alpha)) >> 8;
|
||||||
|
return bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* PREDEFINED COLORS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* MACROS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /*extern "C"*/
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /*LV_COLOR_H*/
|
||||||
@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* This file is derived from the LVGL project.
|
||||||
|
* See https://github.com/lvgl/lvgl for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file lv_draw_sw_blend.h
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_BLEND_H
|
||||||
|
#define LV_DRAW_SW_BLEND_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* INCLUDES
|
||||||
|
*********************/
|
||||||
|
#include "lv_style.h"
|
||||||
|
#include "lv_color.h"
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* DEFINES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* TYPEDEFS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
void *dest_buf;
|
||||||
|
int32_t dest_w;
|
||||||
|
int32_t dest_h;
|
||||||
|
int32_t dest_stride;
|
||||||
|
const lv_opa_t *mask_buf;
|
||||||
|
int32_t mask_stride;
|
||||||
|
lv_color_t color;
|
||||||
|
lv_opa_t opa;
|
||||||
|
bool use_asm;
|
||||||
|
} _lv_draw_sw_blend_fill_dsc_t;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
void *dest_buf;
|
||||||
|
int32_t dest_w;
|
||||||
|
int32_t dest_h;
|
||||||
|
int32_t dest_stride;
|
||||||
|
const lv_opa_t *mask_buf;
|
||||||
|
int32_t mask_stride;
|
||||||
|
const void *src_buf;
|
||||||
|
int32_t src_stride;
|
||||||
|
lv_color_format_t src_color_format;
|
||||||
|
lv_opa_t opa;
|
||||||
|
lv_blend_mode_t blend_mode;
|
||||||
|
} _lv_draw_sw_blend_image_dsc_t;
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* GLOBAL PROTOTYPES
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* MACROS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /*extern "C"*/
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /*LV_DRAW_SW_BLEND_H*/
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* This file is derived from the LVGL project.
|
||||||
|
* See https://github.com/lvgl/lvgl for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file lv_draw_sw_blend_argb8888.h
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_BLEND_ARGB8888_H
|
||||||
|
#define LV_DRAW_SW_BLEND_ARGB8888_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* INCLUDES
|
||||||
|
*********************/
|
||||||
|
#include "lv_draw_sw_blend.h"
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* DEFINES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* TYPEDEFS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* GLOBAL PROTOTYPES
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
void /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_sw_blend_color_to_argb8888(_lv_draw_sw_blend_fill_dsc_t *dsc);
|
||||||
|
|
||||||
|
void /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_sw_blend_image_to_argb8888(_lv_draw_sw_blend_image_dsc_t *dsc);
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* MACROS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /*extern "C"*/
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /*LV_DRAW_SW_BLEND_ARGB8888_H*/
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* This file is derived from the LVGL project.
|
||||||
|
* See https://github.com/lvgl/lvgl for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file lv_draw_sw_blend_rgb565.h
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_BLEND_RGB565_H
|
||||||
|
#define LV_DRAW_SW_BLEND_RGB565_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* INCLUDES
|
||||||
|
*********************/
|
||||||
|
#include "lv_draw_sw_blend.h"
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* DEFINES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* TYPEDEFS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* GLOBAL PROTOTYPES
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
void /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_sw_blend_color_to_rgb565(_lv_draw_sw_blend_fill_dsc_t *dsc);
|
||||||
|
|
||||||
|
void /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_sw_blend_image_to_rgb565(_lv_draw_sw_blend_image_dsc_t *dsc);
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* MACROS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /*extern "C"*/
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /*LV_DRAW_SW_BLEND_RGB565_H*/
|
||||||
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* This file is derived from the LVGL project.
|
||||||
|
* See https://github.com/lvgl/lvgl for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file lv_log.h
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LV_LOG_H
|
||||||
|
#define LV_LOG_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* INCLUDES
|
||||||
|
*********************/
|
||||||
|
#include "lv_types.h"
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* DEFINES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
/*Do nothing if `LV_USE_LOG 0`*/
|
||||||
|
#define _lv_log_add(level, file, line, ...)
|
||||||
|
#define LV_LOG_TRACE(...) do {}while(0)
|
||||||
|
#define LV_LOG_INFO(...) do {}while(0)
|
||||||
|
#define LV_LOG_WARN(...) do {}while(0)
|
||||||
|
#define LV_LOG_ERROR(...) do {}while(0)
|
||||||
|
#define LV_LOG_USER(...) do {}while(0)
|
||||||
|
#define LV_LOG(...) do {}while(0)
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /*extern "C"*/
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /*LV_LOG_H*/
|
||||||
@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* This file is derived from the LVGL project.
|
||||||
|
* See https://github.com/lvgl/lvgl for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file lv_math.h
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LV_MATH_H
|
||||||
|
#define LV_MATH_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* INCLUDES
|
||||||
|
*********************/
|
||||||
|
#include "lv_types.h"
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* DEFINES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* MACROS
|
||||||
|
**********************/
|
||||||
|
#define LV_MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||||
|
#define LV_MIN3(a, b, c) (LV_MIN(LV_MIN(a,b), c))
|
||||||
|
#define LV_MIN4(a, b, c, d) (LV_MIN(LV_MIN(a,b), LV_MIN(c,d)))
|
||||||
|
|
||||||
|
#define LV_MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||||
|
#define LV_MAX3(a, b, c) (LV_MAX(LV_MAX(a,b), c))
|
||||||
|
#define LV_MAX4(a, b, c, d) (LV_MAX(LV_MAX(a,b), LV_MAX(c,d)))
|
||||||
|
|
||||||
|
#define LV_CLAMP(min, val, max) (LV_MAX(min, (LV_MIN(val, max))))
|
||||||
|
|
||||||
|
#define LV_ABS(x) ((x) > 0 ? (x) : (-(x)))
|
||||||
|
#define LV_UDIV255(x) (((x) * 0x8081U) >> 0x17)
|
||||||
|
|
||||||
|
#define LV_IS_SIGNED(t) (((t)(-1)) < ((t)0))
|
||||||
|
#define LV_UMAX_OF(t) (((0x1ULL << ((sizeof(t) * 8ULL) - 1ULL)) - 1ULL) | (0xFULL << ((sizeof(t) * 8ULL) - 4ULL)))
|
||||||
|
#define LV_SMAX_OF(t) (((0x1ULL << ((sizeof(t) * 8ULL) - 1ULL)) - 1ULL) | (0x7ULL << ((sizeof(t) * 8ULL) - 4ULL)))
|
||||||
|
#define LV_MAX_OF(t) ((unsigned long)(LV_IS_SIGNED(t) ? LV_SMAX_OF(t) : LV_UMAX_OF(t)))
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /*extern "C"*/
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* This file is derived from the LVGL project.
|
||||||
|
* See https://github.com/lvgl/lvgl for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file lv_style.h
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LV_STYLE_H
|
||||||
|
#define LV_STYLE_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* INCLUDES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* DEFINES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* TYPEDEFS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Possible options how to blend opaque drawings
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
LV_BLEND_MODE_NORMAL, /**< Simply mix according to the opacity value*/
|
||||||
|
LV_BLEND_MODE_ADDITIVE, /**< Add the respective color channels*/
|
||||||
|
LV_BLEND_MODE_SUBTRACTIVE,/**< Subtract the foreground from the background*/
|
||||||
|
LV_BLEND_MODE_MULTIPLY, /**< Multiply the foreground and background*/
|
||||||
|
} lv_blend_mode_t;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /*extern "C"*/
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /*LV_STYLE_H*/
|
||||||
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* This file is derived from the LVGL project.
|
||||||
|
* See https://github.com/lvgl/lvgl for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file lv_types.h
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef LV_TYPES_H
|
||||||
|
#define LV_TYPES_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* TYPEDEFS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LVGL error codes.
|
||||||
|
*/
|
||||||
|
typedef enum {
|
||||||
|
LV_RESULT_INVALID = 0, /*Typically indicates that the object is deleted (become invalid) in the action
|
||||||
|
function or an operation was failed*/
|
||||||
|
LV_RESULT_OK, /*The object is valid (no deleted) after the action*/
|
||||||
|
} lv_result_t;
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* TYPEDEFS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
typedef uintptr_t lv_uintptr_t;
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* MACROS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
#define LV_UNUSED(x) ((void)x)
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /*extern "C"*/
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /*LV_TYPES_H*/
|
||||||
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* This file is derived from the LVGL project.
|
||||||
|
* See https://github.com/lvgl/lvgl for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file lv_color.c
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* INCLUDES
|
||||||
|
*********************/
|
||||||
|
#include "lv_color.h"
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* DEFINES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* TYPEDEFS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* STATIC PROTOTYPES
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* GLOBAL VARIABLES
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* STATIC VARIABLES
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* MACROS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* GLOBAL FUNCTIONS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
lv_color32_t lv_color_to_32(lv_color_t color, lv_opa_t opa)
|
||||||
|
{
|
||||||
|
lv_color32_t c32;
|
||||||
|
c32.red = color.red;
|
||||||
|
c32.green = color.green;
|
||||||
|
c32.blue = color.blue;
|
||||||
|
c32.alpha = opa;
|
||||||
|
return c32;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t lv_color_to_u16(lv_color_t color)
|
||||||
|
{
|
||||||
|
return ((color.red & 0xF8) << 8) + ((color.green & 0xFC) << 3) + ((color.blue & 0xF8) >> 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t lv_color_to_u32(lv_color_t color)
|
||||||
|
{
|
||||||
|
return (uint32_t)((uint32_t)0xff << 24) + (color.red << 16) + (color.green << 8) + (color.blue);
|
||||||
|
}
|
||||||
@ -0,0 +1,911 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* This file is derived from the LVGL project.
|
||||||
|
* See https://github.com/lvgl/lvgl for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file lv_draw_sw_blend.c
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* INCLUDES
|
||||||
|
*********************/
|
||||||
|
#include "lv_draw_sw_blend_to_argb8888.h"
|
||||||
|
|
||||||
|
#include "lv_assert.h"
|
||||||
|
#include "lv_types.h"
|
||||||
|
#include "lv_log.h"
|
||||||
|
#include "lv_draw_sw_blend.h"
|
||||||
|
#include "lv_math.h"
|
||||||
|
#include "lv_color.h"
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
#include "esp_lvgl_port_lv_blend.h"
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* DEFINES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
#define LV_ATTRIBUTE_FAST_MEM
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* TYPEDEFS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
lv_color32_t fg_saved;
|
||||||
|
lv_color32_t bg_saved;
|
||||||
|
lv_color32_t res_saved;
|
||||||
|
lv_opa_t res_alpha_saved;
|
||||||
|
lv_opa_t ratio_saved;
|
||||||
|
} lv_color_mix_alpha_cache_t;
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* STATIC PROTOTYPES
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
static void /* LV_ATTRIBUTE_FAST_MEM */ al88_image_blend(_lv_draw_sw_blend_image_dsc_t *dsc);
|
||||||
|
|
||||||
|
static void /* LV_ATTRIBUTE_FAST_MEM */ l8_image_blend(_lv_draw_sw_blend_image_dsc_t *dsc);
|
||||||
|
|
||||||
|
static void /* LV_ATTRIBUTE_FAST_MEM */ rgb565_image_blend(_lv_draw_sw_blend_image_dsc_t *dsc);
|
||||||
|
|
||||||
|
static void /* LV_ATTRIBUTE_FAST_MEM */ rgb888_image_blend(_lv_draw_sw_blend_image_dsc_t *dsc,
|
||||||
|
const uint8_t src_px_size);
|
||||||
|
|
||||||
|
static void /* LV_ATTRIBUTE_FAST_MEM */ argb8888_image_blend(_lv_draw_sw_blend_image_dsc_t *dsc);
|
||||||
|
|
||||||
|
static inline void /* LV_ATTRIBUTE_FAST_MEM */ lv_color_8_32_mix(const uint8_t src, lv_color32_t *dest, uint8_t mix);
|
||||||
|
|
||||||
|
static inline lv_color32_t /* LV_ATTRIBUTE_FAST_MEM */ lv_color_32_32_mix(lv_color32_t fg, lv_color32_t bg,
|
||||||
|
lv_color_mix_alpha_cache_t *cache);
|
||||||
|
|
||||||
|
static void lv_color_mix_with_alpha_cache_init(lv_color_mix_alpha_cache_t *cache);
|
||||||
|
|
||||||
|
static inline void /* LV_ATTRIBUTE_FAST_MEM */ blend_non_normal_pixel(lv_color32_t *dest, lv_color32_t src,
|
||||||
|
lv_blend_mode_t mode, lv_color_mix_alpha_cache_t *cache);
|
||||||
|
static inline void * /* LV_ATTRIBUTE_FAST_MEM */ drawbuf_next_row(const void *buf, uint32_t stride);
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* STATIC VARIABLES
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* MACROS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_COLOR_BLEND_TO_ARGB8888
|
||||||
|
#define LV_DRAW_SW_COLOR_BLEND_TO_ARGB8888(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_COLOR_BLEND_TO_ARGB8888_WITH_OPA
|
||||||
|
#define LV_DRAW_SW_COLOR_BLEND_TO_ARGB8888_WITH_OPA(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_COLOR_BLEND_TO_ARGB8888_WITH_MASK
|
||||||
|
#define LV_DRAW_SW_COLOR_BLEND_TO_ARGB8888_WITH_MASK(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_COLOR_BLEND_TO_ARGB8888_MIX_MASK_OPA
|
||||||
|
#define LV_DRAW_SW_COLOR_BLEND_TO_ARGB8888_MIX_MASK_OPA(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_L8_BLEND_NORMAL_TO_ARGB8888
|
||||||
|
#define LV_DRAW_SW_L8_BLEND_NORMAL_TO_ARGB8888(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_L8_BLEND_NORMAL_TO_ARGB8888_WITH_OPA
|
||||||
|
#define LV_DRAW_SW_L8_BLEND_NORMAL_TO_ARGB8888_WITH_OPA(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_L8_BLEND_NORMAL_TO_ARGB8888_WITH_MASK
|
||||||
|
#define LV_DRAW_SW_L8_BLEND_NORMAL_TO_ARGB8888_WITH_MASK(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_L8_BLEND_NORMAL_TO_ARGB8888_MIX_MASK_OPA
|
||||||
|
#define LV_DRAW_SW_L8_BLEND_NORMAL_TO_ARGB8888_MIX_MASK_OPA(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_AL88_BLEND_NORMAL_TO_ARGB8888
|
||||||
|
#define LV_DRAW_SW_AL88_BLEND_NORMAL_TO_ARGB8888(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_AL88_BLEND_NORMAL_TO_ARGB8888_WITH_OPA
|
||||||
|
#define LV_DRAW_SW_AL88_BLEND_NORMAL_TO_ARGB8888_WITH_OPA(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_AL88_BLEND_NORMAL_TO_ARGB8888_WITH_MASK
|
||||||
|
#define LV_DRAW_SW_AL88_BLEND_NORMAL_TO_ARGB8888_WITH_MASK(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_AL88_BLEND_NORMAL_TO_ARGB8888_MIX_MASK_OPA
|
||||||
|
#define LV_DRAW_SW_AL88_BLEND_NORMAL_TO_ARGB8888_MIX_MASK_OPA(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_ARGB8888
|
||||||
|
#define LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_ARGB8888(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_ARGB8888_WITH_OPA
|
||||||
|
#define LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_ARGB8888_WITH_OPA(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_ARGB8888_WITH_MASK
|
||||||
|
#define LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_ARGB8888_WITH_MASK(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_ARGB8888_MIX_MASK_OPA
|
||||||
|
#define LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_ARGB8888_MIX_MASK_OPA(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_ARGB8888
|
||||||
|
#define LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_ARGB8888(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_ARGB8888_WITH_OPA
|
||||||
|
#define LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_ARGB8888_WITH_OPA(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_ARGB8888_WITH_MASK
|
||||||
|
#define LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_ARGB8888_WITH_MASK(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_ARGB8888_MIX_MASK_OPA
|
||||||
|
#define LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_ARGB8888_MIX_MASK_OPA(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_ARGB8888
|
||||||
|
#define LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_ARGB8888(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_ARGB8888_WITH_OPA
|
||||||
|
#define LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_ARGB8888_WITH_OPA(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_ARGB8888_WITH_MASK
|
||||||
|
#define LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_ARGB8888_WITH_MASK(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_ARGB8888_MIX_MASK_OPA
|
||||||
|
#define LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_ARGB8888_MIX_MASK_OPA(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* GLOBAL FUNCTIONS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
void LV_ATTRIBUTE_FAST_MEM lv_draw_sw_blend_color_to_argb8888(_lv_draw_sw_blend_fill_dsc_t *dsc)
|
||||||
|
{
|
||||||
|
int32_t w = dsc->dest_w;
|
||||||
|
int32_t h = dsc->dest_h;
|
||||||
|
lv_opa_t opa = dsc->opa;
|
||||||
|
const lv_opa_t *mask = dsc->mask_buf;
|
||||||
|
int32_t mask_stride = dsc->mask_stride;
|
||||||
|
int32_t dest_stride = dsc->dest_stride;
|
||||||
|
|
||||||
|
lv_color_mix_alpha_cache_t cache;
|
||||||
|
lv_color_mix_with_alpha_cache_init(&cache);
|
||||||
|
|
||||||
|
int32_t x;
|
||||||
|
int32_t y;
|
||||||
|
|
||||||
|
LV_UNUSED(w);
|
||||||
|
LV_UNUSED(h);
|
||||||
|
LV_UNUSED(x);
|
||||||
|
LV_UNUSED(y);
|
||||||
|
LV_UNUSED(opa);
|
||||||
|
LV_UNUSED(mask);
|
||||||
|
LV_UNUSED(mask_stride);
|
||||||
|
LV_UNUSED(dest_stride);
|
||||||
|
|
||||||
|
/*Simple fill*/
|
||||||
|
if (mask == NULL && opa >= LV_OPA_MAX) {
|
||||||
|
if (dsc->use_asm) {
|
||||||
|
LV_DRAW_SW_COLOR_BLEND_TO_ARGB8888(dsc);
|
||||||
|
} else {
|
||||||
|
uint32_t color32 = lv_color_to_u32(dsc->color);
|
||||||
|
uint32_t *dest_buf = dsc->dest_buf;
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (x = 0; x < w - 16; x += 16) {
|
||||||
|
dest_buf[x + 0] = color32;
|
||||||
|
dest_buf[x + 1] = color32;
|
||||||
|
dest_buf[x + 2] = color32;
|
||||||
|
dest_buf[x + 3] = color32;
|
||||||
|
|
||||||
|
dest_buf[x + 4] = color32;
|
||||||
|
dest_buf[x + 5] = color32;
|
||||||
|
dest_buf[x + 6] = color32;
|
||||||
|
dest_buf[x + 7] = color32;
|
||||||
|
|
||||||
|
dest_buf[x + 8] = color32;
|
||||||
|
dest_buf[x + 9] = color32;
|
||||||
|
dest_buf[x + 10] = color32;
|
||||||
|
dest_buf[x + 11] = color32;
|
||||||
|
|
||||||
|
dest_buf[x + 12] = color32;
|
||||||
|
dest_buf[x + 13] = color32;
|
||||||
|
dest_buf[x + 14] = color32;
|
||||||
|
dest_buf[x + 15] = color32;
|
||||||
|
}
|
||||||
|
for (; x < w; x ++) {
|
||||||
|
dest_buf[x] = color32;
|
||||||
|
}
|
||||||
|
|
||||||
|
dest_buf = drawbuf_next_row(dest_buf, dest_stride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
/*Opacity only*/
|
||||||
|
else if (mask == NULL && opa < LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_COLOR_BLEND_TO_ARGB8888_WITH_OPA(dsc)) {
|
||||||
|
lv_color32_t color_argb = lv_color_to_32(dsc->color, opa);
|
||||||
|
lv_color32_t *dest_buf = dsc->dest_buf;
|
||||||
|
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (x = 0; x < w; x++) {
|
||||||
|
dest_buf[x] = lv_color_32_32_mix(color_argb, dest_buf[x], &cache);
|
||||||
|
}
|
||||||
|
dest_buf = drawbuf_next_row(dest_buf, dest_stride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
/*Masked with full opacity*/
|
||||||
|
else if (mask && opa >= LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_COLOR_BLEND_TO_ARGB8888_WITH_MASK(dsc)) {
|
||||||
|
lv_color32_t color_argb = lv_color_to_32(dsc->color, 0xff);
|
||||||
|
lv_color32_t *dest_buf = dsc->dest_buf;
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (x = 0; x < w; x++) {
|
||||||
|
color_argb.alpha = mask[x];
|
||||||
|
dest_buf[x] = lv_color_32_32_mix(color_argb, dest_buf[x], &cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
dest_buf = drawbuf_next_row(dest_buf, dest_stride);
|
||||||
|
mask += mask_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
/*Masked with opacity*/
|
||||||
|
else {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_COLOR_BLEND_TO_ARGB8888_MIX_MASK_OPA(dsc)) {
|
||||||
|
lv_color32_t color_argb = lv_color_to_32(dsc->color, opa);
|
||||||
|
lv_color32_t *dest_buf = dsc->dest_buf;
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (x = 0; x < w; x++) {
|
||||||
|
color_argb.alpha = LV_OPA_MIX2(mask[x], opa);
|
||||||
|
dest_buf[x] = lv_color_32_32_mix(color_argb, dest_buf[x], &cache);
|
||||||
|
}
|
||||||
|
dest_buf = drawbuf_next_row(dest_buf, dest_stride);
|
||||||
|
mask += mask_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LV_ATTRIBUTE_FAST_MEM lv_draw_sw_blend_image_to_argb8888(_lv_draw_sw_blend_image_dsc_t *dsc)
|
||||||
|
{
|
||||||
|
switch (dsc->src_color_format) {
|
||||||
|
case LV_COLOR_FORMAT_RGB565:
|
||||||
|
rgb565_image_blend(dsc);
|
||||||
|
break;
|
||||||
|
case LV_COLOR_FORMAT_RGB888:
|
||||||
|
rgb888_image_blend(dsc, 3);
|
||||||
|
break;
|
||||||
|
case LV_COLOR_FORMAT_XRGB8888:
|
||||||
|
rgb888_image_blend(dsc, 4);
|
||||||
|
break;
|
||||||
|
case LV_COLOR_FORMAT_ARGB8888:
|
||||||
|
argb8888_image_blend(dsc);
|
||||||
|
break;
|
||||||
|
case LV_COLOR_FORMAT_L8:
|
||||||
|
l8_image_blend(dsc);
|
||||||
|
break;
|
||||||
|
case LV_COLOR_FORMAT_AL88:
|
||||||
|
al88_image_blend(dsc);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LV_LOG_WARN("Not supported source color format");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* STATIC FUNCTIONS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
static void LV_ATTRIBUTE_FAST_MEM al88_image_blend(_lv_draw_sw_blend_image_dsc_t *dsc)
|
||||||
|
{
|
||||||
|
int32_t w = dsc->dest_w;
|
||||||
|
int32_t h = dsc->dest_h;
|
||||||
|
lv_opa_t opa = dsc->opa;
|
||||||
|
lv_color32_t *dest_buf_c32 = dsc->dest_buf;
|
||||||
|
int32_t dest_stride = dsc->dest_stride;
|
||||||
|
const lv_color16a_t *src_buf_al88 = dsc->src_buf;
|
||||||
|
int32_t src_stride = dsc->src_stride;
|
||||||
|
const lv_opa_t *mask_buf = dsc->mask_buf;
|
||||||
|
int32_t mask_stride = dsc->mask_stride;
|
||||||
|
|
||||||
|
int32_t dest_x;
|
||||||
|
int32_t src_x;
|
||||||
|
int32_t y;
|
||||||
|
|
||||||
|
if (dsc->blend_mode == LV_BLEND_MODE_NORMAL) {
|
||||||
|
if (mask_buf == NULL && opa >= LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_AL88_BLEND_NORMAL_TO_ARGB8888(dsc)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (dest_x = 0, src_x = 0; src_x < w; dest_x++, src_x++) {
|
||||||
|
/*
|
||||||
|
dest_buf_c32[dest_x].alpha = src_buf_al88[src_x].alpha;
|
||||||
|
dest_buf_c32[dest_x].red = src_buf_al88[src_x].lumi;
|
||||||
|
dest_buf_c32[dest_x].green = src_buf_al88[src_x].lumi;
|
||||||
|
dest_buf_c32[dest_x].blue = src_buf_al88[src_x].lumi;
|
||||||
|
*/
|
||||||
|
lv_color_8_32_mix(src_buf_al88[src_x].lumi, &dest_buf_c32[dest_x], src_buf_al88[src_x].alpha);
|
||||||
|
}
|
||||||
|
dest_buf_c32 = drawbuf_next_row(dest_buf_c32, dest_stride);
|
||||||
|
src_buf_al88 = drawbuf_next_row(src_buf_al88, src_stride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (mask_buf == NULL && opa < LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_AL88_BLEND_NORMAL_TO_ARGB8888_WITH_OPA(dsc)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (dest_x = 0, src_x = 0; src_x < w; dest_x++, src_x++) {
|
||||||
|
lv_color_8_32_mix(src_buf_al88[src_x].lumi, &dest_buf_c32[dest_x], LV_OPA_MIX2(src_buf_al88[src_x].alpha, opa));
|
||||||
|
}
|
||||||
|
dest_buf_c32 = drawbuf_next_row(dest_buf_c32, dest_stride);
|
||||||
|
src_buf_al88 = drawbuf_next_row(src_buf_al88, src_stride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (mask_buf && opa >= LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_AL88_BLEND_NORMAL_TO_ARGB8888_WITH_MASK(dsc)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (dest_x = 0, src_x = 0; src_x < w; dest_x++, src_x++) {
|
||||||
|
lv_color_8_32_mix(src_buf_al88[src_x].lumi, &dest_buf_c32[dest_x], LV_OPA_MIX2(src_buf_al88[src_x].alpha,
|
||||||
|
mask_buf[src_x]));
|
||||||
|
}
|
||||||
|
dest_buf_c32 = drawbuf_next_row(dest_buf_c32, dest_stride);
|
||||||
|
src_buf_al88 = drawbuf_next_row(src_buf_al88, src_stride);
|
||||||
|
mask_buf += mask_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (mask_buf && opa < LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_AL88_BLEND_NORMAL_TO_ARGB8888_MIX_MASK_OPA(dsc)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (dest_x = 0, src_x = 0; src_x < w; dest_x++, src_x++) {
|
||||||
|
lv_color_8_32_mix(src_buf_al88[src_x].lumi, &dest_buf_c32[dest_x], LV_OPA_MIX3(src_buf_al88[src_x].alpha,
|
||||||
|
mask_buf[src_x], opa));
|
||||||
|
}
|
||||||
|
dest_buf_c32 = drawbuf_next_row(dest_buf_c32, dest_stride);
|
||||||
|
src_buf_al88 = drawbuf_next_row(src_buf_al88, src_stride);
|
||||||
|
mask_buf += mask_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lv_color32_t src_argb;
|
||||||
|
lv_color_mix_alpha_cache_t cache;
|
||||||
|
lv_color_mix_with_alpha_cache_init(&cache);
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (dest_x = 0, src_x = 0; src_x < w; dest_x++, src_x++) {
|
||||||
|
src_argb.red = src_buf_al88[src_x].lumi;
|
||||||
|
src_argb.green = src_buf_al88[src_x].lumi;
|
||||||
|
src_argb.blue = src_buf_al88[src_x].lumi;
|
||||||
|
if (mask_buf == NULL) {
|
||||||
|
src_argb.alpha = LV_OPA_MIX2(src_buf_al88[src_x].alpha, opa);
|
||||||
|
} else {
|
||||||
|
src_argb.alpha = LV_OPA_MIX3(src_buf_al88[src_x].alpha, mask_buf[dest_x], opa);
|
||||||
|
}
|
||||||
|
blend_non_normal_pixel(&dest_buf_c32[dest_x], src_argb, dsc->blend_mode, &cache);
|
||||||
|
}
|
||||||
|
if (mask_buf) {
|
||||||
|
mask_buf += mask_stride;
|
||||||
|
}
|
||||||
|
dest_buf_c32 = drawbuf_next_row(dest_buf_c32, dest_stride);
|
||||||
|
src_buf_al88 = drawbuf_next_row(src_buf_al88, src_stride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void LV_ATTRIBUTE_FAST_MEM l8_image_blend(_lv_draw_sw_blend_image_dsc_t *dsc)
|
||||||
|
{
|
||||||
|
int32_t w = dsc->dest_w;
|
||||||
|
int32_t h = dsc->dest_h;
|
||||||
|
lv_opa_t opa = dsc->opa;
|
||||||
|
lv_color32_t *dest_buf_c32 = dsc->dest_buf;
|
||||||
|
int32_t dest_stride = dsc->dest_stride;
|
||||||
|
const uint8_t *src_buf_l8 = dsc->src_buf;
|
||||||
|
int32_t src_stride = dsc->src_stride;
|
||||||
|
const lv_opa_t *mask_buf = dsc->mask_buf;
|
||||||
|
int32_t mask_stride = dsc->mask_stride;
|
||||||
|
|
||||||
|
int32_t dest_x;
|
||||||
|
int32_t src_x;
|
||||||
|
int32_t y;
|
||||||
|
|
||||||
|
if (dsc->blend_mode == LV_BLEND_MODE_NORMAL) {
|
||||||
|
if (mask_buf == NULL && opa >= LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_L8_BLEND_NORMAL_TO_ARGB8888(dsc)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (dest_x = 0, src_x = 0; src_x < w; dest_x++, src_x++) {
|
||||||
|
dest_buf_c32[dest_x].alpha = src_buf_l8[src_x];
|
||||||
|
dest_buf_c32[dest_x].red = src_buf_l8[src_x];
|
||||||
|
dest_buf_c32[dest_x].green = src_buf_l8[src_x];
|
||||||
|
dest_buf_c32[dest_x].blue = src_buf_l8[src_x];
|
||||||
|
}
|
||||||
|
dest_buf_c32 = drawbuf_next_row(dest_buf_c32, dest_stride);
|
||||||
|
src_buf_l8 = drawbuf_next_row(src_buf_l8, src_stride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (mask_buf == NULL && opa < LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_L8_BLEND_NORMAL_TO_ARGB8888_WITH_OPA(dsc)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (dest_x = 0, src_x = 0; src_x < w; dest_x++, src_x++) {
|
||||||
|
lv_color_8_32_mix(src_buf_l8[src_x], &dest_buf_c32[dest_x], opa);
|
||||||
|
}
|
||||||
|
dest_buf_c32 = drawbuf_next_row(dest_buf_c32, dest_stride);
|
||||||
|
src_buf_l8 = drawbuf_next_row(src_buf_l8, src_stride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (mask_buf && opa >= LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_L8_BLEND_NORMAL_TO_ARGB8888_WITH_MASK(dsc)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (dest_x = 0, src_x = 0; src_x < w; dest_x++, src_x++) {
|
||||||
|
lv_color_8_32_mix(src_buf_l8[src_x], &dest_buf_c32[dest_x], mask_buf[src_x]);
|
||||||
|
}
|
||||||
|
dest_buf_c32 = drawbuf_next_row(dest_buf_c32, dest_stride);
|
||||||
|
src_buf_l8 = drawbuf_next_row(src_buf_l8, src_stride);
|
||||||
|
mask_buf += mask_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (mask_buf && opa < LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_L8_BLEND_NORMAL_TO_ARGB8888_MIX_MASK_OPA(dsc)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (dest_x = 0, src_x = 0; src_x < w; dest_x++, src_x++) {
|
||||||
|
lv_color_8_32_mix(src_buf_l8[src_x], &dest_buf_c32[dest_x], LV_OPA_MIX2(mask_buf[src_x], opa));
|
||||||
|
}
|
||||||
|
dest_buf_c32 = drawbuf_next_row(dest_buf_c32, dest_stride);
|
||||||
|
src_buf_l8 = drawbuf_next_row(src_buf_l8, src_stride);
|
||||||
|
mask_buf += mask_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lv_color32_t src_argb;
|
||||||
|
lv_color_mix_alpha_cache_t cache;
|
||||||
|
lv_color_mix_with_alpha_cache_init(&cache);
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (dest_x = 0, src_x = 0; src_x < w; dest_x++, src_x++) {
|
||||||
|
src_argb.red = src_buf_l8[src_x];
|
||||||
|
src_argb.green = src_buf_l8[src_x];
|
||||||
|
src_argb.blue = src_buf_l8[src_x];
|
||||||
|
if (mask_buf == NULL) {
|
||||||
|
src_argb.alpha = opa;
|
||||||
|
} else {
|
||||||
|
src_argb.alpha = LV_OPA_MIX2(mask_buf[dest_x], opa);
|
||||||
|
}
|
||||||
|
blend_non_normal_pixel(&dest_buf_c32[dest_x], src_argb, dsc->blend_mode, &cache);
|
||||||
|
}
|
||||||
|
if (mask_buf) {
|
||||||
|
mask_buf += mask_stride;
|
||||||
|
}
|
||||||
|
dest_buf_c32 = drawbuf_next_row(dest_buf_c32, dest_stride);
|
||||||
|
src_buf_l8 = drawbuf_next_row(src_buf_l8, src_stride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void LV_ATTRIBUTE_FAST_MEM rgb565_image_blend(_lv_draw_sw_blend_image_dsc_t *dsc)
|
||||||
|
{
|
||||||
|
int32_t w = dsc->dest_w;
|
||||||
|
int32_t h = dsc->dest_h;
|
||||||
|
lv_opa_t opa = dsc->opa;
|
||||||
|
lv_color32_t *dest_buf_c32 = dsc->dest_buf;
|
||||||
|
int32_t dest_stride = dsc->dest_stride;
|
||||||
|
const lv_color16_t *src_buf_c16 = (const lv_color16_t *) dsc->src_buf;
|
||||||
|
int32_t src_stride = dsc->src_stride;
|
||||||
|
const lv_opa_t *mask_buf = dsc->mask_buf;
|
||||||
|
int32_t mask_stride = dsc->mask_stride;
|
||||||
|
|
||||||
|
lv_color32_t color_argb;
|
||||||
|
lv_color_mix_alpha_cache_t cache;
|
||||||
|
lv_color_mix_with_alpha_cache_init(&cache);
|
||||||
|
|
||||||
|
int32_t x;
|
||||||
|
int32_t y;
|
||||||
|
|
||||||
|
LV_UNUSED(color_argb);
|
||||||
|
|
||||||
|
if (dsc->blend_mode == LV_BLEND_MODE_NORMAL) {
|
||||||
|
if (mask_buf == NULL) {
|
||||||
|
lv_result_t accelerated;
|
||||||
|
if (opa >= LV_OPA_MAX) {
|
||||||
|
accelerated = LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_ARGB8888(dsc);
|
||||||
|
} else {
|
||||||
|
accelerated = LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_ARGB8888_WITH_OPA(dsc);
|
||||||
|
}
|
||||||
|
if (LV_RESULT_INVALID == accelerated) {
|
||||||
|
color_argb.alpha = opa;
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (x = 0; x < w; x++) {
|
||||||
|
color_argb.red = (src_buf_c16[x].red * 2106) >> 8; /*To make it rounded*/
|
||||||
|
color_argb.green = (src_buf_c16[x].green * 1037) >> 8;
|
||||||
|
color_argb.blue = (src_buf_c16[x].blue * 2106) >> 8;
|
||||||
|
dest_buf_c32[x] = lv_color_32_32_mix(color_argb, dest_buf_c32[x], &cache);
|
||||||
|
}
|
||||||
|
dest_buf_c32 = drawbuf_next_row(dest_buf_c32, dest_stride);
|
||||||
|
src_buf_c16 = drawbuf_next_row(src_buf_c16, src_stride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (mask_buf && opa >= LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_ARGB8888_WITH_MASK(dsc)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (x = 0; x < w; x++) {
|
||||||
|
color_argb.alpha = mask_buf[x];
|
||||||
|
color_argb.red = (src_buf_c16[x].red * 2106) >> 8; /*To make it rounded*/
|
||||||
|
color_argb.green = (src_buf_c16[x].green * 1037) >> 8;
|
||||||
|
color_argb.blue = (src_buf_c16[x].blue * 2106) >> 8;
|
||||||
|
dest_buf_c32[x] = lv_color_32_32_mix(color_argb, dest_buf_c32[x], &cache);
|
||||||
|
}
|
||||||
|
dest_buf_c32 = drawbuf_next_row(dest_buf_c32, dest_stride);
|
||||||
|
src_buf_c16 = drawbuf_next_row(src_buf_c16, src_stride);
|
||||||
|
mask_buf += mask_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_ARGB8888_MIX_MASK_OPA(dsc)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (x = 0; x < w; x++) {
|
||||||
|
color_argb.alpha = LV_OPA_MIX2(mask_buf[x], opa);
|
||||||
|
color_argb.red = (src_buf_c16[x].red * 2106) >> 8; /*To make it rounded*/
|
||||||
|
color_argb.green = (src_buf_c16[x].green * 1037) >> 8;
|
||||||
|
color_argb.blue = (src_buf_c16[x].blue * 2106) >> 8;
|
||||||
|
dest_buf_c32[x] = lv_color_32_32_mix(color_argb, dest_buf_c32[x], &cache);
|
||||||
|
}
|
||||||
|
dest_buf_c32 = drawbuf_next_row(dest_buf_c32, dest_stride);
|
||||||
|
src_buf_c16 = drawbuf_next_row(src_buf_c16, src_stride);
|
||||||
|
mask_buf += mask_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lv_color32_t src_argb;
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (x = 0; x < w; x++) {
|
||||||
|
src_argb.red = (src_buf_c16[x].red * 2106) >> 8;
|
||||||
|
src_argb.green = (src_buf_c16[x].green * 1037) >> 8;
|
||||||
|
src_argb.blue = (src_buf_c16[x].blue * 2106) >> 8;
|
||||||
|
if (mask_buf == NULL) {
|
||||||
|
src_argb.alpha = opa;
|
||||||
|
} else {
|
||||||
|
src_argb.alpha = LV_OPA_MIX2(mask_buf[x], opa);
|
||||||
|
}
|
||||||
|
blend_non_normal_pixel(&dest_buf_c32[x], src_argb, dsc->blend_mode, &cache);
|
||||||
|
}
|
||||||
|
if (mask_buf) {
|
||||||
|
mask_buf += mask_stride;
|
||||||
|
}
|
||||||
|
dest_buf_c32 = drawbuf_next_row(dest_buf_c32, dest_stride);
|
||||||
|
src_buf_c16 = drawbuf_next_row(src_buf_c16, src_stride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void LV_ATTRIBUTE_FAST_MEM rgb888_image_blend(_lv_draw_sw_blend_image_dsc_t *dsc, const uint8_t src_px_size)
|
||||||
|
{
|
||||||
|
|
||||||
|
int32_t w = dsc->dest_w;
|
||||||
|
int32_t h = dsc->dest_h;
|
||||||
|
lv_opa_t opa = dsc->opa;
|
||||||
|
lv_color32_t *dest_buf_c32 = dsc->dest_buf;
|
||||||
|
int32_t dest_stride = dsc->dest_stride;
|
||||||
|
const uint8_t *src_buf = dsc->src_buf;
|
||||||
|
int32_t src_stride = dsc->src_stride;
|
||||||
|
const lv_opa_t *mask_buf = dsc->mask_buf;
|
||||||
|
int32_t mask_stride = dsc->mask_stride;
|
||||||
|
|
||||||
|
lv_color32_t color_argb;
|
||||||
|
lv_color_mix_alpha_cache_t cache;
|
||||||
|
lv_color_mix_with_alpha_cache_init(&cache);
|
||||||
|
|
||||||
|
int32_t dest_x;
|
||||||
|
int32_t src_x;
|
||||||
|
int32_t y;
|
||||||
|
|
||||||
|
LV_UNUSED(color_argb);
|
||||||
|
|
||||||
|
if (dsc->blend_mode == LV_BLEND_MODE_NORMAL) {
|
||||||
|
/*Special case*/
|
||||||
|
if (mask_buf == NULL && opa >= LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_ARGB8888(dsc, src_px_size)) {
|
||||||
|
if (src_px_size == 4) {
|
||||||
|
uint32_t line_in_bytes = w * 4;
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
memcpy(dest_buf_c32, src_buf, line_in_bytes); // lv_memcpy
|
||||||
|
dest_buf_c32 = drawbuf_next_row(dest_buf_c32, dest_stride);
|
||||||
|
src_buf = drawbuf_next_row(src_buf, src_stride);
|
||||||
|
}
|
||||||
|
} else if (src_px_size == 3) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (dest_x = 0, src_x = 0; dest_x < w; dest_x++, src_x += 3) {
|
||||||
|
dest_buf_c32[dest_x].red = src_buf[src_x + 2];
|
||||||
|
dest_buf_c32[dest_x].green = src_buf[src_x + 1];
|
||||||
|
dest_buf_c32[dest_x].blue = src_buf[src_x + 0];
|
||||||
|
dest_buf_c32[dest_x].alpha = 0xff;
|
||||||
|
}
|
||||||
|
dest_buf_c32 = drawbuf_next_row(dest_buf_c32, dest_stride);
|
||||||
|
src_buf = drawbuf_next_row(src_buf, src_stride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (mask_buf == NULL && opa < LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_ARGB8888_WITH_OPA(dsc, src_px_size)) {
|
||||||
|
color_argb.alpha = opa;
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (dest_x = 0, src_x = 0; dest_x < w; dest_x++, src_x += src_px_size) {
|
||||||
|
color_argb.red = src_buf[src_x + 2];
|
||||||
|
color_argb.green = src_buf[src_x + 1];
|
||||||
|
color_argb.blue = src_buf[src_x + 0];
|
||||||
|
dest_buf_c32[dest_x] = lv_color_32_32_mix(color_argb, dest_buf_c32[dest_x], &cache);
|
||||||
|
}
|
||||||
|
dest_buf_c32 = drawbuf_next_row(dest_buf_c32, dest_stride);
|
||||||
|
src_buf = drawbuf_next_row(src_buf, src_stride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
if (mask_buf && opa >= LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_ARGB8888_WITH_MASK(dsc, src_px_size)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (dest_x = 0, src_x = 0; dest_x < w; dest_x++, src_x += src_px_size) {
|
||||||
|
color_argb.alpha = mask_buf[dest_x];
|
||||||
|
color_argb.red = src_buf[src_x + 2];
|
||||||
|
color_argb.green = src_buf[src_x + 1];
|
||||||
|
color_argb.blue = src_buf[src_x + 0];
|
||||||
|
dest_buf_c32[dest_x] = lv_color_32_32_mix(color_argb, dest_buf_c32[dest_x], &cache);
|
||||||
|
}
|
||||||
|
dest_buf_c32 = drawbuf_next_row(dest_buf_c32, dest_stride);
|
||||||
|
src_buf = drawbuf_next_row(src_buf, src_stride);
|
||||||
|
mask_buf += mask_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mask_buf && opa < LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_ARGB8888_MIX_MASK_OPA(dsc, src_px_size)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (dest_x = 0, src_x = 0; dest_x < w; dest_x++, src_x += src_px_size) {
|
||||||
|
color_argb.alpha = (opa * mask_buf[dest_x]) >> 8;
|
||||||
|
color_argb.red = src_buf[src_x + 2];
|
||||||
|
color_argb.green = src_buf[src_x + 1];
|
||||||
|
color_argb.blue = src_buf[src_x + 0];
|
||||||
|
dest_buf_c32[dest_x] = lv_color_32_32_mix(color_argb, dest_buf_c32[dest_x], &cache);
|
||||||
|
}
|
||||||
|
dest_buf_c32 = drawbuf_next_row(dest_buf_c32, dest_stride);
|
||||||
|
src_buf = drawbuf_next_row(src_buf, src_stride);
|
||||||
|
mask_buf += mask_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lv_color32_t src_argb;
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (dest_x = 0, src_x = 0; dest_x < w; dest_x++, src_x += src_px_size) {
|
||||||
|
src_argb.red = src_buf[src_x + 2];
|
||||||
|
src_argb.green = src_buf[src_x + 1];
|
||||||
|
src_argb.blue = src_buf[src_x + 0];
|
||||||
|
if (mask_buf == NULL) {
|
||||||
|
src_argb.alpha = opa;
|
||||||
|
} else {
|
||||||
|
src_argb.alpha = LV_OPA_MIX2(mask_buf[dest_x], opa);
|
||||||
|
}
|
||||||
|
|
||||||
|
blend_non_normal_pixel(&dest_buf_c32[dest_x], src_argb, dsc->blend_mode, &cache);
|
||||||
|
}
|
||||||
|
if (mask_buf) {
|
||||||
|
mask_buf += mask_stride;
|
||||||
|
}
|
||||||
|
dest_buf_c32 = drawbuf_next_row(dest_buf_c32, dest_stride);
|
||||||
|
src_buf = drawbuf_next_row(src_buf, src_stride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void LV_ATTRIBUTE_FAST_MEM argb8888_image_blend(_lv_draw_sw_blend_image_dsc_t *dsc)
|
||||||
|
{
|
||||||
|
int32_t w = dsc->dest_w;
|
||||||
|
int32_t h = dsc->dest_h;
|
||||||
|
lv_opa_t opa = dsc->opa;
|
||||||
|
lv_color32_t *dest_buf_c32 = dsc->dest_buf;
|
||||||
|
int32_t dest_stride = dsc->dest_stride;
|
||||||
|
const lv_color32_t *src_buf_c32 = dsc->src_buf;
|
||||||
|
int32_t src_stride = dsc->src_stride;
|
||||||
|
const lv_opa_t *mask_buf = dsc->mask_buf;
|
||||||
|
int32_t mask_stride = dsc->mask_stride;
|
||||||
|
|
||||||
|
lv_color32_t color_argb;
|
||||||
|
lv_color_mix_alpha_cache_t cache;
|
||||||
|
lv_color_mix_with_alpha_cache_init(&cache);
|
||||||
|
|
||||||
|
int32_t x;
|
||||||
|
int32_t y;
|
||||||
|
|
||||||
|
if (dsc->blend_mode == LV_BLEND_MODE_NORMAL) {
|
||||||
|
if (mask_buf == NULL && opa >= LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_ARGB8888(dsc)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (x = 0; x < w; x++) {
|
||||||
|
dest_buf_c32[x] = lv_color_32_32_mix(src_buf_c32[x], dest_buf_c32[x], &cache);
|
||||||
|
}
|
||||||
|
dest_buf_c32 = drawbuf_next_row(dest_buf_c32, dest_stride);
|
||||||
|
src_buf_c32 = drawbuf_next_row(src_buf_c32, src_stride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (mask_buf == NULL && opa < LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_ARGB8888_WITH_OPA(dsc)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (x = 0; x < w; x++) {
|
||||||
|
color_argb = src_buf_c32[x];
|
||||||
|
color_argb.alpha = LV_OPA_MIX2(color_argb.alpha, opa);
|
||||||
|
dest_buf_c32[x] = lv_color_32_32_mix(color_argb, dest_buf_c32[x], &cache);
|
||||||
|
}
|
||||||
|
dest_buf_c32 = drawbuf_next_row(dest_buf_c32, dest_stride);
|
||||||
|
src_buf_c32 = drawbuf_next_row(src_buf_c32, src_stride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (mask_buf && opa >= LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_ARGB8888_WITH_MASK(dsc)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (x = 0; x < w; x++) {
|
||||||
|
color_argb = src_buf_c32[x];
|
||||||
|
color_argb.alpha = LV_OPA_MIX2(color_argb.alpha, mask_buf[x]);
|
||||||
|
dest_buf_c32[x] = lv_color_32_32_mix(color_argb, dest_buf_c32[x], &cache);
|
||||||
|
}
|
||||||
|
dest_buf_c32 = drawbuf_next_row(dest_buf_c32, dest_stride);
|
||||||
|
src_buf_c32 = drawbuf_next_row(src_buf_c32, src_stride);
|
||||||
|
mask_buf += mask_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (mask_buf && opa < LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_ARGB8888_MIX_MASK_OPA(dsc)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (x = 0; x < w; x++) {
|
||||||
|
color_argb = src_buf_c32[x];
|
||||||
|
color_argb.alpha = LV_OPA_MIX3(color_argb.alpha, opa, mask_buf[x]);
|
||||||
|
dest_buf_c32[x] = lv_color_32_32_mix(color_argb, dest_buf_c32[x], &cache);
|
||||||
|
}
|
||||||
|
dest_buf_c32 = drawbuf_next_row(dest_buf_c32, dest_stride);
|
||||||
|
src_buf_c32 = drawbuf_next_row(src_buf_c32, src_stride);
|
||||||
|
mask_buf += mask_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (x = 0; x < w; x++) {
|
||||||
|
color_argb = src_buf_c32[x];
|
||||||
|
if (mask_buf == NULL) {
|
||||||
|
color_argb.alpha = LV_OPA_MIX2(color_argb.alpha, opa);
|
||||||
|
} else {
|
||||||
|
color_argb.alpha = LV_OPA_MIX3(color_argb.alpha, mask_buf[x], opa);
|
||||||
|
}
|
||||||
|
blend_non_normal_pixel(&dest_buf_c32[x], color_argb, dsc->blend_mode, &cache);
|
||||||
|
}
|
||||||
|
if (mask_buf) {
|
||||||
|
mask_buf += mask_stride;
|
||||||
|
}
|
||||||
|
dest_buf_c32 = drawbuf_next_row(dest_buf_c32, dest_stride);
|
||||||
|
src_buf_c32 = drawbuf_next_row(src_buf_c32, src_stride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void LV_ATTRIBUTE_FAST_MEM lv_color_8_32_mix(const uint8_t src, lv_color32_t *dest, uint8_t mix)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (mix == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dest->alpha = 255;
|
||||||
|
if (mix >= LV_OPA_MAX) {
|
||||||
|
dest->red = src;
|
||||||
|
dest->green = src;
|
||||||
|
dest->blue = src;
|
||||||
|
} else {
|
||||||
|
lv_opa_t mix_inv = 255 - mix;
|
||||||
|
dest->red = (uint32_t)((uint32_t)src * mix + dest->red * mix_inv) >> 8;
|
||||||
|
dest->green = (uint32_t)((uint32_t)src * mix + dest->green * mix_inv) >> 8;
|
||||||
|
dest->blue = (uint32_t)((uint32_t)src * mix + dest->blue * mix_inv) >> 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline lv_color32_t LV_ATTRIBUTE_FAST_MEM lv_color_32_32_mix(lv_color32_t fg, lv_color32_t bg,
|
||||||
|
lv_color_mix_alpha_cache_t *cache)
|
||||||
|
{
|
||||||
|
/*Pick the foreground if it's fully opaque or the Background is fully transparent*/
|
||||||
|
if (fg.alpha >= LV_OPA_MAX || bg.alpha <= LV_OPA_MIN) {
|
||||||
|
return fg;
|
||||||
|
}
|
||||||
|
/*Transparent foreground: use the Background*/
|
||||||
|
else if (fg.alpha <= LV_OPA_MIN) {
|
||||||
|
return bg;
|
||||||
|
}
|
||||||
|
/*Opaque background: use simple mix*/
|
||||||
|
else if (bg.alpha == 255) {
|
||||||
|
return lv_color_mix32(fg, bg);
|
||||||
|
}
|
||||||
|
/*Both colors have alpha. Expensive calculation need to be applied*/
|
||||||
|
else {
|
||||||
|
/*Save the parameters and the result. If they will be asked again don't compute again*/
|
||||||
|
|
||||||
|
/*Update the ratio and the result alpha value if the input alpha values change*/
|
||||||
|
if (bg.alpha != cache->bg_saved.alpha || fg.alpha != cache->fg_saved.alpha) {
|
||||||
|
/*Info:
|
||||||
|
* https://en.wikipedia.org/wiki/Alpha_compositing#Analytical_derivation_of_the_over_operator*/
|
||||||
|
cache->res_alpha_saved = 255 - LV_OPA_MIX2(255 - fg.alpha, 255 - bg.alpha);
|
||||||
|
LV_ASSERT(cache->res_alpha_saved != 0);
|
||||||
|
cache->ratio_saved = (uint32_t)((uint32_t)fg.alpha * 255) / cache->res_alpha_saved;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!lv_color32_eq(bg, cache->bg_saved) || !lv_color32_eq(fg, cache->fg_saved)) {
|
||||||
|
cache->fg_saved = fg;
|
||||||
|
cache->bg_saved = bg;
|
||||||
|
fg.alpha = cache->ratio_saved;
|
||||||
|
cache->res_saved = lv_color_mix32(fg, bg);
|
||||||
|
cache->res_saved.alpha = cache->res_alpha_saved;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cache->res_saved;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void lv_color_mix_with_alpha_cache_init(lv_color_mix_alpha_cache_t *cache)
|
||||||
|
{
|
||||||
|
memset(&cache->fg_saved, 0x00, sizeof(lv_color32_t)); //lv_memzero
|
||||||
|
memset(&cache->bg_saved, 0x00, sizeof(lv_color32_t)); //lv_memzero
|
||||||
|
memset(&cache->res_saved, 0x00, sizeof(lv_color32_t)); //lv_memzero
|
||||||
|
cache->res_alpha_saved = 255;
|
||||||
|
cache->ratio_saved = 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void LV_ATTRIBUTE_FAST_MEM blend_non_normal_pixel(lv_color32_t *dest, lv_color32_t src,
|
||||||
|
lv_blend_mode_t mode, lv_color_mix_alpha_cache_t *cache)
|
||||||
|
{
|
||||||
|
lv_color32_t res;
|
||||||
|
switch (mode) {
|
||||||
|
case LV_BLEND_MODE_ADDITIVE:
|
||||||
|
res.red = LV_MIN(dest->red + src.red, 255);
|
||||||
|
res.green = LV_MIN(dest->green + src.green, 255);
|
||||||
|
res.blue = LV_MIN(dest->blue + src.blue, 255);
|
||||||
|
break;
|
||||||
|
case LV_BLEND_MODE_SUBTRACTIVE:
|
||||||
|
res.red = LV_MAX(dest->red - src.red, 0);
|
||||||
|
res.green = LV_MAX(dest->green - src.green, 0);
|
||||||
|
res.blue = LV_MAX(dest->blue - src.blue, 0);
|
||||||
|
break;
|
||||||
|
case LV_BLEND_MODE_MULTIPLY:
|
||||||
|
res.red = (dest->red * src.red) >> 8;
|
||||||
|
res.green = (dest->green * src.green) >> 8;
|
||||||
|
res.blue = (dest->blue * src.blue) >> 8;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LV_LOG_WARN("Not supported blend mode: %d", mode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
res.alpha = src.alpha;
|
||||||
|
*dest = lv_color_32_32_mix(res, *dest, cache);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void *LV_ATTRIBUTE_FAST_MEM drawbuf_next_row(const void *buf, uint32_t stride)
|
||||||
|
{
|
||||||
|
return (void *)((uint8_t *)buf + stride);
|
||||||
|
}
|
||||||
@ -0,0 +1,960 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*
|
||||||
|
* This file is derived from the LVGL project.
|
||||||
|
* See https://github.com/lvgl/lvgl for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @file lv_draw_sw_blend_to_rgb565.c
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* INCLUDES
|
||||||
|
*********************/
|
||||||
|
#include "lv_draw_sw_blend_to_rgb565.h"
|
||||||
|
|
||||||
|
#include "lv_assert.h"
|
||||||
|
#include "lv_types.h"
|
||||||
|
#include "lv_log.h"
|
||||||
|
#include "lv_draw_sw_blend.h"
|
||||||
|
#include "lv_math.h"
|
||||||
|
#include "lv_color.h"
|
||||||
|
#include "string.h"
|
||||||
|
|
||||||
|
#include "esp_lvgl_port_lv_blend.h"
|
||||||
|
|
||||||
|
|
||||||
|
/*********************
|
||||||
|
* DEFINES
|
||||||
|
*********************/
|
||||||
|
|
||||||
|
#define LV_ATTRIBUTE_FAST_MEM
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* TYPEDEFS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* STATIC PROTOTYPES
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
static void /* LV_ATTRIBUTE_FAST_MEM */ al88_image_blend(_lv_draw_sw_blend_image_dsc_t *dsc);
|
||||||
|
|
||||||
|
static void /* LV_ATTRIBUTE_FAST_MEM */ l8_image_blend(_lv_draw_sw_blend_image_dsc_t *dsc);
|
||||||
|
|
||||||
|
static void /* LV_ATTRIBUTE_FAST_MEM */ rgb565_image_blend(_lv_draw_sw_blend_image_dsc_t *dsc);
|
||||||
|
|
||||||
|
static void /* LV_ATTRIBUTE_FAST_MEM */ rgb888_image_blend(_lv_draw_sw_blend_image_dsc_t *dsc,
|
||||||
|
const uint8_t src_px_size);
|
||||||
|
|
||||||
|
static void /* LV_ATTRIBUTE_FAST_MEM */ argb8888_image_blend(_lv_draw_sw_blend_image_dsc_t *dsc);
|
||||||
|
|
||||||
|
static inline uint16_t /* LV_ATTRIBUTE_FAST_MEM */ l8_to_rgb565(const uint8_t c1);
|
||||||
|
|
||||||
|
static inline uint16_t /* LV_ATTRIBUTE_FAST_MEM */ lv_color_8_16_mix(const uint8_t c1, uint16_t c2, uint8_t mix);
|
||||||
|
|
||||||
|
static inline uint16_t /* LV_ATTRIBUTE_FAST_MEM */ lv_color_24_16_mix(const uint8_t *c1, uint16_t c2, uint8_t mix);
|
||||||
|
|
||||||
|
static inline void * /* LV_ATTRIBUTE_FAST_MEM */ drawbuf_next_row(const void *buf, uint32_t stride);
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* STATIC VARIABLES
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* MACROS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_COLOR_BLEND_TO_RGB565
|
||||||
|
#define LV_DRAW_SW_COLOR_BLEND_TO_RGB565(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_COLOR_BLEND_TO_RGB565_WITH_OPA
|
||||||
|
#define LV_DRAW_SW_COLOR_BLEND_TO_RGB565_WITH_OPA(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_COLOR_BLEND_TO_RGB565_WITH_MASK
|
||||||
|
#define LV_DRAW_SW_COLOR_BLEND_TO_RGB565_WITH_MASK(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_COLOR_BLEND_TO_RGB565_MIX_MASK_OPA
|
||||||
|
#define LV_DRAW_SW_COLOR_BLEND_TO_RGB565_MIX_MASK_OPA(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_L8_BLEND_NORMAL_TO_RGB565
|
||||||
|
#define LV_DRAW_SW_L8_BLEND_NORMAL_TO_RGB565(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_L8_BLEND_NORMAL_TO_RGB565_WITH_OPA
|
||||||
|
#define LV_DRAW_SW_L8_BLEND_NORMAL_TO_RGB565_WITH_OPA(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_L8_BLEND_NORMAL_TO_RGB565_WITH_MASK
|
||||||
|
#define LV_DRAW_SW_L8_BLEND_NORMAL_TO_RGB565_WITH_MASK(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_L8_BLEND_NORMAL_TO_RGB565_MIX_MASK_OPA
|
||||||
|
#define LV_DRAW_SW_L8_BLEND_NORMAL_TO_RGB565_MIX_MASK_OPA(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_AL88_BLEND_NORMAL_TO_RGB565
|
||||||
|
#define LV_DRAW_SW_AL88_BLEND_NORMAL_TO_RGB565(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_AL88_BLEND_NORMAL_TO_RGB565_WITH_OPA
|
||||||
|
#define LV_DRAW_SW_AL88_BLEND_NORMAL_TO_RGB565_WITH_OPA(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_AL88_BLEND_NORMAL_TO_RGB565_WITH_MASK
|
||||||
|
#define LV_DRAW_SW_AL88_BLEND_NORMAL_TO_RGB565_WITH_MASK(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_AL88_BLEND_NORMAL_TO_RGB565_MIX_MASK_OPA
|
||||||
|
#define LV_DRAW_SW_AL88_BLEND_NORMAL_TO_RGB565_MIX_MASK_OPA(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_RGB565
|
||||||
|
#define LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_RGB565(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_RGB565_WITH_OPA
|
||||||
|
#define LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_RGB565_WITH_OPA(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_RGB565_WITH_MASK
|
||||||
|
#define LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_RGB565_WITH_MASK(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_RGB565_MIX_MASK_OPA
|
||||||
|
#define LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_RGB565_MIX_MASK_OPA(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_RGB565
|
||||||
|
#define LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_RGB565(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_RGB565_WITH_OPA
|
||||||
|
#define LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_RGB565_WITH_OPA(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_RGB565_WITH_MASK
|
||||||
|
#define LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_RGB565_WITH_MASK(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_RGB565_MIX_MASK_OPA
|
||||||
|
#define LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_RGB565_MIX_MASK_OPA(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_RGB565
|
||||||
|
#define LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_RGB565(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_RGB565_WITH_OPA
|
||||||
|
#define LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_RGB565_WITH_OPA(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_RGB565_WITH_MASK
|
||||||
|
#define LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_RGB565_WITH_MASK(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_RGB565_MIX_MASK_OPA
|
||||||
|
#define LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_RGB565_MIX_MASK_OPA(...) LV_RESULT_INVALID
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* GLOBAL FUNCTIONS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fill an area with a color.
|
||||||
|
* Supports normal fill, fill with opacity, fill with mask, and fill with mask and opacity.
|
||||||
|
* dest_buf and color have native color depth. (RGB565, RGB888, XRGB8888)
|
||||||
|
* The background (dest_buf) cannot have alpha channel
|
||||||
|
* @param dest_buf
|
||||||
|
* @param dest_area
|
||||||
|
* @param dest_stride
|
||||||
|
* @param color
|
||||||
|
* @param opa
|
||||||
|
* @param mask
|
||||||
|
* @param mask_stride
|
||||||
|
*/
|
||||||
|
void LV_ATTRIBUTE_FAST_MEM lv_draw_sw_blend_color_to_rgb565(_lv_draw_sw_blend_fill_dsc_t *dsc)
|
||||||
|
{
|
||||||
|
int32_t w = dsc->dest_w;
|
||||||
|
int32_t h = dsc->dest_h;
|
||||||
|
uint16_t color16 = lv_color_to_u16(dsc->color);
|
||||||
|
lv_opa_t opa = dsc->opa;
|
||||||
|
const lv_opa_t *mask = dsc->mask_buf;
|
||||||
|
int32_t mask_stride = dsc->mask_stride;
|
||||||
|
uint16_t *dest_buf_u16 = dsc->dest_buf;
|
||||||
|
int32_t dest_stride = dsc->dest_stride;
|
||||||
|
|
||||||
|
int32_t x;
|
||||||
|
int32_t y;
|
||||||
|
|
||||||
|
LV_UNUSED(w);
|
||||||
|
LV_UNUSED(h);
|
||||||
|
LV_UNUSED(x);
|
||||||
|
LV_UNUSED(y);
|
||||||
|
LV_UNUSED(opa);
|
||||||
|
LV_UNUSED(mask);
|
||||||
|
LV_UNUSED(color16);
|
||||||
|
LV_UNUSED(mask_stride);
|
||||||
|
LV_UNUSED(dest_stride);
|
||||||
|
LV_UNUSED(dest_buf_u16);
|
||||||
|
|
||||||
|
/*Simple fill*/
|
||||||
|
if (mask == NULL && opa >= LV_OPA_MAX) {
|
||||||
|
if (dsc->use_asm) {
|
||||||
|
LV_DRAW_SW_COLOR_BLEND_TO_RGB565(dsc);
|
||||||
|
} else {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
uint16_t *dest_end_final = dest_buf_u16 + w;
|
||||||
|
uint32_t *dest_end_mid = (uint32_t *)((uint16_t *) dest_buf_u16 + ((w - 1) & ~(0xF)));
|
||||||
|
if ((lv_uintptr_t)&dest_buf_u16[0] & 0x3) {
|
||||||
|
dest_buf_u16[0] = color16;
|
||||||
|
dest_buf_u16++;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t c32 = (uint32_t)color16 + ((uint32_t)color16 << 16);
|
||||||
|
uint32_t *dest32 = (uint32_t *)dest_buf_u16;
|
||||||
|
while (dest32 < dest_end_mid) {
|
||||||
|
dest32[0] = c32;
|
||||||
|
dest32[1] = c32;
|
||||||
|
dest32[2] = c32;
|
||||||
|
dest32[3] = c32;
|
||||||
|
dest32[4] = c32;
|
||||||
|
dest32[5] = c32;
|
||||||
|
dest32[6] = c32;
|
||||||
|
dest32[7] = c32;
|
||||||
|
dest32 += 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
dest_buf_u16 = (uint16_t *)dest32;
|
||||||
|
|
||||||
|
while (dest_buf_u16 < dest_end_final) {
|
||||||
|
*dest_buf_u16 = color16;
|
||||||
|
dest_buf_u16++;
|
||||||
|
}
|
||||||
|
|
||||||
|
dest_buf_u16 = drawbuf_next_row(dest_buf_u16, dest_stride);
|
||||||
|
dest_buf_u16 -= w;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
/*Opacity only*/
|
||||||
|
else if (mask == NULL && opa < LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_COLOR_BLEND_TO_RGB565_WITH_OPA(dsc)) {
|
||||||
|
uint32_t last_dest32_color = dest_buf_u16[0] + 1; /*Set to value which is not equal to the first pixel*/
|
||||||
|
uint32_t last_res32_color = 0;
|
||||||
|
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
x = 0;
|
||||||
|
if ((lv_uintptr_t)&dest_buf_u16[0] & 0x3) {
|
||||||
|
dest_buf_u16[0] = lv_color_16_16_mix(color16, dest_buf_u16[0], opa);
|
||||||
|
x = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; x < w - 2; x += 2) {
|
||||||
|
if (dest_buf_u16[x] != dest_buf_u16[x + 1]) {
|
||||||
|
dest_buf_u16[x + 0] = lv_color_16_16_mix(color16, dest_buf_u16[x + 0], opa);
|
||||||
|
dest_buf_u16[x + 1] = lv_color_16_16_mix(color16, dest_buf_u16[x + 1], opa);
|
||||||
|
} else {
|
||||||
|
volatile uint32_t *dest32 = (uint32_t *)&dest_buf_u16[x];
|
||||||
|
if (last_dest32_color == *dest32) {
|
||||||
|
*dest32 = last_res32_color;
|
||||||
|
} else {
|
||||||
|
last_dest32_color = *dest32;
|
||||||
|
|
||||||
|
dest_buf_u16[x] = lv_color_16_16_mix(color16, dest_buf_u16[x + 0], opa);
|
||||||
|
dest_buf_u16[x + 1] = dest_buf_u16[x];
|
||||||
|
|
||||||
|
last_res32_color = *dest32;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; x < w ; x++) {
|
||||||
|
dest_buf_u16[x] = lv_color_16_16_mix(color16, dest_buf_u16[x], opa);
|
||||||
|
}
|
||||||
|
dest_buf_u16 = drawbuf_next_row(dest_buf_u16, dest_stride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/*Masked with full opacity*/
|
||||||
|
else if (mask && opa >= LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_COLOR_BLEND_TO_RGB565_WITH_MASK(dsc)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
x = 0;
|
||||||
|
if ((lv_uintptr_t)(mask) & 0x1) {
|
||||||
|
dest_buf_u16[x] = lv_color_16_16_mix(color16, dest_buf_u16[x], mask[x]);
|
||||||
|
x++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; x <= w - 2; x += 2) {
|
||||||
|
uint16_t mask16 = *((uint16_t *)&mask[x]);
|
||||||
|
if (mask16 == 0xFFFF) {
|
||||||
|
dest_buf_u16[x + 0] = color16;
|
||||||
|
dest_buf_u16[x + 1] = color16;
|
||||||
|
} else if (mask16 != 0) {
|
||||||
|
dest_buf_u16[x + 0] = lv_color_16_16_mix(color16, dest_buf_u16[x + 0], mask[x + 0]);
|
||||||
|
dest_buf_u16[x + 1] = lv_color_16_16_mix(color16, dest_buf_u16[x + 1], mask[x + 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (; x < w ; x++) {
|
||||||
|
dest_buf_u16[x] = lv_color_16_16_mix(color16, dest_buf_u16[x], mask[x]);
|
||||||
|
}
|
||||||
|
dest_buf_u16 = drawbuf_next_row(dest_buf_u16, dest_stride);
|
||||||
|
mask += mask_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
/*Masked with opacity*/
|
||||||
|
else if (mask && opa < LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_COLOR_BLEND_TO_RGB565_MIX_MASK_OPA(dsc)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (x = 0; x < w; x++) {
|
||||||
|
dest_buf_u16[x] = lv_color_16_16_mix(color16, dest_buf_u16[x], LV_OPA_MIX2(mask[x], opa));
|
||||||
|
}
|
||||||
|
dest_buf_u16 = drawbuf_next_row(dest_buf_u16, dest_stride);
|
||||||
|
mask += mask_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LV_ATTRIBUTE_FAST_MEM lv_draw_sw_blend_image_to_rgb565(_lv_draw_sw_blend_image_dsc_t *dsc)
|
||||||
|
{
|
||||||
|
switch (dsc->src_color_format) {
|
||||||
|
case LV_COLOR_FORMAT_RGB565:
|
||||||
|
rgb565_image_blend(dsc);
|
||||||
|
break;
|
||||||
|
case LV_COLOR_FORMAT_RGB888:
|
||||||
|
rgb888_image_blend(dsc, 3);
|
||||||
|
break;
|
||||||
|
case LV_COLOR_FORMAT_XRGB8888:
|
||||||
|
rgb888_image_blend(dsc, 4);
|
||||||
|
break;
|
||||||
|
case LV_COLOR_FORMAT_ARGB8888:
|
||||||
|
argb8888_image_blend(dsc);
|
||||||
|
break;
|
||||||
|
case LV_COLOR_FORMAT_L8:
|
||||||
|
l8_image_blend(dsc);
|
||||||
|
break;
|
||||||
|
case LV_COLOR_FORMAT_AL88:
|
||||||
|
al88_image_blend(dsc);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LV_LOG_WARN("Not supported source color format");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**********************
|
||||||
|
* STATIC FUNCTIONS
|
||||||
|
**********************/
|
||||||
|
|
||||||
|
static void LV_ATTRIBUTE_FAST_MEM al88_image_blend(_lv_draw_sw_blend_image_dsc_t *dsc)
|
||||||
|
{
|
||||||
|
int32_t w = dsc->dest_w;
|
||||||
|
int32_t h = dsc->dest_h;
|
||||||
|
lv_opa_t opa = dsc->opa;
|
||||||
|
uint16_t *dest_buf_u16 = dsc->dest_buf;
|
||||||
|
int32_t dest_stride = dsc->dest_stride;
|
||||||
|
const lv_color16a_t *src_buf_al88 = dsc->src_buf;
|
||||||
|
int32_t src_stride = dsc->src_stride;
|
||||||
|
const lv_opa_t *mask_buf = dsc->mask_buf;
|
||||||
|
int32_t mask_stride = dsc->mask_stride;
|
||||||
|
|
||||||
|
int32_t dest_x;
|
||||||
|
int32_t src_x;
|
||||||
|
int32_t y;
|
||||||
|
|
||||||
|
if (dsc->blend_mode == LV_BLEND_MODE_NORMAL) {
|
||||||
|
if (mask_buf == NULL && opa >= LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_AL88_BLEND_NORMAL_TO_RGB565(dsc)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (dest_x = 0, src_x = 0; dest_x < w; dest_x++, src_x++) {
|
||||||
|
dest_buf_u16[dest_x] = lv_color_8_16_mix(src_buf_al88[src_x].lumi, dest_buf_u16[dest_x], src_buf_al88[src_x].alpha);
|
||||||
|
}
|
||||||
|
dest_buf_u16 = drawbuf_next_row(dest_buf_u16, dest_stride);
|
||||||
|
src_buf_al88 = drawbuf_next_row(src_buf_al88, src_stride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (mask_buf == NULL && opa < LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_AL88_BLEND_NORMAL_TO_RGB565_WITH_OPA(dsc)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (dest_x = 0, src_x = 0; dest_x < w; dest_x++, src_x++) {
|
||||||
|
dest_buf_u16[dest_x] = lv_color_8_16_mix(src_buf_al88[src_x].lumi, dest_buf_u16[dest_x],
|
||||||
|
LV_OPA_MIX2(src_buf_al88[src_x].alpha, opa));
|
||||||
|
}
|
||||||
|
dest_buf_u16 = drawbuf_next_row(dest_buf_u16, dest_stride);
|
||||||
|
src_buf_al88 = drawbuf_next_row(src_buf_al88, src_stride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (mask_buf && opa >= LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_AL88_BLEND_NORMAL_TO_RGB565_WITH_MASK(dsc)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (dest_x = 0, src_x = 0; dest_x < w; dest_x++, src_x++) {
|
||||||
|
dest_buf_u16[dest_x] = lv_color_8_16_mix(src_buf_al88[src_x].lumi, dest_buf_u16[dest_x],
|
||||||
|
LV_OPA_MIX2(src_buf_al88[src_x].alpha, mask_buf[dest_x]));
|
||||||
|
}
|
||||||
|
dest_buf_u16 = drawbuf_next_row(dest_buf_u16, dest_stride);
|
||||||
|
src_buf_al88 = drawbuf_next_row(src_buf_al88, src_stride);
|
||||||
|
mask_buf += mask_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (mask_buf && opa < LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_AL88_BLEND_NORMAL_TO_RGB565_MIX_MASK_OPA(dsc)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (dest_x = 0, src_x = 0; dest_x < w; dest_x++, src_x++) {
|
||||||
|
dest_buf_u16[dest_x] = lv_color_8_16_mix(src_buf_al88[src_x].lumi, dest_buf_u16[dest_x],
|
||||||
|
LV_OPA_MIX3(src_buf_al88[src_x].alpha, mask_buf[dest_x], opa));
|
||||||
|
}
|
||||||
|
dest_buf_u16 = drawbuf_next_row(dest_buf_u16, dest_stride);
|
||||||
|
src_buf_al88 = drawbuf_next_row(src_buf_al88, src_stride);
|
||||||
|
mask_buf += mask_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uint16_t res = 0;
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
lv_color16_t *dest_buf_c16 = (lv_color16_t *)dest_buf_u16;
|
||||||
|
for (dest_x = 0, src_x = 0; dest_x < w; dest_x++, src_x += 4) {
|
||||||
|
uint8_t rb = src_buf_al88[src_x].lumi >> 3;
|
||||||
|
uint8_t g = src_buf_al88[src_x].lumi >> 2;
|
||||||
|
switch (dsc->blend_mode) {
|
||||||
|
case LV_BLEND_MODE_ADDITIVE:
|
||||||
|
res = (LV_MIN(dest_buf_c16[dest_x].red + rb, 31)) << 11;
|
||||||
|
res += (LV_MIN(dest_buf_c16[dest_x].green + g, 63)) << 5;
|
||||||
|
res += LV_MIN(dest_buf_c16[dest_x].blue + rb, 31);
|
||||||
|
break;
|
||||||
|
case LV_BLEND_MODE_SUBTRACTIVE:
|
||||||
|
res = (LV_MAX(dest_buf_c16[dest_x].red - rb, 0)) << 11;
|
||||||
|
res += (LV_MAX(dest_buf_c16[dest_x].green - g, 0)) << 5;
|
||||||
|
res += LV_MAX(dest_buf_c16[dest_x].blue - rb, 0);
|
||||||
|
break;
|
||||||
|
case LV_BLEND_MODE_MULTIPLY:
|
||||||
|
res = ((dest_buf_c16[dest_x].red * rb) >> 5) << 11;
|
||||||
|
res += ((dest_buf_c16[dest_x].green * g) >> 6) << 5;
|
||||||
|
res += (dest_buf_c16[dest_x].blue * rb) >> 5;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LV_LOG_WARN("Not supported blend mode: %d", dsc->blend_mode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (mask_buf == NULL && opa >= LV_OPA_MAX) {
|
||||||
|
dest_buf_u16[dest_x] = lv_color_16_16_mix(res, dest_buf_u16[dest_x], src_buf_al88[src_x].alpha);
|
||||||
|
} else if (mask_buf == NULL && opa < LV_OPA_MAX) {
|
||||||
|
dest_buf_u16[dest_x] = lv_color_16_16_mix(res, dest_buf_u16[dest_x], LV_OPA_MIX2(opa, src_buf_al88[src_x].alpha));
|
||||||
|
} else {
|
||||||
|
if (opa >= LV_OPA_MAX) {
|
||||||
|
dest_buf_u16[dest_x] = lv_color_16_16_mix(res, dest_buf_u16[dest_x], mask_buf[dest_x]);
|
||||||
|
} else dest_buf_u16[dest_x] = lv_color_16_16_mix(res, dest_buf_u16[dest_x], LV_OPA_MIX3(mask_buf[dest_x], opa,
|
||||||
|
src_buf_al88[src_x].alpha));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dest_buf_u16 = drawbuf_next_row(dest_buf_u16, dest_stride);
|
||||||
|
src_buf_al88 = drawbuf_next_row(src_buf_al88, src_stride);
|
||||||
|
if (mask_buf) {
|
||||||
|
mask_buf += mask_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void LV_ATTRIBUTE_FAST_MEM l8_image_blend(_lv_draw_sw_blend_image_dsc_t *dsc)
|
||||||
|
{
|
||||||
|
int32_t w = dsc->dest_w;
|
||||||
|
int32_t h = dsc->dest_h;
|
||||||
|
lv_opa_t opa = dsc->opa;
|
||||||
|
uint16_t *dest_buf_u16 = dsc->dest_buf;
|
||||||
|
int32_t dest_stride = dsc->dest_stride;
|
||||||
|
const uint8_t *src_buf_l8 = dsc->src_buf;
|
||||||
|
int32_t src_stride = dsc->src_stride;
|
||||||
|
const lv_opa_t *mask_buf = dsc->mask_buf;
|
||||||
|
int32_t mask_stride = dsc->mask_stride;
|
||||||
|
|
||||||
|
int32_t dest_x;
|
||||||
|
int32_t src_x;
|
||||||
|
int32_t y;
|
||||||
|
|
||||||
|
if (dsc->blend_mode == LV_BLEND_MODE_NORMAL) {
|
||||||
|
if (mask_buf == NULL && opa >= LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_L8_BLEND_NORMAL_TO_RGB565(dsc)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (dest_x = 0, src_x = 0; dest_x < w; dest_x++, src_x++) {
|
||||||
|
dest_buf_u16[dest_x] = l8_to_rgb565(src_buf_l8[src_x]);
|
||||||
|
}
|
||||||
|
dest_buf_u16 = drawbuf_next_row(dest_buf_u16, dest_stride);
|
||||||
|
src_buf_l8 += src_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (mask_buf == NULL && opa < LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_L8_BLEND_NORMAL_TO_RGB565_WITH_OPA(dsc)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (dest_x = 0, src_x = 0; dest_x < w; dest_x++, src_x++) {
|
||||||
|
dest_buf_u16[dest_x] = lv_color_8_16_mix(src_buf_l8[src_x], dest_buf_u16[dest_x], opa);
|
||||||
|
}
|
||||||
|
dest_buf_u16 = drawbuf_next_row(dest_buf_u16, dest_stride);
|
||||||
|
src_buf_l8 += src_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (mask_buf && opa >= LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_L8_BLEND_NORMAL_TO_RGB565_WITH_MASK(dsc)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (dest_x = 0, src_x = 0; dest_x < w; dest_x++, src_x++) {
|
||||||
|
dest_buf_u16[dest_x] = lv_color_8_16_mix(src_buf_l8[src_x], dest_buf_u16[dest_x], mask_buf[dest_x]);
|
||||||
|
}
|
||||||
|
dest_buf_u16 = drawbuf_next_row(dest_buf_u16, dest_stride);
|
||||||
|
src_buf_l8 += src_stride;
|
||||||
|
mask_buf += mask_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (mask_buf && opa < LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_L8_BLEND_NORMAL_TO_RGB565_MIX_MASK_OPA(dsc)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (dest_x = 0, src_x = 0; dest_x < w; dest_x++, src_x++) {
|
||||||
|
dest_buf_u16[dest_x] = lv_color_8_16_mix(src_buf_l8[src_x], dest_buf_u16[dest_x], LV_OPA_MIX2(mask_buf[dest_x], opa));
|
||||||
|
}
|
||||||
|
dest_buf_u16 = drawbuf_next_row(dest_buf_u16, dest_stride);
|
||||||
|
src_buf_l8 += src_stride;
|
||||||
|
mask_buf += mask_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uint16_t res = 0;
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
lv_color16_t *dest_buf_c16 = (lv_color16_t *)dest_buf_u16;
|
||||||
|
for (dest_x = 0, src_x = 0; dest_x < w; dest_x++, src_x += 4) {
|
||||||
|
uint8_t rb = src_buf_l8[src_x] >> 3;
|
||||||
|
uint8_t g = src_buf_l8[src_x] >> 2;
|
||||||
|
switch (dsc->blend_mode) {
|
||||||
|
case LV_BLEND_MODE_ADDITIVE:
|
||||||
|
res = (LV_MIN(dest_buf_c16[dest_x].red + rb, 31)) << 11;
|
||||||
|
res += (LV_MIN(dest_buf_c16[dest_x].green + g, 63)) << 5;
|
||||||
|
res += LV_MIN(dest_buf_c16[dest_x].blue + rb, 31);
|
||||||
|
break;
|
||||||
|
case LV_BLEND_MODE_SUBTRACTIVE:
|
||||||
|
res = (LV_MAX(dest_buf_c16[dest_x].red - rb, 0)) << 11;
|
||||||
|
res += (LV_MAX(dest_buf_c16[dest_x].green - g, 0)) << 5;
|
||||||
|
res += LV_MAX(dest_buf_c16[dest_x].blue - rb, 0);
|
||||||
|
break;
|
||||||
|
case LV_BLEND_MODE_MULTIPLY:
|
||||||
|
res = ((dest_buf_c16[dest_x].red * rb) >> 5) << 11;
|
||||||
|
res += ((dest_buf_c16[dest_x].green * g) >> 6) << 5;
|
||||||
|
res += (dest_buf_c16[dest_x].blue * rb) >> 5;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LV_LOG_WARN("Not supported blend mode: %d", dsc->blend_mode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask_buf == NULL && opa >= LV_OPA_MAX) {
|
||||||
|
dest_buf_u16[dest_x] = res;
|
||||||
|
} else if (mask_buf == NULL && opa < LV_OPA_MAX) {
|
||||||
|
dest_buf_u16[dest_x] = lv_color_16_16_mix(res, dest_buf_u16[dest_x], opa);
|
||||||
|
} else {
|
||||||
|
if (opa >= LV_OPA_MAX) {
|
||||||
|
dest_buf_u16[dest_x] = lv_color_16_16_mix(res, dest_buf_u16[dest_x], mask_buf[dest_x]);
|
||||||
|
} else {
|
||||||
|
dest_buf_u16[dest_x] = lv_color_16_16_mix(res, dest_buf_u16[dest_x], LV_OPA_MIX2(mask_buf[dest_x], opa));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dest_buf_u16 = drawbuf_next_row(dest_buf_u16, dest_stride);
|
||||||
|
src_buf_l8 += src_stride;
|
||||||
|
if (mask_buf) {
|
||||||
|
mask_buf += mask_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void LV_ATTRIBUTE_FAST_MEM rgb565_image_blend(_lv_draw_sw_blend_image_dsc_t *dsc)
|
||||||
|
{
|
||||||
|
int32_t w = dsc->dest_w;
|
||||||
|
int32_t h = dsc->dest_h;
|
||||||
|
lv_opa_t opa = dsc->opa;
|
||||||
|
uint16_t *dest_buf_u16 = dsc->dest_buf;
|
||||||
|
int32_t dest_stride = dsc->dest_stride;
|
||||||
|
const uint16_t *src_buf_u16 = dsc->src_buf;
|
||||||
|
int32_t src_stride = dsc->src_stride;
|
||||||
|
const lv_opa_t *mask_buf = dsc->mask_buf;
|
||||||
|
int32_t mask_stride = dsc->mask_stride;
|
||||||
|
|
||||||
|
int32_t x;
|
||||||
|
int32_t y;
|
||||||
|
|
||||||
|
if (dsc->blend_mode == LV_BLEND_MODE_NORMAL) {
|
||||||
|
if (mask_buf == NULL && opa >= LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_RGB565(dsc)) {
|
||||||
|
uint32_t line_in_bytes = w * 2;
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
memcpy(dest_buf_u16, src_buf_u16, line_in_bytes); // lv_memcpy
|
||||||
|
dest_buf_u16 = drawbuf_next_row(dest_buf_u16, dest_stride);
|
||||||
|
src_buf_u16 = drawbuf_next_row(src_buf_u16, src_stride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (mask_buf == NULL && opa < LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_RGB565_WITH_OPA(dsc)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (x = 0; x < w; x++) {
|
||||||
|
dest_buf_u16[x] = lv_color_16_16_mix(src_buf_u16[x], dest_buf_u16[x], opa);
|
||||||
|
}
|
||||||
|
dest_buf_u16 = drawbuf_next_row(dest_buf_u16, dest_stride);
|
||||||
|
src_buf_u16 = drawbuf_next_row(src_buf_u16, src_stride);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (mask_buf && opa >= LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_RGB565_WITH_MASK(dsc)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (x = 0; x < w; x++) {
|
||||||
|
dest_buf_u16[x] = lv_color_16_16_mix(src_buf_u16[x], dest_buf_u16[x], mask_buf[x]);
|
||||||
|
}
|
||||||
|
dest_buf_u16 = drawbuf_next_row(dest_buf_u16, dest_stride);
|
||||||
|
src_buf_u16 = drawbuf_next_row(src_buf_u16, src_stride);
|
||||||
|
mask_buf += mask_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_RGB565_MIX_MASK_OPA(dsc)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (x = 0; x < w; x++) {
|
||||||
|
dest_buf_u16[x] = lv_color_16_16_mix(src_buf_u16[x], dest_buf_u16[x], LV_OPA_MIX2(mask_buf[x], opa));
|
||||||
|
}
|
||||||
|
dest_buf_u16 = drawbuf_next_row(dest_buf_u16, dest_stride);
|
||||||
|
src_buf_u16 = drawbuf_next_row(src_buf_u16, src_stride);
|
||||||
|
mask_buf += mask_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uint16_t res = 0;
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
lv_color16_t *dest_buf_c16 = (lv_color16_t *) dest_buf_u16;
|
||||||
|
lv_color16_t *src_buf_c16 = (lv_color16_t *) src_buf_u16;
|
||||||
|
for (x = 0; x < w; x++) {
|
||||||
|
switch (dsc->blend_mode) {
|
||||||
|
case LV_BLEND_MODE_ADDITIVE:
|
||||||
|
if (src_buf_u16[x] == 0x0000) {
|
||||||
|
continue; /*Do not add pure black*/
|
||||||
|
}
|
||||||
|
res = (LV_MIN(dest_buf_c16[x].red + src_buf_c16[x].red, 31)) << 11;
|
||||||
|
res += (LV_MIN(dest_buf_c16[x].green + src_buf_c16[x].green, 63)) << 5;
|
||||||
|
res += LV_MIN(dest_buf_c16[x].blue + src_buf_c16[x].blue, 31);
|
||||||
|
break;
|
||||||
|
case LV_BLEND_MODE_SUBTRACTIVE:
|
||||||
|
if (src_buf_u16[x] == 0x0000) {
|
||||||
|
continue; /*Do not subtract pure black*/
|
||||||
|
}
|
||||||
|
res = (LV_MAX(dest_buf_c16[x].red - src_buf_c16[x].red, 0)) << 11;
|
||||||
|
res += (LV_MAX(dest_buf_c16[x].green - src_buf_c16[x].green, 0)) << 5;
|
||||||
|
res += LV_MAX(dest_buf_c16[x].blue - src_buf_c16[x].blue, 0);
|
||||||
|
break;
|
||||||
|
case LV_BLEND_MODE_MULTIPLY:
|
||||||
|
if (src_buf_u16[x] == 0xffff) {
|
||||||
|
continue; /*Do not multiply with pure white (considered as 1)*/
|
||||||
|
}
|
||||||
|
res = ((dest_buf_c16[x].red * src_buf_c16[x].red) >> 5) << 11;
|
||||||
|
res += ((dest_buf_c16[x].green * src_buf_c16[x].green) >> 6) << 5;
|
||||||
|
res += (dest_buf_c16[x].blue * src_buf_c16[x].blue) >> 5;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LV_LOG_WARN("Not supported blend mode: %d", dsc->blend_mode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask_buf == NULL) {
|
||||||
|
dest_buf_u16[x] = lv_color_16_16_mix(res, dest_buf_u16[x], opa);
|
||||||
|
} else {
|
||||||
|
if (opa >= LV_OPA_MAX) {
|
||||||
|
dest_buf_u16[x] = lv_color_16_16_mix(res, dest_buf_u16[x], mask_buf[x]);
|
||||||
|
} else {
|
||||||
|
dest_buf_u16[x] = lv_color_16_16_mix(res, dest_buf_u16[x], LV_OPA_MIX2(mask_buf[x], opa));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dest_buf_u16 = drawbuf_next_row(dest_buf_u16, dest_stride);
|
||||||
|
src_buf_u16 = drawbuf_next_row(src_buf_u16, src_stride);
|
||||||
|
if (mask_buf) {
|
||||||
|
mask_buf += mask_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void LV_ATTRIBUTE_FAST_MEM rgb888_image_blend(_lv_draw_sw_blend_image_dsc_t *dsc, const uint8_t src_px_size)
|
||||||
|
{
|
||||||
|
int32_t w = dsc->dest_w;
|
||||||
|
int32_t h = dsc->dest_h;
|
||||||
|
lv_opa_t opa = dsc->opa;
|
||||||
|
uint16_t *dest_buf_u16 = dsc->dest_buf;
|
||||||
|
int32_t dest_stride = dsc->dest_stride;
|
||||||
|
const uint8_t *src_buf_u8 = dsc->src_buf;
|
||||||
|
int32_t src_stride = dsc->src_stride;
|
||||||
|
const lv_opa_t *mask_buf = dsc->mask_buf;
|
||||||
|
int32_t mask_stride = dsc->mask_stride;
|
||||||
|
|
||||||
|
int32_t dest_x;
|
||||||
|
int32_t src_x;
|
||||||
|
int32_t y;
|
||||||
|
|
||||||
|
if (dsc->blend_mode == LV_BLEND_MODE_NORMAL) {
|
||||||
|
if (mask_buf == NULL && opa >= LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_RGB565(dsc, src_px_size)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (dest_x = 0, src_x = 0; dest_x < w; dest_x++, src_x += src_px_size) {
|
||||||
|
dest_buf_u16[dest_x] = ((src_buf_u8[src_x + 2] & 0xF8) << 8) +
|
||||||
|
((src_buf_u8[src_x + 1] & 0xFC) << 3) +
|
||||||
|
((src_buf_u8[src_x + 0] & 0xF8) >> 3);
|
||||||
|
}
|
||||||
|
dest_buf_u16 = drawbuf_next_row(dest_buf_u16, dest_stride);
|
||||||
|
src_buf_u8 += src_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (mask_buf == NULL && opa < LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_RGB565_WITH_OPA(dsc, src_px_size)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (dest_x = 0, src_x = 0; dest_x < w; dest_x++, src_x += src_px_size) {
|
||||||
|
dest_buf_u16[dest_x] = lv_color_24_16_mix(&src_buf_u8[src_x], dest_buf_u16[dest_x], opa);
|
||||||
|
}
|
||||||
|
dest_buf_u16 = drawbuf_next_row(dest_buf_u16, dest_stride);
|
||||||
|
src_buf_u8 += src_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mask_buf && opa >= LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_RGB565_WITH_MASK(dsc, src_px_size)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (dest_x = 0, src_x = 0; dest_x < w; dest_x++, src_x += src_px_size) {
|
||||||
|
dest_buf_u16[dest_x] = lv_color_24_16_mix(&src_buf_u8[src_x], dest_buf_u16[dest_x], mask_buf[dest_x]);
|
||||||
|
}
|
||||||
|
dest_buf_u16 = drawbuf_next_row(dest_buf_u16, dest_stride);
|
||||||
|
src_buf_u8 += src_stride;
|
||||||
|
mask_buf += mask_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mask_buf && opa < LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_RGB565_MIX_MASK_OPA(dsc, src_px_size)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (dest_x = 0, src_x = 0; dest_x < w; dest_x++, src_x += src_px_size) {
|
||||||
|
dest_buf_u16[dest_x] = lv_color_24_16_mix(&src_buf_u8[src_x], dest_buf_u16[dest_x], LV_OPA_MIX2(mask_buf[dest_x], opa));
|
||||||
|
}
|
||||||
|
dest_buf_u16 = drawbuf_next_row(dest_buf_u16, dest_stride);
|
||||||
|
src_buf_u8 += src_stride;
|
||||||
|
mask_buf += mask_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uint16_t res = 0;
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
lv_color16_t *dest_buf_c16 = (lv_color16_t *) dest_buf_u16;
|
||||||
|
for (dest_x = 0, src_x = 0; dest_x < w; dest_x++, src_x += src_px_size) {
|
||||||
|
switch (dsc->blend_mode) {
|
||||||
|
case LV_BLEND_MODE_ADDITIVE:
|
||||||
|
res = (LV_MIN(dest_buf_c16[dest_x].red + (src_buf_u8[src_x + 2] >> 3), 31)) << 11;
|
||||||
|
res += (LV_MIN(dest_buf_c16[dest_x].green + (src_buf_u8[src_x + 1] >> 2), 63)) << 5;
|
||||||
|
res += LV_MIN(dest_buf_c16[dest_x].blue + (src_buf_u8[src_x + 0] >> 3), 31);
|
||||||
|
break;
|
||||||
|
case LV_BLEND_MODE_SUBTRACTIVE:
|
||||||
|
res = (LV_MAX(dest_buf_c16[dest_x].red - (src_buf_u8[src_x + 2] >> 3), 0)) << 11;
|
||||||
|
res += (LV_MAX(dest_buf_c16[dest_x].green - (src_buf_u8[src_x + 1] >> 2), 0)) << 5;
|
||||||
|
res += LV_MAX(dest_buf_c16[dest_x].blue - (src_buf_u8[src_x + 0] >> 3), 0);
|
||||||
|
break;
|
||||||
|
case LV_BLEND_MODE_MULTIPLY:
|
||||||
|
res = ((dest_buf_c16[dest_x].red * (src_buf_u8[src_x + 2] >> 3)) >> 5) << 11;
|
||||||
|
res += ((dest_buf_c16[dest_x].green * (src_buf_u8[src_x + 1] >> 2)) >> 6) << 5;
|
||||||
|
res += (dest_buf_c16[dest_x].blue * (src_buf_u8[src_x + 0] >> 3)) >> 5;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LV_LOG_WARN("Not supported blend mode: %d", dsc->blend_mode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask_buf == NULL) {
|
||||||
|
dest_buf_u16[dest_x] = lv_color_16_16_mix(res, dest_buf_u16[dest_x], opa);
|
||||||
|
} else {
|
||||||
|
if (opa >= LV_OPA_MAX) {
|
||||||
|
dest_buf_u16[dest_x] = lv_color_16_16_mix(res, dest_buf_u16[dest_x], mask_buf[dest_x]);
|
||||||
|
} else {
|
||||||
|
dest_buf_u16[dest_x] = lv_color_16_16_mix(res, dest_buf_u16[dest_x], LV_OPA_MIX2(mask_buf[dest_x], opa));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dest_buf_u16 = drawbuf_next_row(dest_buf_u16, dest_stride);
|
||||||
|
src_buf_u8 += src_stride;
|
||||||
|
if (mask_buf) {
|
||||||
|
mask_buf += mask_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void LV_ATTRIBUTE_FAST_MEM argb8888_image_blend(_lv_draw_sw_blend_image_dsc_t *dsc)
|
||||||
|
{
|
||||||
|
int32_t w = dsc->dest_w;
|
||||||
|
int32_t h = dsc->dest_h;
|
||||||
|
lv_opa_t opa = dsc->opa;
|
||||||
|
uint16_t *dest_buf_u16 = dsc->dest_buf;
|
||||||
|
int32_t dest_stride = dsc->dest_stride;
|
||||||
|
const uint8_t *src_buf_u8 = dsc->src_buf;
|
||||||
|
int32_t src_stride = dsc->src_stride;
|
||||||
|
const lv_opa_t *mask_buf = dsc->mask_buf;
|
||||||
|
int32_t mask_stride = dsc->mask_stride;
|
||||||
|
|
||||||
|
int32_t dest_x;
|
||||||
|
int32_t src_x;
|
||||||
|
int32_t y;
|
||||||
|
|
||||||
|
if (dsc->blend_mode == LV_BLEND_MODE_NORMAL) {
|
||||||
|
if (mask_buf == NULL && opa >= LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_RGB565(dsc)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (dest_x = 0, src_x = 0; dest_x < w; dest_x++, src_x += 4) {
|
||||||
|
dest_buf_u16[dest_x] = lv_color_24_16_mix(&src_buf_u8[src_x], dest_buf_u16[dest_x], src_buf_u8[src_x + 3]);
|
||||||
|
}
|
||||||
|
dest_buf_u16 = drawbuf_next_row(dest_buf_u16, dest_stride);
|
||||||
|
src_buf_u8 += src_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (mask_buf == NULL && opa < LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_RGB565_WITH_OPA(dsc)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (dest_x = 0, src_x = 0; dest_x < w; dest_x++, src_x += 4) {
|
||||||
|
dest_buf_u16[dest_x] = lv_color_24_16_mix(&src_buf_u8[src_x], dest_buf_u16[dest_x], LV_OPA_MIX2(src_buf_u8[src_x + 3],
|
||||||
|
opa));
|
||||||
|
}
|
||||||
|
dest_buf_u16 = drawbuf_next_row(dest_buf_u16, dest_stride);
|
||||||
|
src_buf_u8 += src_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (mask_buf && opa >= LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_RGB565_WITH_MASK(dsc)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (dest_x = 0, src_x = 0; dest_x < w; dest_x++, src_x += 4) {
|
||||||
|
dest_buf_u16[dest_x] = lv_color_24_16_mix(&src_buf_u8[src_x], dest_buf_u16[dest_x],
|
||||||
|
LV_OPA_MIX2(src_buf_u8[src_x + 3], mask_buf[dest_x]));
|
||||||
|
}
|
||||||
|
dest_buf_u16 = drawbuf_next_row(dest_buf_u16, dest_stride);
|
||||||
|
src_buf_u8 += src_stride;
|
||||||
|
mask_buf += mask_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (mask_buf && opa < LV_OPA_MAX) {
|
||||||
|
if (LV_RESULT_INVALID == LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_RGB565_MIX_MASK_OPA(dsc)) {
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
for (dest_x = 0, src_x = 0; dest_x < w; dest_x++, src_x += 4) {
|
||||||
|
dest_buf_u16[dest_x] = lv_color_24_16_mix(&src_buf_u8[src_x], dest_buf_u16[dest_x],
|
||||||
|
LV_OPA_MIX3(src_buf_u8[src_x + 3], mask_buf[dest_x], opa));
|
||||||
|
}
|
||||||
|
dest_buf_u16 = drawbuf_next_row(dest_buf_u16, dest_stride);
|
||||||
|
src_buf_u8 += src_stride;
|
||||||
|
mask_buf += mask_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
uint16_t res = 0;
|
||||||
|
for (y = 0; y < h; y++) {
|
||||||
|
lv_color16_t *dest_buf_c16 = (lv_color16_t *) dest_buf_u16;
|
||||||
|
for (dest_x = 0, src_x = 0; dest_x < w; dest_x++, src_x += 4) {
|
||||||
|
switch (dsc->blend_mode) {
|
||||||
|
case LV_BLEND_MODE_ADDITIVE:
|
||||||
|
res = (LV_MIN(dest_buf_c16[dest_x].red + (src_buf_u8[src_x + 2] >> 3), 31)) << 11;
|
||||||
|
res += (LV_MIN(dest_buf_c16[dest_x].green + (src_buf_u8[src_x + 1] >> 2), 63)) << 5;
|
||||||
|
res += LV_MIN(dest_buf_c16[dest_x].blue + (src_buf_u8[src_x + 0] >> 3), 31);
|
||||||
|
break;
|
||||||
|
case LV_BLEND_MODE_SUBTRACTIVE:
|
||||||
|
res = (LV_MAX(dest_buf_c16[dest_x].red - (src_buf_u8[src_x + 2] >> 3), 0)) << 11;
|
||||||
|
res += (LV_MAX(dest_buf_c16[dest_x].green - (src_buf_u8[src_x + 1] >> 2), 0)) << 5;
|
||||||
|
res += LV_MAX(dest_buf_c16[dest_x].blue - (src_buf_u8[src_x + 0] >> 3), 0);
|
||||||
|
break;
|
||||||
|
case LV_BLEND_MODE_MULTIPLY:
|
||||||
|
res = ((dest_buf_c16[dest_x].red * (src_buf_u8[src_x + 2] >> 3)) >> 5) << 11;
|
||||||
|
res += ((dest_buf_c16[dest_x].green * (src_buf_u8[src_x + 1] >> 2)) >> 6) << 5;
|
||||||
|
res += (dest_buf_c16[dest_x].blue * (src_buf_u8[src_x + 0] >> 3)) >> 5;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LV_LOG_WARN("Not supported blend mode: %d", dsc->blend_mode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mask_buf == NULL && opa >= LV_OPA_MAX) {
|
||||||
|
dest_buf_u16[dest_x] = lv_color_16_16_mix(res, dest_buf_u16[dest_x], src_buf_u8[src_x + 3]);
|
||||||
|
} else if (mask_buf == NULL && opa < LV_OPA_MAX) {
|
||||||
|
dest_buf_u16[dest_x] = lv_color_16_16_mix(res, dest_buf_u16[dest_x], LV_OPA_MIX2(opa, src_buf_u8[src_x + 3]));
|
||||||
|
} else {
|
||||||
|
if (opa >= LV_OPA_MAX) {
|
||||||
|
dest_buf_u16[dest_x] = lv_color_16_16_mix(res, dest_buf_u16[dest_x], mask_buf[dest_x]);
|
||||||
|
} else dest_buf_u16[dest_x] = lv_color_16_16_mix(res, dest_buf_u16[dest_x], LV_OPA_MIX3(mask_buf[dest_x], opa,
|
||||||
|
src_buf_u8[src_x + 3]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dest_buf_u16 = drawbuf_next_row(dest_buf_u16, dest_stride);
|
||||||
|
src_buf_u8 += src_stride;
|
||||||
|
if (mask_buf) {
|
||||||
|
mask_buf += mask_stride;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint16_t LV_ATTRIBUTE_FAST_MEM l8_to_rgb565(const uint8_t c1)
|
||||||
|
{
|
||||||
|
return ((c1 & 0xF8) << 8) + ((c1 & 0xFC) << 3) + ((c1 & 0xF8) >> 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint16_t LV_ATTRIBUTE_FAST_MEM lv_color_8_16_mix(const uint8_t c1, uint16_t c2, uint8_t mix)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (mix == 0) {
|
||||||
|
return c2;
|
||||||
|
} else if (mix == 255) {
|
||||||
|
return ((c1 & 0xF8) << 8) + ((c1 & 0xFC) << 3) + ((c1 & 0xF8) >> 3);
|
||||||
|
} else {
|
||||||
|
lv_opa_t mix_inv = 255 - mix;
|
||||||
|
|
||||||
|
return ((((c1 >> 3) * mix + ((c2 >> 11) & 0x1F) * mix_inv) << 3) & 0xF800) +
|
||||||
|
((((c1 >> 2) * mix + ((c2 >> 5) & 0x3F) * mix_inv) >> 3) & 0x07E0) +
|
||||||
|
(((c1 >> 3) * mix + (c2 & 0x1F) * mix_inv) >> 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline uint16_t LV_ATTRIBUTE_FAST_MEM lv_color_24_16_mix(const uint8_t *c1, uint16_t c2, uint8_t mix)
|
||||||
|
{
|
||||||
|
if (mix == 0) {
|
||||||
|
return c2;
|
||||||
|
} else if (mix == 255) {
|
||||||
|
return ((c1[2] & 0xF8) << 8) + ((c1[1] & 0xFC) << 3) + ((c1[0] & 0xF8) >> 3);
|
||||||
|
} else {
|
||||||
|
lv_opa_t mix_inv = 255 - mix;
|
||||||
|
|
||||||
|
return ((((c1[2] >> 3) * mix + ((c2 >> 11) & 0x1F) * mix_inv) << 3) & 0xF800) +
|
||||||
|
((((c1[1] >> 2) * mix + ((c2 >> 5) & 0x3F) * mix_inv) >> 3) & 0x07E0) +
|
||||||
|
(((c1[0] >> 3) * mix + (c2 & 0x1F) * mix_inv) >> 8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void *LV_ATTRIBUTE_FAST_MEM drawbuf_next_row(const void *buf, uint32_t stride)
|
||||||
|
{
|
||||||
|
return (void *)((uint8_t *)buf + stride);
|
||||||
|
}
|
||||||
73
Libraries/esp_lvgl_port/test_apps/simd/main/lv_fill_common.h
Normal file
73
Libraries/esp_lvgl_port/test_apps/simd/main/lv_fill_common.h
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "esp_err.h"
|
||||||
|
#include <stdint.h>
|
||||||
|
#include "lv_color.h"
|
||||||
|
#include "lv_draw_sw_blend.h"
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// ------------------------------------------------- Macros and Types --------------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Functionality test combinations
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
unsigned int min_w; // Minimum width of the test array
|
||||||
|
unsigned int min_h; // Minimum height of the test array
|
||||||
|
unsigned int max_w; // Maximum width of the test array
|
||||||
|
unsigned int max_h; // Maximum height of the test array
|
||||||
|
unsigned int min_unalign_byte; // Minimum amount of unaligned bytes of the test array
|
||||||
|
unsigned int max_unalign_byte; // Maximum amount of unaligned bytes of the test array
|
||||||
|
unsigned int unalign_step; // Increment step in bytes unalignment of the test array
|
||||||
|
unsigned int dest_stride_step; // Increment step in destination stride of the test array
|
||||||
|
unsigned int test_combinations_count; // Count of fest combinations
|
||||||
|
} test_matrix_params_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Functionality test case parameters
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
struct {
|
||||||
|
void *p_asm; // pointer to the working ASM test buf
|
||||||
|
void *p_ansi; // pointer to the working ANSI test buf
|
||||||
|
void *p_asm_alloc; // pointer to the beginning of the memory allocated for ASM test buf, used in free()
|
||||||
|
void *p_ansi_alloc; // pointer to the beginning of the memory allocated for ANSI test buf, used in free()
|
||||||
|
} buf;
|
||||||
|
void (*blend_api_func)(_lv_draw_sw_blend_fill_dsc_t *); // pointer to LVGL API function
|
||||||
|
lv_color_format_t color_format; // LV color format
|
||||||
|
size_t data_type_size; // Used data type size, eg sizeof()
|
||||||
|
size_t active_buf_len; // Length of buffer, where the actual data are stored (not including Canary bytes)
|
||||||
|
size_t total_buf_len; // Total length of buffer (including Canary bytes)
|
||||||
|
unsigned int dest_w; // Destination buffer width
|
||||||
|
unsigned int dest_h; // Destination buffer height
|
||||||
|
unsigned int dest_stride; // Destination buffer stride
|
||||||
|
unsigned int unalign_byte; // Destination buffer memory unalignment
|
||||||
|
} func_test_case_params_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Benchmark test case parameters
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
unsigned int height; // Test array height
|
||||||
|
unsigned int width; // Test array width
|
||||||
|
unsigned int stride; // Test array stride
|
||||||
|
unsigned int cc_height; // Corner case test array height
|
||||||
|
unsigned int cc_width; // Corner case test array width
|
||||||
|
unsigned int benchmark_cycles; // Count of benchmark cycles
|
||||||
|
void *array_align16; // test array with 16 byte alignment - testing most ideal case
|
||||||
|
void *array_align1; // test array with 1 byte alignment - testing wort case
|
||||||
|
void (*blend_api_func)(_lv_draw_sw_blend_fill_dsc_t *); // pointer to LVGL API function
|
||||||
|
} bench_test_case_params_t;
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
} /*extern "C"*/
|
||||||
|
#endif
|
||||||
50
Libraries/esp_lvgl_port/test_apps/simd/main/test_app_main.c
Normal file
50
Libraries/esp_lvgl_port/test_apps/simd/main/test_app_main.c
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include "unity.h"
|
||||||
|
#include "unity_test_utils.h"
|
||||||
|
#include "lv_fill_common.h"
|
||||||
|
|
||||||
|
#define TEST_MEMORY_LEAK_THRESHOLD (300)
|
||||||
|
|
||||||
|
void app_main(void)
|
||||||
|
{
|
||||||
|
|
||||||
|
// ______ _____ ______ _ _
|
||||||
|
// | _ \/ ___|| ___ \ | | | |
|
||||||
|
// | | | |\ `--. | |_/ / | |_ ___ ___ | |_
|
||||||
|
// | | | | `--. \| __/ | __| / _ \/ __|| __|
|
||||||
|
// | |/ / /\__/ /| | | |_ | __/\__ \| |_
|
||||||
|
// |___/ \____/ \_| \__| \___||___/ \__|
|
||||||
|
|
||||||
|
printf("______ _____ ______ _ _ \r\n");
|
||||||
|
printf("| _ \\/ ___|| ___ \\ | | | | \r\n");
|
||||||
|
printf("| | | |\\ `--. | |_/ / | |_ ___ ___ | |_ \r\n");
|
||||||
|
printf("| | | | `--. \\| __/ | __| / _ \\/ __|| __|\r\n");
|
||||||
|
printf("| |/ / /\\__/ /| | | |_ | __/\\__ \\| |_ \r\n");
|
||||||
|
printf("|___/ \\____/ \\_| \\__| \\___||___/ \\__|\r\n");
|
||||||
|
|
||||||
|
|
||||||
|
UNITY_BEGIN();
|
||||||
|
unity_run_menu();
|
||||||
|
UNITY_END();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* setUp runs before every test */
|
||||||
|
void setUp(void)
|
||||||
|
{
|
||||||
|
// Check for memory leaks
|
||||||
|
unity_utils_set_leak_level(TEST_MEMORY_LEAK_THRESHOLD);
|
||||||
|
unity_utils_record_free_mem();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* tearDown runs after every test */
|
||||||
|
void tearDown(void)
|
||||||
|
{
|
||||||
|
// Evaluate memory leaks
|
||||||
|
unity_utils_evaluate_leaks();
|
||||||
|
}
|
||||||
@ -0,0 +1,176 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <sdkconfig.h>
|
||||||
|
|
||||||
|
#include "unity.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "freertos/FreeRTOS.h" // for xthal_get_ccount()
|
||||||
|
#include "lv_fill_common.h"
|
||||||
|
#include "lv_draw_sw_blend.h"
|
||||||
|
#include "lv_draw_sw_blend_to_argb8888.h"
|
||||||
|
#include "lv_draw_sw_blend_to_rgb565.h"
|
||||||
|
|
||||||
|
#define WIDTH 128
|
||||||
|
#define HEIGHT 128
|
||||||
|
#define STRIDE WIDTH
|
||||||
|
#define UNALIGN_BYTES 1
|
||||||
|
#define BENCHMARK_CYCLES 1000
|
||||||
|
|
||||||
|
// ------------------------------------------------- Macros and Types --------------------------------------------------
|
||||||
|
|
||||||
|
static const char *TAG_LV_FILL_BENCH = "LV Fill Benchmark";
|
||||||
|
static const char *asm_ansi_func[] = {"ASM", "ANSI"};
|
||||||
|
static lv_color_t test_color = {
|
||||||
|
.blue = 0x56,
|
||||||
|
.green = 0x34,
|
||||||
|
.red = 0x12,
|
||||||
|
};
|
||||||
|
|
||||||
|
// ------------------------------------------------ Static function headers --------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Initialize the benchmark test
|
||||||
|
*/
|
||||||
|
static void lv_fill_benchmark_init(bench_test_case_params_t *test_params);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Run the benchmark test
|
||||||
|
*/
|
||||||
|
static float lv_fill_benchmark_run(bench_test_case_params_t *test_params, _lv_draw_sw_blend_fill_dsc_t *dsc);
|
||||||
|
|
||||||
|
// ------------------------------------------------ Test cases ---------------------------------------------------------
|
||||||
|
|
||||||
|
/*
|
||||||
|
Benchmark tests
|
||||||
|
|
||||||
|
Requires:
|
||||||
|
- To pass functionality tests first
|
||||||
|
|
||||||
|
Purpose:
|
||||||
|
- Test that an acceleration is achieved by an assembly implementation of LVGL blending API
|
||||||
|
|
||||||
|
Procedure:
|
||||||
|
- Initialize input parameters (test array length, width, allocate array...) of the benchmark test
|
||||||
|
- Run assembly version of LVGL blending API multiple times (1000-times or so)
|
||||||
|
- Firstly use an input test parameters for the most ideal case (16-byte aligned array, array width and height divisible by 4 for ARGB8888 color format)
|
||||||
|
- Then use worst-case input test parameters (1-byte aligned array, array width and height NOT divisible by 4 for ARGB8888 color format)
|
||||||
|
- Count how many CPU cycles does it take to run a function from the LVGL blending API for each case (ideal and worst case)
|
||||||
|
- Run ansi version of LVGL blending API multiple times (1000-times or so) and repeat the 2 above steps for the ansi version
|
||||||
|
- Free test arrays and structures needed for LVGL blending API
|
||||||
|
*/
|
||||||
|
// ------------------------------------------------ Test cases stages --------------------------------------------------
|
||||||
|
|
||||||
|
TEST_CASE("LV Fill benchmark ARGB8888", "[fill][benchmark][ARGB8888]")
|
||||||
|
{
|
||||||
|
uint32_t *dest_array_align16 = (uint32_t *)memalign(16, STRIDE * HEIGHT * sizeof(uint32_t) + UNALIGN_BYTES);
|
||||||
|
TEST_ASSERT_NOT_EQUAL(NULL, dest_array_align16);
|
||||||
|
|
||||||
|
// Apply byte unalignment for the worst-case test scenario
|
||||||
|
uint32_t *dest_array_align1 = dest_array_align16 + UNALIGN_BYTES;
|
||||||
|
|
||||||
|
bench_test_case_params_t test_params = {
|
||||||
|
.height = HEIGHT,
|
||||||
|
.width = WIDTH,
|
||||||
|
.stride = STRIDE * sizeof(uint32_t),
|
||||||
|
.cc_height = HEIGHT - 1,
|
||||||
|
.cc_width = WIDTH - 1,
|
||||||
|
.benchmark_cycles = BENCHMARK_CYCLES,
|
||||||
|
.array_align16 = (void *)dest_array_align16,
|
||||||
|
.array_align1 = (void *)dest_array_align1,
|
||||||
|
.blend_api_func = &lv_draw_sw_blend_color_to_argb8888,
|
||||||
|
};
|
||||||
|
|
||||||
|
ESP_LOGI(TAG_LV_FILL_BENCH, "running test for ARGB8888 color format");
|
||||||
|
lv_fill_benchmark_init(&test_params);
|
||||||
|
free(dest_array_align16);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("LV Fill benchmark RGB565", "[fill][benchmark][RGB565]")
|
||||||
|
{
|
||||||
|
uint16_t *dest_array_align16 = (uint16_t *)memalign(16, STRIDE * HEIGHT * sizeof(uint16_t) + UNALIGN_BYTES);
|
||||||
|
TEST_ASSERT_NOT_EQUAL(NULL, dest_array_align16);
|
||||||
|
|
||||||
|
// Apply byte unalignment for the worst-case test scenario
|
||||||
|
uint16_t *dest_array_align1 = dest_array_align16 + UNALIGN_BYTES;
|
||||||
|
|
||||||
|
bench_test_case_params_t test_params = {
|
||||||
|
.height = HEIGHT,
|
||||||
|
.width = WIDTH,
|
||||||
|
.stride = STRIDE * sizeof(uint16_t),
|
||||||
|
.cc_height = HEIGHT - 1,
|
||||||
|
.cc_width = WIDTH - 1,
|
||||||
|
.benchmark_cycles = BENCHMARK_CYCLES,
|
||||||
|
.array_align16 = (void *)dest_array_align16,
|
||||||
|
.array_align1 = (void *)dest_array_align1,
|
||||||
|
.blend_api_func = &lv_draw_sw_blend_color_to_rgb565,
|
||||||
|
};
|
||||||
|
|
||||||
|
ESP_LOGI(TAG_LV_FILL_BENCH, "running test for RGB565 color format");
|
||||||
|
lv_fill_benchmark_init(&test_params);
|
||||||
|
free(dest_array_align16);
|
||||||
|
}
|
||||||
|
// ------------------------------------------------ Static test functions ----------------------------------------------
|
||||||
|
|
||||||
|
static void lv_fill_benchmark_init(bench_test_case_params_t *test_params)
|
||||||
|
{
|
||||||
|
// Init structure for LVGL blend API, to call the Assembly API
|
||||||
|
_lv_draw_sw_blend_fill_dsc_t dsc = {
|
||||||
|
.dest_buf = test_params->array_align16,
|
||||||
|
.dest_w = test_params->width,
|
||||||
|
.dest_h = test_params->height,
|
||||||
|
.dest_stride = test_params->stride, // stride * sizeof()
|
||||||
|
.mask_buf = NULL,
|
||||||
|
.color = test_color,
|
||||||
|
.opa = LV_OPA_MAX,
|
||||||
|
.use_asm = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Init structure for LVGL blend API, to call the ANSI API
|
||||||
|
_lv_draw_sw_blend_fill_dsc_t dsc_cc = dsc;
|
||||||
|
dsc_cc.dest_buf = test_params->array_align1;
|
||||||
|
dsc_cc.dest_w = test_params->cc_width;
|
||||||
|
dsc_cc.dest_h = test_params->cc_height;
|
||||||
|
|
||||||
|
// Run benchmark 2 times:
|
||||||
|
// First run using assembly, second run using ANSI
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
|
||||||
|
// Run benchmark with the most ideal input parameters
|
||||||
|
// Dest array is 16 byte aligned, dest_w and dest_h are dividable by 4
|
||||||
|
float cycles = lv_fill_benchmark_run(test_params, &dsc); // Call Benchmark cycle
|
||||||
|
float per_sample = cycles / ((float)(dsc.dest_w * dsc.dest_h));
|
||||||
|
ESP_LOGI(TAG_LV_FILL_BENCH, " %s ideal case: %.3f cycles for %"PRIi32"x%"PRIi32" matrix, %.3f cycles per sample", asm_ansi_func[i], cycles, dsc.dest_w, dsc.dest_h, per_sample);
|
||||||
|
|
||||||
|
// Run benchmark with the corner case input parameters
|
||||||
|
// Dest array is 1 byte aligned, dest_w and dest_h are not dividable by 4
|
||||||
|
cycles = lv_fill_benchmark_run(test_params, &dsc_cc); // Call Benchmark cycle
|
||||||
|
per_sample = cycles / ((float)(dsc_cc.dest_w * dsc_cc.dest_h));
|
||||||
|
ESP_LOGI(TAG_LV_FILL_BENCH, " %s corner case: %.3f cycles for %"PRIi32"x%"PRIi32" matrix, %.3f cycles per sample\n", asm_ansi_func[i], cycles, dsc_cc.dest_w, dsc_cc.dest_h, per_sample);
|
||||||
|
|
||||||
|
// change to ANSI
|
||||||
|
dsc.use_asm = false;
|
||||||
|
dsc_cc.use_asm = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static float lv_fill_benchmark_run(bench_test_case_params_t *test_params, _lv_draw_sw_blend_fill_dsc_t *dsc)
|
||||||
|
{
|
||||||
|
// Call the DUT function for the first time to init the benchmark test
|
||||||
|
test_params->blend_api_func(dsc);
|
||||||
|
|
||||||
|
const unsigned int start_b = xthal_get_ccount();
|
||||||
|
for (int i = 0; i < test_params->benchmark_cycles; i++) {
|
||||||
|
test_params->blend_api_func(dsc);
|
||||||
|
}
|
||||||
|
const unsigned int end_b = xthal_get_ccount();
|
||||||
|
|
||||||
|
const float total_b = end_b - start_b;
|
||||||
|
const float cycles = total_b / (test_params->benchmark_cycles);
|
||||||
|
return cycles;
|
||||||
|
}
|
||||||
@ -0,0 +1,311 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <malloc.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include "unity.h"
|
||||||
|
#include "esp_log.h"
|
||||||
|
#include "lv_fill_common.h"
|
||||||
|
#include "lv_draw_sw_blend.h"
|
||||||
|
#include "lv_draw_sw_blend_to_argb8888.h"
|
||||||
|
#include "lv_draw_sw_blend_to_rgb565.h"
|
||||||
|
|
||||||
|
// ------------------------------------------------- Defines -----------------------------------------------------------
|
||||||
|
|
||||||
|
#define DBG_PRINT_OUTPUT false
|
||||||
|
#define CANARY_BYTES 4
|
||||||
|
|
||||||
|
// ------------------------------------------------- Macros and Types --------------------------------------------------
|
||||||
|
|
||||||
|
#define UPDATE_TEST_CASE(test_case_ptr, dest_w, dest_h, dest_stride, unalign_byte) ({ \
|
||||||
|
(test_case_ptr)->active_buf_len = (size_t)(dest_h * dest_stride); \
|
||||||
|
(test_case_ptr)->total_buf_len = (size_t)((dest_h * dest_stride) + (CANARY_BYTES * 2)); \
|
||||||
|
(test_case_ptr)->dest_w = (dest_w); \
|
||||||
|
(test_case_ptr)->dest_h = (dest_h); \
|
||||||
|
(test_case_ptr)->dest_stride = (dest_stride); \
|
||||||
|
(test_case_ptr)->unalign_byte = (unalign_byte); \
|
||||||
|
})
|
||||||
|
|
||||||
|
static const char *TAG_LV_FILL_FUNC = "LV Fill Functionality";
|
||||||
|
static char test_msg_buf[128];
|
||||||
|
|
||||||
|
static lv_color_t test_color = {
|
||||||
|
.blue = 0x56,
|
||||||
|
.green = 0x34,
|
||||||
|
.red = 0x12,
|
||||||
|
};
|
||||||
|
|
||||||
|
// ------------------------------------------------ Static function headers --------------------------------------------
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Generate all the functionality test combinations
|
||||||
|
*
|
||||||
|
* - generate functionality test combinations, based on the provided test_matrix struct
|
||||||
|
*
|
||||||
|
* @param[in] test_matrix Pointer to structure defining test matrix - all the test combinations
|
||||||
|
* @param[in] test_case Pointer ot structure defining functionality test case
|
||||||
|
*/
|
||||||
|
static void functionality_test_matrix(test_matrix_params_t *test_matrix, func_test_case_params_t *test_case);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Fill test buffers for functionality test
|
||||||
|
*
|
||||||
|
* @param[in] test_case Pointer ot structure defining functionality test case
|
||||||
|
*/
|
||||||
|
static void fill_test_bufs(func_test_case_params_t *test_case);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief The actual functionality test
|
||||||
|
*
|
||||||
|
* - function prepares structures for functionality testing and runs the LVGL API
|
||||||
|
*
|
||||||
|
* @param[in] test_case Pointer ot structure defining functionality test case
|
||||||
|
*/
|
||||||
|
static void lv_fill_functionality(func_test_case_params_t *test_case);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Evaluate results for 32bit data length
|
||||||
|
*
|
||||||
|
* @param[in] test_case Pointer ot structure defining functionality test case
|
||||||
|
*/
|
||||||
|
static void test_eval_32bit_data(func_test_case_params_t *test_case);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Evaluate results for 16bit data length
|
||||||
|
*
|
||||||
|
* @param[in] test_case Pointer ot structure defining functionality test case
|
||||||
|
*/
|
||||||
|
static void test_eval_16bit_data(func_test_case_params_t *test_case);
|
||||||
|
|
||||||
|
// ------------------------------------------------ Test cases ---------------------------------------------------------
|
||||||
|
|
||||||
|
/*
|
||||||
|
Functionality tests
|
||||||
|
|
||||||
|
Purpose:
|
||||||
|
- Test that an assembly version of LVGL blending API achieves the same results as the ANSI version
|
||||||
|
|
||||||
|
Procedure:
|
||||||
|
- Prepare testing matrix, to cover all the possible combinations of destination array widths, lengths, memory alignment...
|
||||||
|
- Run assembly version of the LVGL blending API
|
||||||
|
- Run ANSI C version of the LVGL blending API
|
||||||
|
- Compare the results
|
||||||
|
- Repeat above 3 steps for each test matrix setup
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ------------------------------------------------ Test cases stages --------------------------------------------------
|
||||||
|
|
||||||
|
TEST_CASE("Test fill functionality ARGB8888", "[fill][functionality][ARGB8888]")
|
||||||
|
{
|
||||||
|
test_matrix_params_t test_matrix = {
|
||||||
|
.min_w = 8, // 8 is the lower limit for the esp32s3 asm implementation, otherwise esp32 is executed
|
||||||
|
.min_h = 1,
|
||||||
|
.max_w = 16,
|
||||||
|
.max_h = 16,
|
||||||
|
.min_unalign_byte = 0,
|
||||||
|
.max_unalign_byte = 16,
|
||||||
|
.unalign_step = 1,
|
||||||
|
.dest_stride_step = 1,
|
||||||
|
.test_combinations_count = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
func_test_case_params_t test_case = {
|
||||||
|
.blend_api_func = &lv_draw_sw_blend_color_to_argb8888,
|
||||||
|
.color_format = LV_COLOR_FORMAT_ARGB8888,
|
||||||
|
.data_type_size = sizeof(uint32_t),
|
||||||
|
};
|
||||||
|
|
||||||
|
ESP_LOGI(TAG_LV_FILL_FUNC, "running test for ARGB8888 color format");
|
||||||
|
functionality_test_matrix(&test_matrix, &test_case);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE("Test fill functionality RGB565", "[fill][functionality][RGB565]")
|
||||||
|
{
|
||||||
|
test_matrix_params_t test_matrix = {
|
||||||
|
.min_w = 16, // 16 is the lower limit for the esp32s3 asm implementation, otherwise esp32 is executed
|
||||||
|
.min_h = 1,
|
||||||
|
.max_w = 32,
|
||||||
|
.max_h = 16,
|
||||||
|
.min_unalign_byte = 0,
|
||||||
|
.max_unalign_byte = 16,
|
||||||
|
.unalign_step = 1,
|
||||||
|
.dest_stride_step = 1,
|
||||||
|
.test_combinations_count = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
func_test_case_params_t test_case = {
|
||||||
|
.blend_api_func = &lv_draw_sw_blend_color_to_rgb565,
|
||||||
|
.color_format = LV_COLOR_FORMAT_RGB565,
|
||||||
|
.data_type_size = sizeof(uint16_t),
|
||||||
|
};
|
||||||
|
|
||||||
|
ESP_LOGI(TAG_LV_FILL_FUNC, "running test for RGB565 color format");
|
||||||
|
functionality_test_matrix(&test_matrix, &test_case);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------ Static test functions ----------------------------------------------
|
||||||
|
|
||||||
|
static void functionality_test_matrix(test_matrix_params_t *test_matrix, func_test_case_params_t *test_case)
|
||||||
|
{
|
||||||
|
// Step destination array width
|
||||||
|
for (int dest_w = test_matrix->min_w; dest_w <= test_matrix->max_w; dest_w++) {
|
||||||
|
|
||||||
|
// Step destination array height
|
||||||
|
for (int dest_h = test_matrix->min_h; dest_h <= test_matrix->max_h; dest_h++) {
|
||||||
|
|
||||||
|
// Step destination array stride
|
||||||
|
for (int dest_stride = dest_w; dest_stride <= dest_w * 2; dest_stride += test_matrix->dest_stride_step) {
|
||||||
|
|
||||||
|
// Step destination array unalignment
|
||||||
|
for (int unalign_byte = test_matrix->min_unalign_byte; unalign_byte <= test_matrix->max_unalign_byte; unalign_byte += test_matrix->unalign_step) {
|
||||||
|
|
||||||
|
// Call functionality test
|
||||||
|
UPDATE_TEST_CASE(test_case, dest_w, dest_h, dest_stride, unalign_byte);
|
||||||
|
lv_fill_functionality(test_case);
|
||||||
|
test_matrix->test_combinations_count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ESP_LOGI(TAG_LV_FILL_FUNC, "test combinations: %d\n", test_matrix->test_combinations_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lv_fill_functionality(func_test_case_params_t *test_case)
|
||||||
|
{
|
||||||
|
fill_test_bufs(test_case);
|
||||||
|
|
||||||
|
// Init structure for LVGL blend API, to call the Assembly API
|
||||||
|
_lv_draw_sw_blend_fill_dsc_t dsc_asm = {
|
||||||
|
.dest_buf = test_case->buf.p_asm,
|
||||||
|
.dest_w = test_case->dest_w,
|
||||||
|
.dest_h = test_case->dest_h,
|
||||||
|
.dest_stride = test_case->dest_stride * test_case->data_type_size, // stride * sizeof()
|
||||||
|
.mask_buf = NULL,
|
||||||
|
.color = test_color,
|
||||||
|
.opa = LV_OPA_MAX,
|
||||||
|
.use_asm = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Init structure for LVGL blend API, to call the ANSI API
|
||||||
|
_lv_draw_sw_blend_fill_dsc_t dsc_ansi = dsc_asm;
|
||||||
|
dsc_ansi.dest_buf = test_case->buf.p_ansi;
|
||||||
|
dsc_ansi.use_asm = false;
|
||||||
|
|
||||||
|
test_case->blend_api_func(&dsc_asm); // Call the LVGL API with Assembly code
|
||||||
|
test_case->blend_api_func(&dsc_ansi); // Call the LVGL API with ANSI code
|
||||||
|
|
||||||
|
// Shift array pointers by Canary Bytes amount back
|
||||||
|
test_case->buf.p_asm -= CANARY_BYTES * test_case->data_type_size;
|
||||||
|
test_case->buf.p_ansi -= CANARY_BYTES * test_case->data_type_size;
|
||||||
|
|
||||||
|
// Evaluate the results
|
||||||
|
sprintf(test_msg_buf, "Test case: dest_w = %d, dest_h = %d, dest_stride = %d, unalign_byte = %d\n", test_case->dest_w, test_case->dest_h, test_case->dest_stride, test_case->unalign_byte);
|
||||||
|
|
||||||
|
switch (test_case->color_format) {
|
||||||
|
case LV_COLOR_FORMAT_ARGB8888: {
|
||||||
|
test_eval_32bit_data(test_case);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case LV_COLOR_FORMAT_RGB565: {
|
||||||
|
test_eval_16bit_data(test_case);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
TEST_ASSERT_MESSAGE(false, "LV Color format not found");
|
||||||
|
}
|
||||||
|
|
||||||
|
free(test_case->buf.p_asm_alloc);
|
||||||
|
free(test_case->buf.p_ansi_alloc);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fill_test_bufs(func_test_case_params_t *test_case)
|
||||||
|
{
|
||||||
|
const size_t data_type_size = test_case->data_type_size; // sizeof() of used data type
|
||||||
|
const size_t total_buf_len = test_case->total_buf_len; // Total buffer length, data part of the buffer including the Canary bytes
|
||||||
|
const size_t active_buf_len = test_case->active_buf_len; // Length of buffer
|
||||||
|
const unsigned int unalign_byte = test_case->unalign_byte;
|
||||||
|
|
||||||
|
// Allocate destination arrays for Assembly and ANSI LVGL Blend API
|
||||||
|
void *mem_asm = memalign(16, (total_buf_len * data_type_size) + unalign_byte);
|
||||||
|
void *mem_ansi = memalign(16, (total_buf_len * data_type_size) + unalign_byte);
|
||||||
|
TEST_ASSERT_NOT_NULL_MESSAGE(mem_asm, "Lack of memory");
|
||||||
|
TEST_ASSERT_NOT_NULL_MESSAGE(mem_ansi, "Lack of memory");
|
||||||
|
|
||||||
|
// Save a pointer to the beginning of the allocated memory which will be used to free()
|
||||||
|
test_case->buf.p_asm_alloc = mem_asm;
|
||||||
|
test_case->buf.p_ansi_alloc = mem_ansi;
|
||||||
|
|
||||||
|
// Apply destination array unalignment
|
||||||
|
uint8_t *dest_buf_asm = (uint8_t *)mem_asm + unalign_byte;
|
||||||
|
uint8_t *dest_buf_ansi = (uint8_t *)mem_ansi + unalign_byte;
|
||||||
|
|
||||||
|
// Set the whole buffer to 0, including the Canary bytes part
|
||||||
|
memset(dest_buf_asm, 0, total_buf_len * data_type_size);
|
||||||
|
memset(dest_buf_ansi, 0, total_buf_len * data_type_size);
|
||||||
|
|
||||||
|
// Fill the actual part of the destination buffers with known values,
|
||||||
|
// Values must be same, because of the stride
|
||||||
|
for (int i = CANARY_BYTES; i < active_buf_len + CANARY_BYTES; i++) {
|
||||||
|
dest_buf_asm[i * data_type_size] = (uint8_t)(i % 255);
|
||||||
|
dest_buf_ansi[i * data_type_size] = (uint8_t)(i % 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shift array pointers by Canary Bytes amount
|
||||||
|
dest_buf_asm += CANARY_BYTES * data_type_size;
|
||||||
|
dest_buf_ansi += CANARY_BYTES * data_type_size;
|
||||||
|
|
||||||
|
// Save a pointer to the working part of the memory, where the test data are stored
|
||||||
|
test_case->buf.p_asm = (void *)dest_buf_asm;
|
||||||
|
test_case->buf.p_ansi = (void *)dest_buf_ansi;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_eval_32bit_data(func_test_case_params_t *test_case)
|
||||||
|
{
|
||||||
|
// Print results 32bit data
|
||||||
|
#if DBG_PRINT_OUTPUT
|
||||||
|
for (uint32_t i = 0; i < test_case->total_buf_len; i++) {
|
||||||
|
printf("dest_buf[%"PRIi32"] %s ansi = %8"PRIx32" \t asm = %8"PRIx32" \n", i, ((i < 10) ? (" ") : ("")), ((uint32_t *)test_case->buf.p_ansi)[i], ((uint32_t *)test_case->buf.p_asm)[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Canary bytes area must stay 0
|
||||||
|
TEST_ASSERT_EACH_EQUAL_UINT32_MESSAGE(0, (uint32_t *)test_case->buf.p_ansi, CANARY_BYTES, test_msg_buf);
|
||||||
|
TEST_ASSERT_EACH_EQUAL_UINT32_MESSAGE(0, (uint32_t *)test_case->buf.p_asm, CANARY_BYTES, test_msg_buf);
|
||||||
|
|
||||||
|
// dest_buf_asm and dest_buf_ansi must be equal
|
||||||
|
TEST_ASSERT_EQUAL_UINT32_ARRAY_MESSAGE((uint32_t *)test_case->buf.p_asm + CANARY_BYTES, (uint32_t *)test_case->buf.p_ansi + CANARY_BYTES, test_case->active_buf_len, test_msg_buf);
|
||||||
|
|
||||||
|
// Canary bytes area must stay 0
|
||||||
|
TEST_ASSERT_EACH_EQUAL_UINT32_MESSAGE(0, (uint32_t *)test_case->buf.p_ansi + (test_case->total_buf_len - CANARY_BYTES), CANARY_BYTES, test_msg_buf);
|
||||||
|
TEST_ASSERT_EACH_EQUAL_UINT32_MESSAGE(0, (uint32_t *)test_case->buf.p_asm + (test_case->total_buf_len - CANARY_BYTES), CANARY_BYTES, test_msg_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_eval_16bit_data(func_test_case_params_t *test_case)
|
||||||
|
{
|
||||||
|
// Print results, 16bit data
|
||||||
|
#if DBG_PRINT_OUTPUT
|
||||||
|
for (uint32_t i = 0; i < test_case->total_buf_len; i++) {
|
||||||
|
printf("dest_buf[%"PRIi32"] %s ansi = %8"PRIx16" \t asm = %8"PRIx16" \n", i, ((i < 10) ? (" ") : ("")), ((uint16_t *)test_case->buf.p_ansi)[i], ((uint16_t *)test_case->buf.p_asm)[i]);
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Canary bytes area must stay 0
|
||||||
|
TEST_ASSERT_EACH_EQUAL_UINT16_MESSAGE(0, (uint16_t *)test_case->buf.p_ansi, CANARY_BYTES, test_msg_buf);
|
||||||
|
TEST_ASSERT_EACH_EQUAL_UINT16_MESSAGE(0, (uint16_t *)test_case->buf.p_asm, CANARY_BYTES, test_msg_buf);
|
||||||
|
|
||||||
|
// dest_buf_asm and dest_buf_ansi must be equal
|
||||||
|
TEST_ASSERT_EQUAL_UINT16_ARRAY_MESSAGE((uint16_t *)test_case->buf.p_asm + CANARY_BYTES, (uint16_t *)test_case->buf.p_ansi + CANARY_BYTES, test_case->active_buf_len, test_msg_buf);
|
||||||
|
|
||||||
|
// Canary bytes area must stay 0
|
||||||
|
TEST_ASSERT_EACH_EQUAL_UINT16_MESSAGE(0, (uint16_t *)test_case->buf.p_ansi + (test_case->total_buf_len - CANARY_BYTES), CANARY_BYTES, test_msg_buf);
|
||||||
|
TEST_ASSERT_EACH_EQUAL_UINT16_MESSAGE(0, (uint16_t *)test_case->buf.p_asm + (test_case->total_buf_len - CANARY_BYTES), CANARY_BYTES, test_msg_buf);
|
||||||
|
}
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
CONFIG_ESP_TASK_WDT=n
|
||||||
|
CONFIG_OPTIMIZATION_LEVEL_RELEASE=y
|
||||||
|
CONFIG_COMPILER_OPTIMIZATION_PERF=y
|
||||||
@ -1,12 +1,11 @@
|
|||||||
#include "lv_screenshot.h"
|
#include "lv_screenshot.h"
|
||||||
|
|
||||||
#include "save_bmp.h"
|
|
||||||
#include "save_png.h"
|
#include "save_png.h"
|
||||||
|
|
||||||
static void data_pre_processing(lv_image_dsc_t* snapshot, uint16_t bpp, lv_100ask_screenshot_sv_t screenshot_sv);
|
static void data_pre_processing(lv_draw_buf_t* snapshot, uint16_t bpp, lv_100ask_screenshot_sv_t screenshot_sv);
|
||||||
|
|
||||||
bool lv_screenshot_create(lv_obj_t* obj, lv_color_format_t cf, lv_100ask_screenshot_sv_t screenshot_sv, const char* filename) {
|
bool lv_screenshot_create(lv_obj_t* obj, lv_color_format_t cf, lv_100ask_screenshot_sv_t screenshot_sv, const char* filename) {
|
||||||
lv_image_dsc_t* snapshot = lv_snapshot_take(obj, cf);
|
lv_draw_buf_t* snapshot = lv_snapshot_take(obj, cf);
|
||||||
|
|
||||||
if (snapshot) {
|
if (snapshot) {
|
||||||
data_pre_processing(snapshot, LV_COLOR_DEPTH, screenshot_sv);
|
data_pre_processing(snapshot, LV_COLOR_DEPTH, screenshot_sv);
|
||||||
@ -17,22 +16,16 @@ bool lv_screenshot_create(lv_obj_t* obj, lv_color_format_t cf, lv_100ask_screens
|
|||||||
} else if (LV_COLOR_DEPTH == 32) {
|
} else if (LV_COLOR_DEPTH == 32) {
|
||||||
lv_screenshot_save_png_file(snapshot->data, snapshot->header.w, snapshot->header.h, 32, filename);
|
lv_screenshot_save_png_file(snapshot->data, snapshot->header.w, snapshot->header.h, 32, filename);
|
||||||
}
|
}
|
||||||
} else if (screenshot_sv == LV_100ASK_SCREENSHOT_SV_BMP) {
|
|
||||||
if (LV_COLOR_DEPTH == 16) {
|
|
||||||
lve_screenshot_save_bmp_file(snapshot->data, snapshot->header.w, snapshot->header.h, 24, filename);
|
|
||||||
} else if (LV_COLOR_DEPTH == 32) {
|
|
||||||
lve_screenshot_save_bmp_file(snapshot->data, snapshot->header.w, snapshot->header.h, 32, filename);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lv_snapshot_free(snapshot);
|
lv_draw_buf_destroy(snapshot);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void data_pre_processing(lv_image_dsc_t* snapshot, uint16_t bpp, lv_100ask_screenshot_sv_t screenshot_sv) {
|
static void data_pre_processing(lv_draw_buf_t* snapshot, uint16_t bpp, lv_100ask_screenshot_sv_t screenshot_sv) {
|
||||||
if (bpp == 16) {
|
if (bpp == 16) {
|
||||||
uint16_t rgb565_data = 0;
|
uint16_t rgb565_data = 0;
|
||||||
uint32_t count = 0;
|
uint32_t count = 0;
|
||||||
|
|||||||
@ -1,93 +0,0 @@
|
|||||||
#include "save_bmp.h"
|
|
||||||
|
|
||||||
typedef struct tagBITMAPFILEHEADER {
|
|
||||||
uint16_t bfType;
|
|
||||||
uint32_t bfSize;
|
|
||||||
uint16_t bfReserved1;
|
|
||||||
uint16_t bfReserved2;
|
|
||||||
uint32_t bfOffBits;
|
|
||||||
} __attribute__((packed)) BITMAPFILEHEADER, *PBITMAPFILEHEADER;
|
|
||||||
|
|
||||||
typedef struct tagBITMAPINFOHEADER {
|
|
||||||
uint32_t biSize;
|
|
||||||
uint32_t biwidth;
|
|
||||||
uint32_t biheight;
|
|
||||||
uint16_t biPlanes;
|
|
||||||
uint16_t biBitCount;
|
|
||||||
uint32_t biCompression;
|
|
||||||
uint32_t biSizeImage;
|
|
||||||
uint32_t biXPelsPerMeter;
|
|
||||||
uint32_t biYPelsPerMeter;
|
|
||||||
uint32_t biClrUsed;
|
|
||||||
uint32_t biClrImportant;
|
|
||||||
} __attribute__((packed)) BITMAPINFOHEADER, *PBITMAPINFOHEADER;
|
|
||||||
|
|
||||||
typedef struct tagRGBQUAD {
|
|
||||||
uint8_t rgbBlue;
|
|
||||||
uint8_t rgbGreen;
|
|
||||||
uint8_t rgbRed;
|
|
||||||
uint8_t rgbReserved;
|
|
||||||
} __attribute__((packed)) RGBQUAD;
|
|
||||||
|
|
||||||
bool lve_screenshot_save_bmp_file(const uint8_t* image, uint32_t w, uint32_t h, uint32_t bpp, const char* filename) {
|
|
||||||
BITMAPFILEHEADER tBmpFileHead;
|
|
||||||
BITMAPINFOHEADER tBmpInfoHead;
|
|
||||||
|
|
||||||
uint32_t dwSize;
|
|
||||||
|
|
||||||
uint32_t bw;
|
|
||||||
lv_fs_file_t f;
|
|
||||||
|
|
||||||
memset(&tBmpFileHead, 0, sizeof(BITMAPFILEHEADER));
|
|
||||||
memset(&tBmpInfoHead, 0, sizeof(BITMAPINFOHEADER));
|
|
||||||
|
|
||||||
lv_fs_res_t res = lv_fs_open(&f, filename, LV_FS_MODE_WR);
|
|
||||||
if (res != LV_FS_RES_OK) {
|
|
||||||
LV_LOG_USER("Can't create output file %s", filename);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
tBmpFileHead.bfType = 0x4d42;
|
|
||||||
tBmpFileHead.bfSize = 0x36 + w * h * (bpp / 8);
|
|
||||||
tBmpFileHead.bfOffBits = 0x00000036;
|
|
||||||
|
|
||||||
tBmpInfoHead.biSize = 0x00000028;
|
|
||||||
tBmpInfoHead.biwidth = w;
|
|
||||||
tBmpInfoHead.biheight = h;
|
|
||||||
tBmpInfoHead.biPlanes = 0x0001;
|
|
||||||
tBmpInfoHead.biBitCount = bpp;
|
|
||||||
tBmpInfoHead.biCompression = 0;
|
|
||||||
tBmpInfoHead.biSizeImage = w * h * (bpp / 8);
|
|
||||||
tBmpInfoHead.biXPelsPerMeter = 0;
|
|
||||||
tBmpInfoHead.biYPelsPerMeter = 0;
|
|
||||||
tBmpInfoHead.biClrUsed = 0;
|
|
||||||
tBmpInfoHead.biClrImportant = 0;
|
|
||||||
|
|
||||||
res = lv_fs_write(&f, &tBmpFileHead, sizeof(tBmpFileHead), &bw);
|
|
||||||
if (bw != sizeof(tBmpFileHead)) {
|
|
||||||
LV_LOG_USER("Can't write BMP File Head to %s", filename);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
res = lv_fs_write(&f, &tBmpInfoHead, sizeof(tBmpInfoHead), &bw);
|
|
||||||
if (bw != sizeof(tBmpInfoHead)) {
|
|
||||||
LV_LOG_USER("Can't write BMP File Info Head to %s", filename);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
dwSize = w * bpp / 8;
|
|
||||||
const uint8_t* pPos = image + (h - 1) * dwSize;
|
|
||||||
|
|
||||||
while (pPos >= image) {
|
|
||||||
res = lv_fs_write(&f, pPos, dwSize, &bw);
|
|
||||||
if (bw != dwSize) {
|
|
||||||
LV_LOG_USER("Can't write date to BMP File %s", filename);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
pPos -= dwSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
lv_fs_close(&f);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
@ -1 +1 @@
|
|||||||
Subproject commit 09cb87cdc6a0168a98bc0a3182a8439b13249ead
|
Subproject commit 6decbb7f7783f6e48d4591fcb9f7810c2fb08e61
|
||||||
@ -50,7 +50,7 @@ static std::string getTitleParameter(std::shared_ptr<const Bundle> bundle) {
|
|||||||
static void onButtonClicked(lv_event_t* e) {
|
static void onButtonClicked(lv_event_t* e) {
|
||||||
lv_event_code_t code = lv_event_get_code(e);
|
lv_event_code_t code = lv_event_get_code(e);
|
||||||
if (code == LV_EVENT_CLICKED) {
|
if (code == LV_EVENT_CLICKED) {
|
||||||
size_t index = (size_t)(e->user_data);
|
auto index = reinterpret_cast<std::size_t>(lv_event_get_user_data(e));
|
||||||
TT_LOG_I(TAG, "Selected item at index %d", index);
|
TT_LOG_I(TAG, "Selected item at index %d", index);
|
||||||
tt::app::AppContext* app = service::loader::getCurrentApp();
|
tt::app::AppContext* app = service::loader::getCurrentApp();
|
||||||
auto bundle = std::make_shared<Bundle>();
|
auto bundle = std::make_shared<Bundle>();
|
||||||
|
|||||||
@ -18,7 +18,7 @@ static void onAppPressed(lv_event_t* e) {
|
|||||||
|
|
||||||
static void createAppWidget(const AppManifest* manifest, void* parent) {
|
static void createAppWidget(const AppManifest* manifest, void* parent) {
|
||||||
tt_check(parent);
|
tt_check(parent);
|
||||||
auto* list = static_cast<lv_obj_t*>(parent);
|
auto* list = reinterpret_cast<lv_obj_t*>(parent);
|
||||||
const void* icon = !manifest->icon.empty() ? manifest->icon.c_str() : TT_ASSETS_APP_ICON_FALLBACK;
|
const void* icon = !manifest->icon.empty() ? manifest->icon.c_str() : TT_ASSETS_APP_ICON_FALLBACK;
|
||||||
lv_obj_t* btn = lv_list_add_button(list, icon, manifest->name.c_str());
|
lv_obj_t* btn = lv_list_add_button(list, icon, manifest->name.c_str());
|
||||||
lv_obj_add_event_cb(btn, &onAppPressed, LV_EVENT_CLICKED, (void*)manifest);
|
lv_obj_add_event_cb(btn, &onAppPressed, LV_EVENT_CLICKED, (void*)manifest);
|
||||||
|
|||||||
@ -15,6 +15,7 @@
|
|||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
namespace tt::app::files {
|
namespace tt::app::files {
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
#include <cstring>
|
||||||
#include "FilesData.h"
|
#include "FilesData.h"
|
||||||
#include "FileUtils.h"
|
#include "FileUtils.h"
|
||||||
#include "StringUtils.h"
|
#include "StringUtils.h"
|
||||||
|
|||||||
@ -48,7 +48,7 @@ static std::string getTitleParameter(std::shared_ptr<const Bundle> bundle) {
|
|||||||
static void onListItemSelected(lv_event_t* e) {
|
static void onListItemSelected(lv_event_t* e) {
|
||||||
lv_event_code_t code = lv_event_get_code(e);
|
lv_event_code_t code = lv_event_get_code(e);
|
||||||
if (code == LV_EVENT_CLICKED) {
|
if (code == LV_EVENT_CLICKED) {
|
||||||
size_t index = (size_t)(e->user_data);
|
size_t index = reinterpret_cast<std::size_t>(lv_event_get_user_data(e));
|
||||||
TT_LOG_I(TAG, "Selected item at index %d", index);
|
TT_LOG_I(TAG, "Selected item at index %d", index);
|
||||||
tt::app::AppContext* app = service::loader::getCurrentApp();
|
tt::app::AppContext* app = service::loader::getCurrentApp();
|
||||||
auto bundle = std::make_shared<Bundle>();
|
auto bundle = std::make_shared<Bundle>();
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
#include "lvgl/Style.h"
|
#include "lvgl/Style.h"
|
||||||
#include "lvgl/Toolbar.h"
|
#include "lvgl/Toolbar.h"
|
||||||
#include <TactilityCore.h>
|
#include <TactilityCore.h>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
namespace tt::app::wificonnect {
|
namespace tt::app::wificonnect {
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
#include <cstring>
|
||||||
#include "ScreenshotTask.h"
|
#include "ScreenshotTask.h"
|
||||||
#include "lv_screenshot.h"
|
#include "lv_screenshot.h"
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user