add some code
This commit is contained in:
@@ -0,0 +1 @@
|
||||
e720c95cf0667554a204591bb5fade4655fb2990465557041200fa44b5bc7556
|
||||
143
managed_components/espressif__esp_lvgl_port/CHANGELOG.md
Normal file
143
managed_components/espressif__esp_lvgl_port/CHANGELOG.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# Changelog
|
||||
|
||||
## 2.6.0
|
||||
|
||||
### Features
|
||||
- Scaling feature in touch
|
||||
- Added support for PPA rotation in LVGL9 (available for ESP32-P4)
|
||||
|
||||
## 2.5.0
|
||||
|
||||
### Features (Functional change for button v4 users)
|
||||
- Updated LVGL port for using IoT button component v4 (LVGL port not anymore creating button, need to be created in app and included handle to LVGL port)
|
||||
|
||||
### Fixes
|
||||
- Fixed buffer size by selected color format
|
||||
- Fixed memory leak in LVGL8 in display removing https://github.com/espressif/esp-bsp/issues/462
|
||||
- Fixed draw buffer alignment
|
||||
|
||||
## 2.4.4
|
||||
|
||||
### Features
|
||||
- Changed queue to event group in main LVGL task for speed up https://github.com/espressif/esp-bsp/issues/492
|
||||
- Reworked handling encoder (knob) https://github.com/espressif/esp-bsp/pull/450
|
||||
|
||||
### Fixes
|
||||
- Fixed a crash when esp_lvgl_port was initialized from high priority task https://github.com/espressif/esp-bsp/issues/455
|
||||
- Allow to swap bytes when used SW rotation https://github.com/espressif/esp-bsp/issues/497
|
||||
|
||||
## 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
|
||||
|
||||
- Divided into files per feature
|
||||
- Added support for LVGL9
|
||||
- Added support for MIPI-DSI display
|
||||
|
||||
## 1.4.0
|
||||
|
||||
### Features
|
||||
|
||||
- Added support for USB HID mouse/keyboard as an input device
|
||||
|
||||
## 1.3.0
|
||||
|
||||
### Features
|
||||
|
||||
- Added low power interface
|
||||
|
||||
## 1.2.0
|
||||
|
||||
### Features
|
||||
|
||||
- Added support for encoder (knob) as an input device
|
||||
|
||||
## 1.1.0
|
||||
|
||||
### Features
|
||||
|
||||
- Added support for navigation buttons as an input device
|
||||
File diff suppressed because one or more lines are too long
134
managed_components/espressif__esp_lvgl_port/CMakeLists.txt
Normal file
134
managed_components/espressif__esp_lvgl_port/CMakeLists.txt
Normal file
@@ -0,0 +1,134 @@
|
||||
include($ENV{IDF_PATH}/tools/cmake/version.cmake) # $ENV{IDF_VERSION} was added after v4.3...
|
||||
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_LESS "4.4")
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(ADD_SRCS "")
|
||||
set(ADD_LIBS "")
|
||||
set(PRIV_REQ "")
|
||||
idf_build_get_property(target IDF_TARGET)
|
||||
if(${target} STREQUAL "esp32p4")
|
||||
list(APPEND ADD_SRCS "src/common/ppa/lcd_ppa.c")
|
||||
list(APPEND ADD_LIBS idf::esp_driver_ppa)
|
||||
list(APPEND PRIV_REQ esp_driver_ppa)
|
||||
endif()
|
||||
|
||||
# This component uses a CMake workaround, so we can compile esp_lvgl_port for both LVGL8.x and LVGL9.x
|
||||
# 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"
|
||||
PRIV_REQUIRES "${PRIV_REQ}")
|
||||
|
||||
# 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}")
|
||||
if(lvgl_ver VERSION_LESS "9.0.0")
|
||||
message(VERBOSE "Compiling esp_lvgl_port for LVGL8")
|
||||
set(PORT_FOLDER "lvgl8")
|
||||
else()
|
||||
message(VERBOSE "Compiling esp_lvgl_port for LVGL9")
|
||||
set(PORT_FOLDER "lvgl9")
|
||||
endif()
|
||||
|
||||
# Add LVGL port extensions
|
||||
set(PORT_PATH "src/${PORT_FOLDER}")
|
||||
|
||||
idf_build_get_property(build_components BUILD_COMPONENTS)
|
||||
if("espressif__button" IN_LIST build_components)
|
||||
list(APPEND ADD_SRCS "${PORT_PATH}/esp_lvgl_port_button.c")
|
||||
list(APPEND ADD_LIBS idf::espressif__button)
|
||||
endif()
|
||||
if("button" IN_LIST build_components)
|
||||
list(APPEND ADD_SRCS "${PORT_PATH}/esp_lvgl_port_button.c")
|
||||
list(APPEND ADD_LIBS idf::button)
|
||||
endif()
|
||||
if("espressif__esp_lcd_touch" IN_LIST build_components)
|
||||
list(APPEND ADD_SRCS "${PORT_PATH}/esp_lvgl_port_touch.c")
|
||||
list(APPEND ADD_LIBS idf::espressif__esp_lcd_touch)
|
||||
endif()
|
||||
if("esp_lcd_touch" IN_LIST build_components)
|
||||
list(APPEND ADD_SRCS "${PORT_PATH}/esp_lvgl_port_touch.c")
|
||||
list(APPEND ADD_LIBS idf::esp_lcd_touch)
|
||||
endif()
|
||||
if("espressif__knob" IN_LIST build_components)
|
||||
list(APPEND ADD_SRCS "${PORT_PATH}/esp_lvgl_port_knob.c")
|
||||
list(APPEND ADD_LIBS idf::espressif__knob)
|
||||
endif()
|
||||
if("knob" IN_LIST build_components)
|
||||
list(APPEND ADD_SRCS "${PORT_PATH}/esp_lvgl_port_knob.c")
|
||||
list(APPEND ADD_LIBS idf::knob)
|
||||
endif()
|
||||
if("espressif__usb_host_hid" IN_LIST build_components)
|
||||
list(APPEND ADD_SRCS "${PORT_PATH}/esp_lvgl_port_usbhid.c" "images/${PORT_FOLDER}/img_cursor.c")
|
||||
list(APPEND ADD_LIBS idf::espressif__usb_host_hid)
|
||||
endif()
|
||||
if("usb_host_hid" IN_LIST build_components)
|
||||
list(APPEND ADD_SRCS "${PORT_PATH}/esp_lvgl_port_usbhid.c" "images/${PORT_FOLDER}/img_cursor.c")
|
||||
list(APPEND ADD_LIBS idf::usb_host_hid)
|
||||
endif()
|
||||
|
||||
# Include SIMD assembly source code for rendering, only for (9.1.0 <= LVG_version < 9.2.0) and only for esp32 and esp32s3
|
||||
if((lvgl_ver VERSION_GREATER_EQUAL "9.1.0") AND (lvgl_ver VERSION_LESS "9.2.0"))
|
||||
if(CONFIG_IDF_TARGET_ESP32 OR CONFIG_IDF_TARGET_ESP32S3)
|
||||
message(VERBOSE "Compiling SIMD")
|
||||
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()
|
||||
|
||||
# Explicitly add all assembly macro files
|
||||
file(GLOB_RECURSE ASM_MACROS ${PORT_PATH}/simd/lv_macro_*.S)
|
||||
list(APPEND ADD_SRCS ${ASM_MACROS})
|
||||
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")
|
||||
set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u lv_color_blend_to_rgb888_esp")
|
||||
set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u lv_rgb565_blend_normal_to_rgb565_esp")
|
||||
set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u lv_rgb888_blend_normal_to_rgb888_esp")
|
||||
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)
|
||||
10
managed_components/espressif__esp_lvgl_port/Kconfig
Normal file
10
managed_components/espressif__esp_lvgl_port/Kconfig
Normal file
@@ -0,0 +1,10 @@
|
||||
menu "ESP LVGL PORT"
|
||||
|
||||
config LVGL_PORT_ENABLE_PPA
|
||||
depends on SOC_PPA_SUPPORTED
|
||||
bool "Enable PPA for screen rotation"
|
||||
default n
|
||||
help
|
||||
Enables using PPA for screen rotation.
|
||||
|
||||
endmenu
|
||||
369
managed_components/espressif__esp_lvgl_port/README.md
Normal file
369
managed_components/espressif__esp_lvgl_port/README.md
Normal file
@@ -0,0 +1,369 @@
|
||||
# LVGL ESP Portation
|
||||
|
||||
[](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.
|
||||
|
||||
## Features
|
||||
* Initialization of the LVGL
|
||||
* Create task and timer
|
||||
* 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 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 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
|
||||
|
||||
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:
|
||||
version: "^8"
|
||||
public: true
|
||||
```
|
||||
|
||||
### 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 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
|
||||
|
||||
### Initialization
|
||||
``` c
|
||||
const lvgl_port_cfg_t lvgl_cfg = ESP_LVGL_PORT_INIT_CONFIG();
|
||||
esp_err_t err = lvgl_port_init(&lvgl_cfg);
|
||||
```
|
||||
|
||||
### Add screen
|
||||
|
||||
Add an LCD screen to the LVGL. It can be called multiple times for adding multiple LCD screens.
|
||||
|
||||
``` c
|
||||
static lv_disp_t * disp_handle;
|
||||
|
||||
/* LCD IO */
|
||||
esp_lcd_panel_io_handle_t io_handle = NULL;
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t) 1, &io_config, &io_handle));
|
||||
|
||||
/* LCD driver initialization */
|
||||
esp_lcd_panel_handle_t lcd_panel_handle;
|
||||
ESP_ERROR_CHECK(esp_lcd_new_panel_st7789(io_handle, &panel_config, &lcd_panel_handle));
|
||||
|
||||
/* Add LCD screen */
|
||||
const lvgl_port_display_cfg_t disp_cfg = {
|
||||
.io_handle = io_handle,
|
||||
.panel_handle = lcd_panel_handle,
|
||||
.buffer_size = DISP_WIDTH*DISP_HEIGHT,
|
||||
.double_buffer = true,
|
||||
.hres = DISP_WIDTH,
|
||||
.vres = DISP_HEIGHT,
|
||||
.monochrome = false,
|
||||
.mipi_dsi = false,
|
||||
.color_format = LV_COLOR_FORMAT_RGB565,
|
||||
.rotation = {
|
||||
.swap_xy = false,
|
||||
.mirror_x = false,
|
||||
.mirror_y = false,
|
||||
},
|
||||
.flags = {
|
||||
.buff_dma = true,
|
||||
.swap_bytes = false,
|
||||
}
|
||||
};
|
||||
disp_handle = lvgl_port_add_disp(&disp_cfg);
|
||||
|
||||
/* ... the rest of the initialization ... */
|
||||
|
||||
/* If deinitializing LVGL port, remember to delete all displays: */
|
||||
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 to the LVGL. It can be called more times for adding more touch inputs.
|
||||
``` c
|
||||
/* Touch driver initialization */
|
||||
...
|
||||
esp_lcd_touch_handle_t tp;
|
||||
esp_err_t err = esp_lcd_touch_new_i2c_gt911(io_handle, &tp_cfg, &tp);
|
||||
|
||||
/* Add touch input (for selected screen) */
|
||||
const lvgl_port_touch_cfg_t touch_cfg = {
|
||||
.disp = disp_handle,
|
||||
.handle = tp,
|
||||
};
|
||||
lv_indev_t* touch_handle = lvgl_port_add_touch(&touch_cfg);
|
||||
|
||||
/* ... the rest of the initialization ... */
|
||||
|
||||
/* If deinitializing LVGL port, remember to delete all touches: */
|
||||
lvgl_port_remove_touch(touch_handle);
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> If the screen has another resolution than the touch resolution, you can use scaling by add `.scale.x` or `.scale.y` into `lvgl_port_touch_cfg_t` configuration structure.
|
||||
|
||||
### Add buttons input
|
||||
|
||||
Add buttons input to the LVGL. It can be called more times for adding more buttons inputs for different displays. This feature is available only when the component `espressif/button` was added into the project.
|
||||
``` c
|
||||
/* Buttons configuration structure */
|
||||
const button_gpio_config_t bsp_button_config[] = {
|
||||
{
|
||||
.gpio_num = GPIO_NUM_37,
|
||||
.active_level = 0,
|
||||
},
|
||||
{
|
||||
.gpio_num = GPIO_NUM_38,
|
||||
.active_level = 0,
|
||||
},
|
||||
{
|
||||
.gpio_num = GPIO_NUM_39,
|
||||
.active_level = 0,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
const button_config_t btn_cfg = {0};
|
||||
button_handle_t prev_btn_handle = NULL;
|
||||
button_handle_t next_btn_handle = NULL;
|
||||
button_handle_t enter_btn_handle = NULL;
|
||||
iot_button_new_gpio_device(&btn_cfg, &bsp_button_config[0], &prev_btn_handle);
|
||||
iot_button_new_gpio_device(&btn_cfg, &bsp_button_config[1], &next_btn_handle);
|
||||
iot_button_new_gpio_device(&btn_cfg, &bsp_button_config[2], &enter_btn_handle);
|
||||
|
||||
const lvgl_port_nav_btns_cfg_t btns = {
|
||||
.disp = disp_handle,
|
||||
.button_prev = prev_btn_handle,
|
||||
.button_next = next_btn_handle,
|
||||
.button_enter = enter_btn_handle
|
||||
};
|
||||
|
||||
/* Add buttons input (for selected screen) */
|
||||
lv_indev_t* buttons_handle = lvgl_port_add_navigation_buttons(&btns);
|
||||
|
||||
/* ... the rest of the initialization ... */
|
||||
|
||||
/* If deinitializing LVGL port, remember to delete all buttons: */
|
||||
lvgl_port_remove_navigation_buttons(buttons_handle);
|
||||
```
|
||||
> [!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.
|
||||
|
||||
### Add encoder input
|
||||
|
||||
Add encoder input to the LVGL. It can be called more times for adding more encoder inputs for different displays. This feature is available only when the component `espressif/knob` was added into the project.
|
||||
``` c
|
||||
|
||||
static const button_gpio_config_t encoder_btn_config = {
|
||||
.gpio_num = GPIO_BTN_PRESS,
|
||||
.active_level = 0,
|
||||
};
|
||||
|
||||
const knob_config_t encoder_a_b_config = {
|
||||
.default_direction = 0,
|
||||
.gpio_encoder_a = GPIO_ENCODER_A,
|
||||
.gpio_encoder_b = GPIO_ENCODER_B,
|
||||
};
|
||||
|
||||
const button_config_t btn_cfg = {0};
|
||||
button_handle_t encoder_btn_handle = NULL;
|
||||
BSP_ERROR_CHECK_RETURN_NULL(iot_button_new_gpio_device(&btn_cfg, &encoder_btn_config, &encoder_btn_handle));
|
||||
|
||||
/* Encoder configuration structure */
|
||||
const lvgl_port_encoder_cfg_t encoder = {
|
||||
.disp = disp_handle,
|
||||
.encoder_a_b = &encoder_a_b_config,
|
||||
.encoder_enter = encoder_btn_handle
|
||||
};
|
||||
|
||||
/* Add encoder input (for selected screen) */
|
||||
lv_indev_t* encoder_handle = lvgl_port_add_encoder(&encoder);
|
||||
|
||||
/* ... the rest of the initialization ... */
|
||||
|
||||
/* If deinitializing LVGL port, remember to delete all encoders: */
|
||||
lvgl_port_remove_encoder(encoder_handle);
|
||||
```
|
||||
> [!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.
|
||||
|
||||
### Add USB HID keyboard and mouse input
|
||||
|
||||
Add mouse and keyboard input to the LVGL. This feature is available only when the component [usb_host_hid](https://components.espressif.com/components/espressif/usb_host_hid) was added into the project.
|
||||
|
||||
``` c
|
||||
/* USB initialization */
|
||||
usb_host_config_t host_config = {
|
||||
.skip_phy_setup = false,
|
||||
.intr_flags = ESP_INTR_FLAG_LEVEL1,
|
||||
};
|
||||
ESP_ERROR_CHECK(usb_host_install(&host_config));
|
||||
|
||||
...
|
||||
|
||||
/* Add mouse input device */
|
||||
const lvgl_port_hid_mouse_cfg_t mouse_cfg = {
|
||||
.disp = display,
|
||||
.sensitivity = 1, /* Sensitivity of the mouse moving */
|
||||
};
|
||||
lvgl_port_add_usb_hid_mouse_input(&mouse_cfg);
|
||||
|
||||
/* Add keyboard input device */
|
||||
const lvgl_port_hid_keyboard_cfg_t kb_cfg = {
|
||||
.disp = display,
|
||||
};
|
||||
kb_indev = lvgl_port_add_usb_hid_keyboard_input(&kb_cfg);
|
||||
```
|
||||
|
||||
Keyboard special behavior (when objects are in group):
|
||||
- **TAB**: Select next object
|
||||
- **SHIFT** + **TAB**: Select previous object
|
||||
- **ENTER**: Control object (e.g. click to button)
|
||||
- **ARROWS** or **HOME** or **END**: Move in text area
|
||||
- **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.
|
||||
|
||||
### LVGL API usage
|
||||
|
||||
Every LVGL calls must be protected with these lock/unlock commands:
|
||||
``` c
|
||||
/* Wait for the other task done the screen operation */
|
||||
lvgl_port_lock(0);
|
||||
...
|
||||
lv_obj_t * screen = lv_disp_get_scr_act(disp_handle);
|
||||
lv_obj_t * obj = lv_label_create(screen);
|
||||
...
|
||||
/* Screen operation done -> release for the other task */
|
||||
lvgl_port_unlock();
|
||||
```
|
||||
|
||||
### Rotating screen
|
||||
|
||||
LVGL port supports rotation of the display. You can select whether you'd like software rotation or hardware rotation.
|
||||
Software rotation requires no additional logic in your `flush_cb` callback.
|
||||
|
||||
Rotation mode can be selected in the `lvgl_port_display_cfg_t` structure.
|
||||
``` c
|
||||
const lvgl_port_display_cfg_t disp_cfg = {
|
||||
...
|
||||
.flags = {
|
||||
...
|
||||
.sw_rotate = true / false, // true: software; false: hardware
|
||||
}
|
||||
}
|
||||
```
|
||||
Display rotation can be changed at runtime.
|
||||
|
||||
``` c
|
||||
lv_disp_set_rotation(disp_handle, LV_DISP_ROT_90);
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> Software rotation consumes more RAM. Software rotation uses [PPA](https://docs.espressif.com/projects/esp-idf/en/latest/esp32p4/api-reference/peripherals/ppa.html) if available on the chip (e.g. ESP32P4).
|
||||
|
||||
> [!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
|
||||
|
||||
If the SRAM is insufficient, you can use the PSRAM as a canvas and use a small trans_buffer to carry it, this makes drawing more efficient.
|
||||
``` c
|
||||
const lvgl_port_display_cfg_t disp_cfg = {
|
||||
...
|
||||
.buffer_size = DISP_WIDTH * DISP_HEIGHT, // in PSRAM, not DMA-capable
|
||||
.trans_size = size, // in SRAM, DMA-capable
|
||||
.flags = {
|
||||
.buff_spiram = true,
|
||||
.buff_dma = false,
|
||||
...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 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
|
||||
|
||||
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
|
||||
```
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 18 KiB |
168
managed_components/espressif__esp_lvgl_port/docs/performance.md
Normal file
168
managed_components/espressif__esp_lvgl_port/docs/performance.md
Normal file
@@ -0,0 +1,168 @@
|
||||
# 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. Examples in [ESP-BSP](https://github.com/espressif/esp-bsp) are ready to use with the best performance.
|
||||
|
||||
## Performance metrics
|
||||
|
||||
In this document we will use following metrics for performance evaluation:
|
||||
|
||||
1. Measure time needed for refreshing the whole screen.
|
||||
2. Use LVGL's [lv_demo_benchmark()](https://github.com/lvgl/lvgl/tree/v8.3.6/demos/benchmark) -test suite- to measure Frames per second (weighted FPS).
|
||||
3. Use LVGL's [lv_demo_music()](https://github.com/lvgl/lvgl/tree/v8.3.6/demos/music) -demo application- to measure Frames per second (average FPS).
|
||||
|
||||
## Settings on ESP32 chips which have impact on LCD and LVGL performance
|
||||
|
||||
Following options and settings have impact on LCD performance (FPS). Some options yield only small difference in FPS (e.g. ~1 FPS), and some of them are more significant. Usually it depends on complexity of the graphical application (number of widgets...), resources (CPU time, RAM available...) and size of screen (definition and color depth).
|
||||
|
||||
Another set of key parametes are hardware related (graphical IO, frame buffer location), which are not yet covered in this document.
|
||||
|
||||
### LVGL Buffer configuration
|
||||
|
||||
**This is by far the most significant setting.** Users are encouraged to focus on correct frame buffer configuration before moving ahead with other optimizations.
|
||||
|
||||
On the other hand, the frame buffer(s) will consume significant portion of your RAM. In the graph below, you can see different frame buffer settings and resulting FPS:
|
||||
|
||||

|
||||
|
||||
Main takeaways from the graph are:
|
||||
|
||||
* The size of **LVGL buffer** and **double buffering** feature has big impact on performance.
|
||||
* Frame buffer size >25% of the screen does not bring relevant performance boost
|
||||
* Frame buffer size <10% will have severe negative effect on performance
|
||||
|
||||
*Note:* The measurements are valid for frame buffer in internal SRAM. Placing the frame buffer into external PSRAM will yield worse results.
|
||||
|
||||
### Compiler optimization level
|
||||
|
||||
Recommended level is "Performance" for good results. The "Performance" setting causes the compiled code to be larger and faster.
|
||||
|
||||
* `CONFIG_COMPILER_OPTIMIZATION_PERF=y`
|
||||
|
||||
### CPU frequency
|
||||
|
||||
The CPU frequency has a big impact on LCD performance. The recommended value is maximum -> 240 MHz.
|
||||
|
||||
* `CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y`
|
||||
|
||||
### Flash frequency and mode
|
||||
|
||||
The flash clock frequency and mode (bus width) has a big impact on LCD performance. Your choices will be limited by your hardware flash configuration. The higher the frequency and bus width, the better.
|
||||
|
||||
* `CONFIG_ESPTOOLPY_FLASHFREQ_120M=y`
|
||||
* `CONFIG_ESPTOOLPY_FLASHMODE_QIO=y` or `CONFIG_ESPTOOLPY_OCT_FLASH` on supported chips
|
||||
|
||||
More information about SPI Flash configuration can be found in [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s3/api-guides/flash_psram_config.html).
|
||||
|
||||
### Fast LVGL memory
|
||||
|
||||
This option puts the most frequently used LVGL functions into IRAM for execution speed up.
|
||||
|
||||
* `CONFIG_LV_ATTRIBUTE_FAST_MEM_USE_IRAM=y`
|
||||
|
||||
### Affinity main task to second core
|
||||
|
||||
The main LVGL task can be processed on the second core of the CPU. It can increase performance. (It is available only on dual-core chips)
|
||||
|
||||
* `CONFIG_ESP_MAIN_TASK_AFFINITY_CPU1=y`
|
||||
|
||||
### Using esp-idf `memcpy` and `memset` instead LVGL's configuration
|
||||
|
||||
Native esp-idf implementation are a little (~1-3 FPS) faster (only for LVGL8).
|
||||
|
||||
* `CONFIG_LV_MEMCPY_MEMSET_STD=y`
|
||||
|
||||
### Default LVGL display refresh period
|
||||
|
||||
This setting can improve subjective performance during screen transitions (scrolling, etc.).
|
||||
|
||||
LVGL8
|
||||
* `CONFIG_LV_DISP_DEF_REFR_PERIOD=10`
|
||||
|
||||
LVGL9
|
||||
* `CONFIG_LV_DEF_REFR_PERIOD=10`
|
||||
|
||||
## 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" />
|
||||
|
||||
Default settings:
|
||||
* BSP example `display_lvgl_demos` with `ws_7inch` component
|
||||
* LCD: 7" 800x480
|
||||
* Intarface: 16bit parallel Intel 8080
|
||||
* LCD IO clock: 20 MHz
|
||||
* LVGL buffer size: 800 x 50
|
||||
* LVGL double buffer: YES
|
||||
* Optimization: Debug
|
||||
* CPU frequency: 160 MHz
|
||||
* Flash frequency: 80 MHz
|
||||
* Flash mode: DIO
|
||||
* LVGL display refresh period: 30 ms
|
||||
|
||||
#### Internal RAM with DMA
|
||||
|
||||
| Average FPS | Weighted FPS | Changed settings |
|
||||
| :---: | :---: | ---------------- |
|
||||
| 17 | 32 | Default |
|
||||
| 18 | 36 | + Optimization: Performance (`CONFIG_COMPILER_OPTIMIZATION_PERF=y`) |
|
||||
| 21 | 46 | + CPU frequency: 240 MHz (`CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y`) |
|
||||
| 26 | 56 | + Flash frequency: 120 MHz, Flash mode: QIO (`CONFIG_ESPTOOLPY_FLASHFREQ_120M=y` + `CONFIG_ESPTOOLPY_FLASHMODE_QIO=y`) |
|
||||
| 28 | **60** | + LVGL fast memory enabled (`CONFIG_LV_ATTRIBUTE_FAST_MEM_USE_IRAM=y`) |
|
||||
| 41 | 55 | + (`CONFIG_LV_DISP_DEF_REFR_PERIOD=10`) |
|
||||
|
||||
#### PSRAM (QUAD) without DMA
|
||||
|
||||
Default changes:
|
||||
* LCD IO clock: 2 MHz
|
||||
* PSRAM frequency: 80 MHz
|
||||
|
||||
| Average FPS | Weighted FPS | Changed settings |
|
||||
| :---: | :---: | ---------------- |
|
||||
| 11 | 7 | Default |
|
||||
| 11 | 7 | + Optimization: Performance (`CONFIG_COMPILER_OPTIMIZATION_PERF=y`) |
|
||||
| 12 | 8 | + CPU frequency: 240 MHz (`CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ_240=y`) |
|
||||
| 12 | 9 | + Flash frequency: 120 MHz, PSRAM frequency: 120 MHz, Flash mode: QIO (`CONFIG_ESPTOOLPY_FLASHFREQ_120M=y` + `CONFIG_SPIRAM_SPEED_120M=y` + `CONFIG_ESPTOOLPY_FLASHMODE_QIO=y`) |
|
||||
| 12 | 9 | + LVGL fast memory enabled (`CONFIG_LV_ATTRIBUTE_FAST_MEM_USE_IRAM=y`) |
|
||||
| 12 | 8 | + LVGL buffer size: 800 x 480 |
|
||||
| 26 | 8 | + (`CONFIG_LV_DISP_DEF_REFR_PERIOD=10`) |
|
||||
| 31 | 23 | + LCD clock: 10 MHz [^1] |
|
||||
|
||||
[^1]: This is not working in default and sometimes in fast changes on screen is not working properly.
|
||||
|
||||
## 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.
|
||||
|
||||
Other configuration options not covered in this document are:
|
||||
* Hardware interfaces, color depth, screen definition (size), clocks, LCD controller and more.
|
||||
* Complexity of the graphical application (number of LVGL objects and their styles).
|
||||
@@ -0,0 +1,4 @@
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(i2c_oled)
|
||||
@@ -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,5 @@
|
||||
idf_component_register(
|
||||
SRCS "i2c_oled_example_main.c" "lvgl_demo_ui.c"
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES driver
|
||||
)
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -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.
|
||||
@@ -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
managed_components/espressif__esp_lvgl_port/examples/rgb_lcd/main/images/.gitignore
vendored
Normal file
1
managed_components/espressif__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 |
@@ -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();
|
||||
}
|
||||
@@ -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,
|
||||
|
@@ -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
|
||||
@@ -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(touchscreen)
|
||||
@@ -0,0 +1,19 @@
|
||||
# ESP LVGL Touch Screen Example
|
||||
|
||||
Very simple example for demonstration of initialization and usage of the `esp_lvgl_port` component. 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 [ESP-BOX](https://github.com/espressif/esp-bsp/tree/master/esp-box) 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 [ESP-BOX](https://github.com/espressif/esp-bsp/tree/master/esp-box) 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.
|
||||
@@ -0,0 +1,5 @@
|
||||
idf_component_register(SRCS "main.c"
|
||||
INCLUDE_DIRS ".")
|
||||
|
||||
lvgl_port_create_c_image("images/esp_logo.png" "images/" "ARGB8888" "NONE")
|
||||
lvgl_port_add_images(${COMPONENT_LIB} "images/")
|
||||
@@ -0,0 +1,8 @@
|
||||
dependencies:
|
||||
idf: ">=4.4"
|
||||
esp_lcd_touch_tt21100:
|
||||
version: "^1"
|
||||
override_path: "../../../../lcd_touch/esp_lcd_touch_tt21100/"
|
||||
esp_lvgl_port:
|
||||
version: "*"
|
||||
override_path: "../../../"
|
||||
1
managed_components/espressif__esp_lvgl_port/examples/touchscreen/main/images/.gitignore
vendored
Normal file
1
managed_components/espressif__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 |
@@ -0,0 +1,277 @@
|
||||
/*
|
||||
* 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 "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"
|
||||
|
||||
/* 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 const char *TAG = "EXAMPLE";
|
||||
|
||||
// LVGL image declare
|
||||
LV_IMG_DECLARE(esp_logo)
|
||||
|
||||
/* 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_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_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,
|
||||
},
|
||||
};
|
||||
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_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_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,
|
||||
.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 = 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 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 .... */
|
||||
|
||||
/* 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);
|
||||
|
||||
/* Task unlock */
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
|
||||
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 */
|
||||
app_main_display();
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
CONFIG_IDF_TARGET="esp32s3"
|
||||
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
|
||||
CONFIG_LV_COLOR_16_SWAP=y
|
||||
@@ -0,0 +1,12 @@
|
||||
dependencies:
|
||||
idf: '>=4.4'
|
||||
lvgl/lvgl:
|
||||
public: true
|
||||
version: '>=8,<10'
|
||||
description: ESP LVGL port
|
||||
repository: git://github.com/espressif/esp-bsp.git
|
||||
repository_info:
|
||||
commit_sha: 76cc90336b34955fc76b510557b837e963b6a9e9
|
||||
path: components/esp_lvgl_port
|
||||
url: https://github.com/espressif/esp-bsp/tree/master/components/esp_lvgl_port
|
||||
version: 2.6.0
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifdef __has_include
|
||||
#if __has_include("lvgl.h")
|
||||
#ifndef LV_LVGL_H_INCLUDE_SIMPLE
|
||||
#define LV_LVGL_H_INCLUDE_SIMPLE
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(LV_LVGL_H_INCLUDE_SIMPLE)
|
||||
#include "lvgl.h"
|
||||
#else
|
||||
#include "lvgl/lvgl.h"
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef LV_ATTRIBUTE_MEM_ALIGN
|
||||
#define LV_ATTRIBUTE_MEM_ALIGN
|
||||
#endif
|
||||
|
||||
#ifndef LV_ATTRIBUTE_IMG_IMG_CURSOR
|
||||
#define LV_ATTRIBUTE_IMG_IMG_CURSOR
|
||||
#endif
|
||||
|
||||
const LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_IMG_CURSOR uint8_t img_cursor_map[] = {
|
||||
#if LV_COLOR_DEPTH == 1 || LV_COLOR_DEPTH == 8
|
||||
/*Pixel format: Alpha 8 bit, Red: 3 bit, Green: 3 bit, Blue: 2 bit*/
|
||||
0x00, 0xb2, 0x00, 0xcc, 0x00, 0x71, 0x00, 0x3a, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0xcc, 0x00, 0xff, 0x00, 0xfc, 0x00, 0xe4, 0x00, 0xae, 0x00, 0x6b, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x71, 0x00, 0xfc, 0x6d, 0xf1, 0xb6, 0xfc, 0x49, 0xf9, 0x24, 0xfe, 0x00, 0xf4, 0x00, 0xb9, 0x00, 0x5c, 0x00, 0x2e, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x3a, 0x00, 0xe4, 0xb6, 0xfc, 0xff, 0xff, 0xff, 0xf9, 0xdb, 0xfe, 0x6e, 0xf3, 0x25, 0xfe, 0x00, 0xf8, 0x00, 0xd8, 0x00, 0x9c, 0x00, 0x51, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x0c, 0x00, 0xae, 0x49, 0xf9, 0xff, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfa, 0xb7, 0xf0, 0x6e, 0xfb, 0x25, 0xf9, 0x00, 0xff, 0x00, 0xe8, 0x00, 0x9e, 0x00, 0x4a, 0x00, 0x21, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x6b, 0x24, 0xfe, 0xdb, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xff, 0xf9, 0xb7, 0xf6, 0x49, 0xf5, 0x24, 0xff, 0x00, 0xf0, 0x00, 0xcb, 0x00, 0x88, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x13, 0x00, 0xf4, 0x6e, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf5, 0x92, 0xf5, 0x24, 0xfd, 0x00, 0xff, 0x00, 0xed, 0x00, 0x02, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x25, 0xfe, 0xff, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf4, 0x92, 0xfc, 0x24, 0xfd, 0x00, 0xe7, 0x00, 0x78, 0x00, 0x01, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x00, 0xf8, 0xb7, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdb, 0xf9, 0x25, 0xfd, 0x00, 0xee, 0x00, 0x9d, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0xd7, 0x6e, 0xfb, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdb, 0xf9, 0x25, 0xfd, 0x00, 0xdd, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x9c, 0x25, 0xf9, 0xff, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdb, 0xfa, 0x25, 0xfc, 0x00, 0xdd, 0x00, 0x2c, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x00, 0xff, 0xb7, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdb, 0xfa, 0x25, 0xfc, 0x00, 0xc2, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0xe8, 0x49, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xdb, 0xf9, 0xdb, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdb, 0xfa, 0x25, 0xfd, 0x00, 0xdd, 0x00, 0x2c, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x24, 0xff, 0xff, 0xf5, 0xff, 0xf4, 0x25, 0xfd, 0x25, 0xfd, 0xdb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdb, 0xfa, 0x25, 0xfc, 0x00, 0xc2, 0x00, 0x2c,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0xf0, 0x92, 0xf5, 0x92, 0xfc, 0x00, 0xee, 0x00, 0xdd, 0x25, 0xfc, 0xdb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdb, 0xef, 0x00, 0xff, 0x00, 0xd7,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0xcb, 0x24, 0xfd, 0x24, 0xfd, 0x00, 0x9d, 0x00, 0x37, 0x00, 0xdd, 0x25, 0xfd, 0xdb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x92, 0xf2, 0x00, 0xff, 0x00, 0xb6,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x88, 0x00, 0xff, 0x00, 0xe7, 0x00, 0x18, 0x00, 0x00, 0x00, 0x2c, 0x00, 0xc2, 0x25, 0xfc, 0xdb, 0xfa, 0xff, 0xff, 0xff, 0xfc, 0x92, 0xf8, 0x00, 0xfe, 0x00, 0x9d, 0x00, 0x16,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0xed, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x2c, 0x00, 0xdd, 0x25, 0xfc, 0xdb, 0xef, 0x92, 0xf2, 0x00, 0xfe, 0x00, 0xb9, 0x00, 0x16, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0xc2, 0x00, 0xff, 0x00, 0xff, 0x00, 0x9d, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x2c, 0x00, 0xd7, 0x00, 0xb6, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
#endif
|
||||
#if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP == 0
|
||||
/*Pixel format: Alpha 8 bit, Red: 5 bit, Green: 6 bit, Blue: 5 bit*/
|
||||
0x00, 0x00, 0xb2, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x71, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xcc, 0x00, 0x00, 0xff, 0x00, 0x00, 0xfc, 0x00, 0x00, 0xe4, 0x00, 0x00, 0xae, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x71, 0x00, 0x00, 0xfc, 0xeb, 0x5a, 0xf1, 0xb2, 0x94, 0xfc, 0x08, 0x42, 0xf9, 0x04, 0x21, 0xfe, 0x41, 0x08, 0xf4, 0x00, 0x00, 0xb9, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x3a, 0x00, 0x00, 0xe4, 0xb2, 0x94, 0xfc, 0xff, 0xff, 0xff, 0x3c, 0xe7, 0xf9, 0x59, 0xce, 0xfe, 0x6e, 0x73, 0xf3, 0x04, 0x21, 0xfe, 0x00, 0x00, 0xf8, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x51, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x0c, 0x00, 0x00, 0xae, 0x08, 0x42, 0xf9, 0x3c, 0xe7, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfa, 0x76, 0xb5, 0xf0, 0x8e, 0x73, 0xfb, 0x45, 0x29, 0xf9, 0x82, 0x10, 0xff, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x21, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x04, 0x21, 0xfe, 0x59, 0xce, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xbb, 0xde, 0xf9, 0x96, 0xb5, 0xf6, 0x49, 0x4a, 0xf5, 0x04, 0x21, 0xff, 0x00, 0x00, 0xf0, 0x00, 0x00, 0xcb, 0x00, 0x00, 0x88, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x41, 0x08, 0xf4, 0x6e, 0x73, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xbe, 0xf7, 0xf5, 0x10, 0x84, 0xf5, 0xa2, 0x10, 0xfd, 0x00, 0x00, 0xff, 0x00, 0x00, 0xed, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x04, 0x21, 0xfe, 0xff, 0xff, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x9e, 0xf7, 0xf4, 0xae, 0x73, 0xfc, 0xa2, 0x10, 0xfd, 0x00, 0x00, 0xe7, 0x00, 0x00, 0x78, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0xf8, 0x76, 0xb5, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x59, 0xce, 0xf9, 0x86, 0x31, 0xfd, 0x00, 0x00, 0xee, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0xd7, 0x8e, 0x73, 0xfb, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x59, 0xce, 0xf9, 0x86, 0x31, 0xfd, 0x00, 0x00, 0xdd, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x9c, 0x45, 0x29, 0xf9, 0xbb, 0xde, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x79, 0xce, 0xfa, 0x65, 0x29, 0xfc, 0x20, 0x00, 0xdd, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x82, 0x10, 0xff, 0x96, 0xb5, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x96, 0xb5, 0xfa, 0x65, 0x29, 0xfc, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0xe8, 0x49, 0x4a, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x59, 0xce, 0xf9, 0x59, 0xce, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x79, 0xce, 0xfa, 0x65, 0x29, 0xfd, 0x20, 0x00, 0xdd, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x04, 0x21, 0xff, 0xbe, 0xf7, 0xf5, 0x9e, 0xf7, 0xf4, 0x86, 0x31, 0xfd, 0x86, 0x31, 0xfd, 0x79, 0xce, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x96, 0xb5, 0xfa, 0x65, 0x29, 0xfc, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x2c,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0xf0, 0x10, 0x84, 0xf5, 0xae, 0x73, 0xfc, 0x00, 0x00, 0xee, 0x00, 0x00, 0xdd, 0x65, 0x29, 0xfc, 0x96, 0xb5, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x96, 0xb5, 0xef, 0x00, 0x00, 0xff, 0x00, 0x00, 0xd7,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0xcb, 0xa2, 0x10, 0xfd, 0xa2, 0x10, 0xfd, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x37, 0x20, 0x00, 0xdd, 0x65, 0x29, 0xfd, 0x79, 0xce, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x51, 0x8c, 0xf2, 0x00, 0x00, 0xff, 0x00, 0x00, 0xb6,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x88, 0x00, 0x00, 0xff, 0x00, 0x00, 0xe7, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0xc2, 0x65, 0x29, 0xfc, 0x96, 0xb5, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xaf, 0x7b, 0xf8, 0x41, 0x08, 0xfe, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x16,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0xed, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x2c, 0x20, 0x00, 0xdd, 0x65, 0x29, 0xfc, 0x96, 0xb5, 0xef, 0x51, 0x8c, 0xf2, 0x41, 0x08, 0xfe, 0x00, 0x00, 0xb9, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0xc2, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x2c, 0x00, 0x00, 0xd7, 0x00, 0x00, 0xb6, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
#endif
|
||||
#if LV_COLOR_DEPTH == 16 && LV_COLOR_16_SWAP != 0
|
||||
/*Pixel format: Alpha 8 bit, Red: 5 bit, Green: 6 bit, Blue: 5 bit BUT the 2 color bytes are swapped*/
|
||||
0x00, 0x00, 0xb2, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x71, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0xcc, 0x00, 0x00, 0xff, 0x00, 0x00, 0xfc, 0x00, 0x00, 0xe4, 0x00, 0x00, 0xae, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x71, 0x00, 0x00, 0xfc, 0x5a, 0xeb, 0xf1, 0x94, 0xb2, 0xfc, 0x42, 0x08, 0xf9, 0x21, 0x04, 0xfe, 0x08, 0x41, 0xf4, 0x00, 0x00, 0xb9, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x3a, 0x00, 0x00, 0xe4, 0x94, 0xb2, 0xfc, 0xff, 0xff, 0xff, 0xe7, 0x3c, 0xf9, 0xce, 0x59, 0xfe, 0x73, 0x6e, 0xf3, 0x21, 0x04, 0xfe, 0x00, 0x00, 0xf8, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x51, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x0c, 0x00, 0x00, 0xae, 0x42, 0x08, 0xf9, 0xe7, 0x3c, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfa, 0xb5, 0x76, 0xf0, 0x73, 0x8e, 0xfb, 0x29, 0x45, 0xf9, 0x10, 0x82, 0xff, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x21, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x21, 0x04, 0xfe, 0xce, 0x59, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xde, 0xbb, 0xf9, 0xb5, 0x96, 0xf6, 0x4a, 0x49, 0xf5, 0x21, 0x04, 0xff, 0x00, 0x00, 0xf0, 0x00, 0x00, 0xcb, 0x00, 0x00, 0x88, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x08, 0x41, 0xf4, 0x73, 0x6e, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0xbe, 0xf5, 0x84, 0x10, 0xf5, 0x10, 0xa2, 0xfd, 0x00, 0x00, 0xff, 0x00, 0x00, 0xed, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x21, 0x04, 0xfe, 0xff, 0xff, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf7, 0x9e, 0xf4, 0x73, 0xae, 0xfc, 0x10, 0xa2, 0xfd, 0x00, 0x00, 0xe7, 0x00, 0x00, 0x78, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0xf8, 0xb5, 0x76, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xce, 0x59, 0xf9, 0x31, 0x86, 0xfd, 0x00, 0x00, 0xee, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0xd7, 0x73, 0x8e, 0xfb, 0xff, 0xff, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xce, 0x59, 0xf9, 0x31, 0x86, 0xfd, 0x00, 0x00, 0xdd, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x9c, 0x29, 0x45, 0xf9, 0xde, 0xbb, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xce, 0x79, 0xfa, 0x29, 0x65, 0xfc, 0x00, 0x20, 0xdd, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x10, 0x82, 0xff, 0xb5, 0x96, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb5, 0x96, 0xfa, 0x29, 0x65, 0xfc, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0xe8, 0x4a, 0x49, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xce, 0x59, 0xf9, 0xce, 0x59, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xce, 0x79, 0xfa, 0x29, 0x65, 0xfd, 0x00, 0x20, 0xdd, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x21, 0x04, 0xff, 0xf7, 0xbe, 0xf5, 0xf7, 0x9e, 0xf4, 0x31, 0x86, 0xfd, 0x31, 0x86, 0xfd, 0xce, 0x79, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb5, 0x96, 0xfa, 0x29, 0x65, 0xfc, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x2c,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0xf0, 0x84, 0x10, 0xf5, 0x73, 0xae, 0xfc, 0x00, 0x00, 0xee, 0x00, 0x00, 0xdd, 0x29, 0x65, 0xfc, 0xb5, 0x96, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb5, 0x96, 0xef, 0x00, 0x00, 0xff, 0x00, 0x00, 0xd7,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0xcb, 0x10, 0xa2, 0xfd, 0x10, 0xa2, 0xfd, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x37, 0x00, 0x20, 0xdd, 0x29, 0x65, 0xfd, 0xce, 0x79, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x8c, 0x51, 0xf2, 0x00, 0x00, 0xff, 0x00, 0x00, 0xb6,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x88, 0x00, 0x00, 0xff, 0x00, 0x00, 0xe7, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0xc2, 0x29, 0x65, 0xfc, 0xb5, 0x96, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0x7b, 0xaf, 0xf8, 0x08, 0x41, 0xfe, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x16,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0xed, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x2c, 0x00, 0x20, 0xdd, 0x29, 0x65, 0xfc, 0xb5, 0x96, 0xef, 0x8c, 0x51, 0xf2, 0x08, 0x41, 0xfe, 0x00, 0x00, 0xb9, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0xc2, 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x2c, 0x00, 0x00, 0xd7, 0x00, 0x00, 0xb6, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
#endif
|
||||
#if LV_COLOR_DEPTH == 32
|
||||
0x00, 0x00, 0x00, 0xb2, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0xae, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0xfc, 0x5b, 0x5b, 0x5b, 0xf1, 0x93, 0x93, 0x93, 0xfc, 0x41, 0x41, 0x41, 0xf9, 0x1e, 0x1e, 0x1e, 0xfe, 0x06, 0x06, 0x06, 0xf4, 0x00, 0x00, 0x00, 0xb9, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0xe4, 0x93, 0x93, 0x93, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xe3, 0xe3, 0xe3, 0xf9, 0xc8, 0xc8, 0xc8, 0xfe, 0x6c, 0x6c, 0x6c, 0xf3, 0x20, 0x20, 0x20, 0xfe, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0xae, 0x41, 0x41, 0x41, 0xf9, 0xe3, 0xe3, 0xe3, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xfb, 0xfb, 0xfa, 0xac, 0xac, 0xac, 0xf0, 0x6f, 0x6f, 0x6f, 0xfb, 0x26, 0x26, 0x26, 0xf9, 0x0f, 0x0f, 0x0f, 0xff, 0x00, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x1e, 0x1e, 0x1e, 0xfe, 0xc8, 0xc8, 0xc8, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfc, 0xfb, 0xd4, 0xd4, 0xd4, 0xf9, 0xae, 0xae, 0xae, 0xf6, 0x48, 0x48, 0x48, 0xf5, 0x1f, 0x1f, 0x1f, 0xff, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0xcb, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x06, 0x06, 0x06, 0xf4, 0x6c, 0x6c, 0x6c, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xf3, 0xf3, 0xf5, 0x7e, 0x7e, 0x7e, 0xf5, 0x12, 0x12, 0x12, 0xfd, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xed, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x20, 0x20, 0x20, 0xfe, 0xfb, 0xfb, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xef, 0xef, 0xf4, 0x73, 0x73, 0x73, 0xfc, 0x12, 0x12, 0x12, 0xfd, 0x00, 0x00, 0x00, 0xe7, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0xf8, 0xac, 0xac, 0xac, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc8, 0xc8, 0xc8, 0xf9, 0x2e, 0x2e, 0x2e, 0xfd, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0xd7, 0x6f, 0x6f, 0x6f, 0xfb, 0xfc, 0xfc, 0xfc, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc8, 0xc8, 0xc8, 0xf9, 0x2e, 0x2e, 0x2e, 0xfd, 0x00, 0x00, 0x00, 0xdd, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x9c, 0x26, 0x26, 0x26, 0xf9, 0xd4, 0xd4, 0xd4, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xca, 0xca, 0xca, 0xfa, 0x2b, 0x2b, 0x2b, 0xfc, 0x03, 0x03, 0x03, 0xdd, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x0f, 0x0f, 0x0f, 0xff, 0xae, 0xae, 0xae, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb0, 0xb0, 0xb0, 0xfa, 0x2b, 0x2b, 0x2b, 0xfc, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xe8, 0x48, 0x48, 0x48, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc8, 0xc8, 0xc8, 0xf9, 0xc8, 0xc8, 0xc8, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xca, 0xca, 0xca, 0xfa, 0x2b, 0x2b, 0x2b, 0xfd, 0x03, 0x03, 0x03, 0xdd, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x1f, 0x1f, 0x1f, 0xff, 0xf3, 0xf3, 0xf3, 0xf5, 0xf0, 0xf0, 0xf0, 0xf4, 0x2e, 0x2e, 0x2e, 0xfd, 0x2e, 0x2e, 0x2e, 0xfd, 0xca, 0xca, 0xca, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb0, 0xb0, 0xb0, 0xfa, 0x2b, 0x2b, 0x2b, 0xfc, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, 0x2c,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0xf0, 0x7e, 0x7e, 0x7e, 0xf5, 0x73, 0x73, 0x73, 0xfc, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xdd, 0x2b, 0x2b, 0x2b, 0xfc, 0xb0, 0xb0, 0xb0, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb1, 0xb1, 0xb1, 0xef, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xd7,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0xcb, 0x12, 0x12, 0x12, 0xfd, 0x12, 0x12, 0x12, 0xfd, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x37, 0x03, 0x03, 0x03, 0xdd, 0x2b, 0x2b, 0x2b, 0xfd, 0xca, 0xca, 0xca, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfc, 0xfc, 0x86, 0x86, 0x86, 0xf2, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xb6,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xe7, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0xc2, 0x2b, 0x2b, 0x2b, 0xfc, 0xb0, 0xb0, 0xb0, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfc, 0xfc, 0x74, 0x74, 0x74, 0xf8, 0x06, 0x06, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x16,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0xed, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2c, 0x03, 0x03, 0x03, 0xdd, 0x2b, 0x2b, 0x2b, 0xfc, 0xb1, 0xb1, 0xb1, 0xef, 0x86, 0x86, 0x86, 0xf2, 0x06, 0x06, 0x06, 0xfe, 0x00, 0x00, 0x00, 0xb9, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0xd7, 0x00, 0x00, 0x00, 0xb6, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
#endif
|
||||
};
|
||||
|
||||
const lv_img_dsc_t img_cursor = {
|
||||
.header.cf = LV_IMG_CF_TRUE_COLOR_ALPHA,
|
||||
.header.always_zero = 0,
|
||||
.header.reserved = 0,
|
||||
.header.w = 20,
|
||||
.header.h = 20,
|
||||
.data_size = 400 * LV_IMG_PX_SIZE_ALPHA_BYTE,
|
||||
.data = img_cursor_map,
|
||||
};
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifdef __has_include
|
||||
#if __has_include("lvgl.h")
|
||||
#ifndef LV_LVGL_H_INCLUDE_SIMPLE
|
||||
#define LV_LVGL_H_INCLUDE_SIMPLE
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(LV_LVGL_H_INCLUDE_SIMPLE)
|
||||
#include "lvgl.h"
|
||||
#else
|
||||
#include "lvgl/lvgl.h"
|
||||
#endif
|
||||
|
||||
|
||||
#ifndef LV_ATTRIBUTE_MEM_ALIGN
|
||||
#define LV_ATTRIBUTE_MEM_ALIGN
|
||||
#endif
|
||||
|
||||
#ifndef LV_ATTRIBUTE_IMG_DUST
|
||||
#define LV_ATTRIBUTE_IMG_DUST
|
||||
#endif
|
||||
|
||||
static const
|
||||
LV_ATTRIBUTE_MEM_ALIGN LV_ATTRIBUTE_LARGE_CONST LV_ATTRIBUTE_IMG_DUST
|
||||
uint8_t img_cursor_20px_map[] = {
|
||||
|
||||
0x00, 0x00, 0x00, 0xb2, 0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0xcc, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xfc, 0x00, 0x00, 0x00, 0xe4, 0x00, 0x00, 0x00, 0xae, 0x00, 0x00, 0x00, 0x6b, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x71, 0x00, 0x00, 0x00, 0xfc, 0x5b, 0x5b, 0x5b, 0xf1, 0x93, 0x93, 0x93, 0xfc, 0x41, 0x41, 0x41, 0xf9, 0x1e, 0x1e, 0x1e, 0xfe, 0x06, 0x06, 0x06, 0xf4, 0x00, 0x00, 0x00, 0xb9, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0x2e, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x3a, 0x00, 0x00, 0x00, 0xe4, 0x93, 0x93, 0x93, 0xfc, 0xff, 0xff, 0xff, 0xff, 0xe3, 0xe3, 0xe3, 0xf9, 0xc8, 0xc8, 0xc8, 0xfe, 0x6c, 0x6c, 0x6c, 0xf3, 0x20, 0x20, 0x20, 0xfe, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x00, 0x00, 0xd8, 0x00, 0x00, 0x00, 0x9c, 0x00, 0x00, 0x00, 0x51, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00, 0xae, 0x41, 0x41, 0x41, 0xf9, 0xe3, 0xe3, 0xe3, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfb, 0xfb, 0xfb, 0xfa, 0xac, 0xac, 0xac, 0xf0, 0x6f, 0x6f, 0x6f, 0xfb, 0x26, 0x26, 0x26, 0xf9, 0x0f, 0x0f, 0x0f, 0xff, 0x00, 0x00, 0x00, 0xe8, 0x00, 0x00, 0x00, 0x9e, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6b, 0x1e, 0x1e, 0x1e, 0xfe, 0xc8, 0xc8, 0xc8, 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfc, 0xfb, 0xd4, 0xd4, 0xd4, 0xf9, 0xae, 0xae, 0xae, 0xf6, 0x48, 0x48, 0x48, 0xf5, 0x1f, 0x1f, 0x1f, 0xff, 0x00, 0x00, 0x00, 0xf0, 0x00, 0x00, 0x00, 0xcb, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0x06, 0x06, 0x06, 0xf4, 0x6c, 0x6c, 0x6c, 0xf3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf2, 0xf2, 0xf2, 0xf5, 0x7e, 0x7e, 0x7e, 0xf5, 0x12, 0x12, 0x12, 0xfd, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xed, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xb9, 0x20, 0x20, 0x20, 0xfe, 0xfb, 0xfb, 0xfb, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xef, 0xef, 0xef, 0xf4, 0x73, 0x73, 0x73, 0xfc, 0x12, 0x12, 0x12, 0xfd, 0x00, 0x00, 0x00, 0xe7, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 0xf8, 0xac, 0xac, 0xac, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc8, 0xc8, 0xc8, 0xf9, 0x2e, 0x2e, 0x2e, 0xfd, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2d, 0x00, 0x00, 0x00, 0xd7, 0x6f, 0x6f, 0x6f, 0xfb, 0xfc, 0xfc, 0xfc, 0xfb, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc8, 0xc8, 0xc8, 0xf9, 0x2e, 0x2e, 0x2e, 0xfd, 0x00, 0x00, 0x00, 0xdd, 0x00, 0x00, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x00, 0x00, 0x9c, 0x26, 0x26, 0x26, 0xf9, 0xd4, 0xd4, 0xd4, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xca, 0xca, 0xca, 0xfa, 0x2b, 0x2b, 0x2b, 0xfc, 0x03, 0x03, 0x03, 0xdd, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x52, 0x0f, 0x0f, 0x0f, 0xff, 0xae, 0xae, 0xae, 0xf6, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb0, 0xb0, 0xb0, 0xfa, 0x2b, 0x2b, 0x2b, 0xfc, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0xe8, 0x48, 0x48, 0x48, 0xf5, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc8, 0xc8, 0xc8, 0xf9, 0xc8, 0xc8, 0xc8, 0xf9, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xca, 0xca, 0xca, 0xfa, 0x2b, 0x2b, 0x2b, 0xfd, 0x03, 0x03, 0x03, 0xdd, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0x01,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9e, 0x1f, 0x1f, 0x1f, 0xff, 0xf2, 0xf2, 0xf2, 0xf5, 0xf0, 0xf0, 0xf0, 0xf4, 0x2e, 0x2e, 0x2e, 0xfd, 0x2e, 0x2e, 0x2e, 0xfd, 0xca, 0xca, 0xca, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb0, 0xb0, 0xb0, 0xfa, 0x2b, 0x2b, 0x2b, 0xfc, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, 0x2c,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0xf0, 0x7e, 0x7e, 0x7e, 0xf5, 0x73, 0x73, 0x73, 0xfc, 0x00, 0x00, 0x00, 0xee, 0x00, 0x00, 0x00, 0xdd, 0x2b, 0x2b, 0x2b, 0xfc, 0xb0, 0xb0, 0xb0, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xb1, 0xb1, 0xb1, 0xef, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xd7,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0x00, 0x00, 0x00, 0xcb, 0x12, 0x12, 0x12, 0xfd, 0x12, 0x12, 0x12, 0xfd, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x37, 0x03, 0x03, 0x03, 0xdd, 0x2b, 0x2b, 0x2b, 0xfd, 0xca, 0xca, 0xca, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfc, 0xfc, 0x86, 0x86, 0x86, 0xf2, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xb6,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x88, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xe7, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0xc2, 0x2b, 0x2b, 0x2b, 0xfc, 0xb0, 0xb0, 0xb0, 0xfa, 0xff, 0xff, 0xff, 0xff, 0xfc, 0xfc, 0xfc, 0xfc, 0x74, 0x74, 0x74, 0xf8, 0x06, 0x06, 0x06, 0xfe, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x16,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, 0xed, 0x00, 0x00, 0x00, 0x79, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2c, 0x03, 0x03, 0x03, 0xdd, 0x2b, 0x2b, 0x2b, 0xfc, 0xb1, 0xb1, 0xb1, 0xef, 0x86, 0x86, 0x86, 0xf2, 0x06, 0x06, 0x06, 0xfe, 0x00, 0x00, 0x00, 0xb9, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0xc2, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x9d, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x2c, 0x00, 0x00, 0x00, 0xd7, 0x00, 0x00, 0x00, 0xb6, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
|
||||
};
|
||||
|
||||
const lv_img_dsc_t img_cursor = {
|
||||
.header.magic = LV_IMAGE_HEADER_MAGIC,
|
||||
.header.cf = LV_COLOR_FORMAT_ARGB8888,
|
||||
.header.flags = 0,
|
||||
.header.w = 20,
|
||||
.header.h = 20,
|
||||
.header.stride = 80,
|
||||
.data_size = 1600,
|
||||
.data = img_cursor_20px_map,
|
||||
};
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief ESP LVGL port
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "lvgl.h"
|
||||
#include "esp_lvgl_port_disp.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"
|
||||
|
||||
#if LVGL_VERSION_MAJOR == 8
|
||||
#include "esp_lvgl_port_compatibility.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief LVGL Port task event type
|
||||
*/
|
||||
typedef enum {
|
||||
LVGL_PORT_EVENT_DISPLAY = 0x01,
|
||||
LVGL_PORT_EVENT_TOUCH = 0x02,
|
||||
LVGL_PORT_EVENT_USER = 0x80,
|
||||
} 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
|
||||
*/
|
||||
typedef struct {
|
||||
int task_priority; /*!< LVGL task priority */
|
||||
int task_stack; /*!< LVGL task stack size */
|
||||
int task_affinity; /*!< LVGL task pinned to core (-1 is no affinity) */
|
||||
int task_max_sleep_ms; /*!< Maximum sleep in LVGL task */
|
||||
int timer_period_ms; /*!< LVGL timer tick period in ms */
|
||||
} lvgl_port_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief LVGL port configuration structure
|
||||
*
|
||||
*/
|
||||
#define ESP_LVGL_PORT_INIT_CONFIG() \
|
||||
{ \
|
||||
.task_priority = 4, \
|
||||
.task_stack = 7168, \
|
||||
.task_affinity = -1, \
|
||||
.task_max_sleep_ms = 500, \
|
||||
.timer_period_ms = 5, \
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Initialize LVGL portation
|
||||
*
|
||||
* @note This function initialize LVGL and create timer and task for LVGL right working.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_ARG if some of the create_args are not valid
|
||||
* - ESP_ERR_INVALID_STATE if esp_timer library is not initialized yet
|
||||
* - ESP_ERR_NO_MEM if memory allocation fails
|
||||
*/
|
||||
esp_err_t lvgl_port_init(const lvgl_port_cfg_t *cfg);
|
||||
|
||||
/**
|
||||
* @brief Deinitialize LVGL portation
|
||||
*
|
||||
* @note This function deinitializes LVGL and stops the task if running.
|
||||
* Some deinitialization will be done after the task will be stopped.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_TIMEOUT when stopping the LVGL task times out
|
||||
*/
|
||||
esp_err_t lvgl_port_deinit(void);
|
||||
|
||||
/**
|
||||
* @brief Take LVGL mutex
|
||||
*
|
||||
* @param timeout_ms Timeout in [ms]. 0 will block indefinitely.
|
||||
* @return
|
||||
* - true Mutex was taken
|
||||
* - false Mutex was NOT taken
|
||||
*/
|
||||
bool lvgl_port_lock(uint32_t timeout_ms);
|
||||
|
||||
/**
|
||||
* @brief Give LVGL mutex
|
||||
*
|
||||
*/
|
||||
void lvgl_port_unlock(void);
|
||||
|
||||
/**
|
||||
* @brief Notify LVGL, that data was flushed to LCD display
|
||||
*
|
||||
* @note It should be used only when not called inside LVGL port (more in README).
|
||||
*
|
||||
* @param disp LVGL display handle (returned from lvgl_port_add_disp)
|
||||
*/
|
||||
void lvgl_port_flush_ready(lv_display_t *disp);
|
||||
|
||||
/**
|
||||
* @brief Stop lvgl timer
|
||||
*
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if the timer is not running
|
||||
*/
|
||||
esp_err_t lvgl_port_stop(void);
|
||||
|
||||
/**
|
||||
* @brief Resume lvgl timer
|
||||
*
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_INVALID_STATE if the timer is not running
|
||||
*/
|
||||
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 parameter is not used, keep for backwards compatibility
|
||||
* @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
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief ESP LVGL port button
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "lvgl.h"
|
||||
|
||||
#if __has_include ("iot_button.h")
|
||||
#include "iot_button.h"
|
||||
#define ESP_LVGL_PORT_BUTTON_COMPONENT 1
|
||||
#endif
|
||||
|
||||
#if LVGL_VERSION_MAJOR == 8
|
||||
#include "esp_lvgl_port_compatibility.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef ESP_LVGL_PORT_BUTTON_COMPONENT
|
||||
/**
|
||||
* @brief Configuration of the navigation buttons structure
|
||||
*/
|
||||
typedef struct {
|
||||
lv_display_t *disp; /*!< LVGL display handle (returned from lvgl_port_add_disp) */
|
||||
#if BUTTON_VER_MAJOR < 4
|
||||
const button_config_t *button_prev; /*!< Navigation button for previous */
|
||||
const button_config_t *button_next; /*!< Navigation button for next */
|
||||
const button_config_t *button_enter; /*!< Navigation button for enter */
|
||||
#else
|
||||
button_handle_t button_prev; /*!< Handle for navigation button for previous */
|
||||
button_handle_t button_next; /*!< Handle for navigation button for next */
|
||||
button_handle_t button_enter; /*!< Handle for navigation button for enter */
|
||||
#endif
|
||||
} lvgl_port_nav_btns_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Add buttons as an input device
|
||||
*
|
||||
* @note Allocated memory in this function is not free in deinit. You must call lvgl_port_remove_navigation_buttons for free all memory!
|
||||
*
|
||||
* @param buttons_cfg Buttons configuration structure
|
||||
* @return Pointer to LVGL buttons input device or NULL when error occurred
|
||||
*/
|
||||
lv_indev_t *lvgl_port_add_navigation_buttons(const lvgl_port_nav_btns_cfg_t *buttons_cfg);
|
||||
|
||||
/**
|
||||
* @brief Remove selected buttons from input devices
|
||||
*
|
||||
* @note Free all memory used for this input device.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t lvgl_port_remove_navigation_buttons(lv_indev_t *buttons);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief ESP LVGL port compatibility
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Backward compatibility with LVGL 8
|
||||
*/
|
||||
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
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief ESP LVGL port display
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_lcd_panel_ops.h"
|
||||
#include "lvgl.h"
|
||||
|
||||
#if LVGL_VERSION_MAJOR == 8
|
||||
#include "esp_lvgl_port_compatibility.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Rotation configuration
|
||||
*/
|
||||
typedef struct {
|
||||
bool swap_xy; /*!< LCD Screen swapped X and Y (in esp_lcd driver) */
|
||||
bool mirror_x; /*!< LCD Screen mirrored X (in esp_lcd driver) */
|
||||
bool mirror_y; /*!< LCD Screen mirrored Y (in esp_lcd driver) */
|
||||
} lvgl_port_rotation_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Configuration display structure
|
||||
*/
|
||||
typedef struct {
|
||||
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 control_handle; /*!< LCD panel control handle */
|
||||
|
||||
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 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 (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 {
|
||||
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 sw_rotate: 1; /*!< Use software rotation (slower) or PPA if available */
|
||||
#if LVGL_VERSION_MAJOR >= 9
|
||||
unsigned int swap_bytes: 1; /*!< Swap bytes in RGB565 (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;
|
||||
} lvgl_port_display_cfg_t;
|
||||
|
||||
/**
|
||||
* @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!
|
||||
*
|
||||
* @param disp_cfg Display configuration structure
|
||||
* @return Pointer to LVGL display or NULL when error occurred
|
||||
*/
|
||||
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
|
||||
*
|
||||
* @note Free all memory used for this display.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t lvgl_port_remove_disp(lv_display_t *disp);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief ESP LVGL port knob
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "lvgl.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_button.h"
|
||||
#define ESP_LVGL_PORT_KNOB_COMPONENT 1
|
||||
#endif
|
||||
|
||||
#if LVGL_VERSION_MAJOR == 8
|
||||
#include "esp_lvgl_port_compatibility.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef ESP_LVGL_PORT_KNOB_COMPONENT
|
||||
/**
|
||||
* @brief Configuration of the encoder structure
|
||||
*/
|
||||
typedef struct {
|
||||
lv_display_t *disp; /*!< LVGL display handle (returned from lvgl_port_add_disp) */
|
||||
const knob_config_t *encoder_a_b; /*!< Encoder knob configuration */
|
||||
#if BUTTON_VER_MAJOR < 4
|
||||
const button_config_t *encoder_enter; /*!< Navigation button for enter */
|
||||
#else
|
||||
button_handle_t encoder_enter; /*!< Handle for enter button */
|
||||
#endif
|
||||
} lvgl_port_encoder_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Add encoder as an input device
|
||||
*
|
||||
* @note Allocated memory in this function is not free in deinit. You must call lvgl_port_remove_encoder for free all memory!
|
||||
*
|
||||
* @param encoder_cfg Encoder configuration structure
|
||||
* @return Pointer to LVGL encoder input device or NULL when error occurred
|
||||
*/
|
||||
lv_indev_t *lvgl_port_add_encoder(const lvgl_port_encoder_cfg_t *encoder_cfg);
|
||||
|
||||
/**
|
||||
* @brief Remove encoder from input devices
|
||||
*
|
||||
* @note Free all memory used for this input device.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t lvgl_port_remove_encoder(lv_indev_t *encoder);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 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
|
||||
|
||||
#ifndef LV_DRAW_SW_COLOR_BLEND_TO_RGB888
|
||||
#define LV_DRAW_SW_COLOR_BLEND_TO_RGB888(dsc, dest_px_size) \
|
||||
_lv_color_blend_to_rgb888_esp(dsc, dest_px_size)
|
||||
#endif
|
||||
|
||||
#ifndef LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_RGB565
|
||||
#define LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_RGB565(dsc) \
|
||||
_lv_rgb565_blend_normal_to_rgb565_esp(dsc)
|
||||
#endif
|
||||
|
||||
#ifndef LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_RGB888
|
||||
#define LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_RGB888(dsc, dest_px_size, src_px_size) \
|
||||
_lv_rgb888_blend_normal_to_rgb888_esp(dsc, dest_px_size, src_px_size)
|
||||
#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);
|
||||
}
|
||||
|
||||
extern int lv_color_blend_to_rgb888_esp(asm_dsc_t *asm_dsc);
|
||||
|
||||
static inline lv_result_t _lv_color_blend_to_rgb888_esp(_lv_draw_sw_blend_fill_dsc_t *dsc, uint32_t dest_px_size)
|
||||
{
|
||||
if (dest_px_size != 3) {
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
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_rgb888_esp(&asm_dsc);
|
||||
}
|
||||
|
||||
extern int lv_rgb565_blend_normal_to_rgb565_esp(asm_dsc_t *asm_dsc);
|
||||
|
||||
static inline lv_result_t _lv_rgb565_blend_normal_to_rgb565_esp(_lv_draw_sw_blend_image_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->src_buf,
|
||||
.src_stride = dsc->src_stride
|
||||
};
|
||||
|
||||
return lv_rgb565_blend_normal_to_rgb565_esp(&asm_dsc);
|
||||
}
|
||||
|
||||
extern int lv_rgb888_blend_normal_to_rgb888_esp(asm_dsc_t *asm_dsc);
|
||||
|
||||
static inline lv_result_t _lv_rgb888_blend_normal_to_rgb888_esp(_lv_draw_sw_blend_image_dsc_t *dsc, uint32_t dest_px_size, uint32_t src_px_size)
|
||||
{
|
||||
if (!(dest_px_size == 3 && src_px_size == 3)) {
|
||||
return LV_RESULT_INVALID;
|
||||
}
|
||||
|
||||
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->src_buf,
|
||||
.src_stride = dsc->src_stride
|
||||
};
|
||||
|
||||
return lv_rgb888_blend_normal_to_rgb888_esp(&asm_dsc);
|
||||
}
|
||||
|
||||
#endif // CONFIG_LV_DRAW_SW_ASM_CUSTOM
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief ESP LVGL port touch
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "lvgl.h"
|
||||
|
||||
#if __has_include ("esp_lcd_touch.h")
|
||||
#include "esp_lcd_touch.h"
|
||||
#define ESP_LVGL_PORT_TOUCH_COMPONENT 1
|
||||
#endif
|
||||
|
||||
#if LVGL_VERSION_MAJOR == 8
|
||||
#include "esp_lvgl_port_compatibility.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef ESP_LVGL_PORT_TOUCH_COMPONENT
|
||||
/**
|
||||
* @brief Configuration touch structure
|
||||
*/
|
||||
typedef struct {
|
||||
lv_display_t *disp; /*!< LVGL display handle (returned from lvgl_port_add_disp) */
|
||||
esp_lcd_touch_handle_t handle; /*!< LCD touch IO handle */
|
||||
struct {
|
||||
float x;
|
||||
float y;
|
||||
} scale; /*!< Touch scale */
|
||||
} lvgl_port_touch_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Add LCD touch as an input device
|
||||
*
|
||||
* @note Allocated memory in this function is not free in deinit. You must call lvgl_port_remove_touch for free all memory!
|
||||
*
|
||||
* @param touch_cfg Touch configuration structure
|
||||
* @return Pointer to LVGL touch input device or NULL when error occurred
|
||||
*/
|
||||
lv_indev_t *lvgl_port_add_touch(const lvgl_port_touch_cfg_t *touch_cfg);
|
||||
|
||||
/**
|
||||
* @brief Remove selected LCD touch from input devices
|
||||
*
|
||||
* @note Free all memory used for this display.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t lvgl_port_remove_touch(lv_indev_t *touch);
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief ESP LVGL port USB HID
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "lvgl.h"
|
||||
|
||||
#if __has_include ("usb/hid_host.h")
|
||||
#define ESP_LVGL_PORT_USB_HOST_HID_COMPONENT 1
|
||||
#endif
|
||||
|
||||
#if LVGL_VERSION_MAJOR == 8
|
||||
#include "esp_lvgl_port_compatibility.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#ifdef ESP_LVGL_PORT_USB_HOST_HID_COMPONENT
|
||||
/**
|
||||
* @brief Configuration of the mouse input
|
||||
*/
|
||||
typedef struct {
|
||||
lv_display_t *disp; /*!< LVGL display handle (returned from lvgl_port_add_disp) */
|
||||
uint8_t sensitivity; /*!< Mouse sensitivity (cannot be zero) */
|
||||
lv_obj_t *cursor_img; /*!< Mouse cursor image, if NULL then used default */
|
||||
} lvgl_port_hid_mouse_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Configuration of the keyboard input
|
||||
*/
|
||||
typedef struct {
|
||||
lv_display_t *disp; /*!< LVGL display handle (returned from lvgl_port_add_disp) */
|
||||
} lvgl_port_hid_keyboard_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Add USB HID mouse as an input device
|
||||
*
|
||||
* @note The USB host must be initialized before. Use `usb_host_install` for host initialization.
|
||||
*
|
||||
* @param mouse_cfg mouse configuration structure
|
||||
* @return Pointer to LVGL buttons input device or NULL when error occurred
|
||||
*/
|
||||
lv_indev_t *lvgl_port_add_usb_hid_mouse_input(const lvgl_port_hid_mouse_cfg_t *mouse_cfg);
|
||||
|
||||
/**
|
||||
* @brief Add USB HID keyboard as an input device
|
||||
*
|
||||
* @note The USB host must be initialized before. Use `usb_host_install` for host initialization.
|
||||
*
|
||||
* @param keyboard_cfg keyboard configuration structure
|
||||
* @return Pointer to LVGL buttons input device or NULL when error occurred
|
||||
*/
|
||||
lv_indev_t *lvgl_port_add_usb_hid_keyboard_input(const lvgl_port_hid_keyboard_cfg_t *keyboard_cfg);
|
||||
|
||||
/**
|
||||
* @brief Remove selected USB HID from input devices
|
||||
*
|
||||
* @note Free all memory used for this input device. When removed all HID devices, the HID task will be freed.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
*/
|
||||
esp_err_t lvgl_port_remove_usb_hid_input(lv_indev_t *hid);
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
202
managed_components/espressif__esp_lvgl_port/license.txt
Normal file
202
managed_components/espressif__esp_lvgl_port/license.txt
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -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
|
||||
@@ -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()
|
||||
@@ -0,0 +1,199 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "lcd_ppa.h"
|
||||
|
||||
#define PPA_LCD_ENABLE_CB 0
|
||||
|
||||
#if SOC_PPA_SUPPORTED
|
||||
#define ALIGN_UP(num, align) (((num) + ((align) - 1)) & ~((align) - 1))
|
||||
|
||||
struct lvgl_port_ppa_t {
|
||||
uint8_t *buffer;
|
||||
uint32_t buffer_size;
|
||||
ppa_client_handle_t srm_handle;
|
||||
uint32_t color_type_id;
|
||||
};
|
||||
|
||||
static const char *TAG = "PPA";
|
||||
/*******************************************************************************
|
||||
* Function definitions
|
||||
*******************************************************************************/
|
||||
#if PPA_LCD_ENABLE_CB
|
||||
static bool _lvgl_port_ppa_callback(ppa_client_handle_t ppa_client, ppa_event_data_t *event_data, void *user_data);
|
||||
#endif
|
||||
/*******************************************************************************
|
||||
* Public API functions
|
||||
*******************************************************************************/
|
||||
|
||||
lvgl_port_ppa_handle_t lvgl_port_ppa_create(const lvgl_port_ppa_cfg_t *cfg)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
assert(cfg != NULL);
|
||||
|
||||
lvgl_port_ppa_t *ppa_ctx = malloc(sizeof(lvgl_port_ppa_t));
|
||||
ESP_GOTO_ON_FALSE(ppa_ctx, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for PPA context allocation!");
|
||||
memset(ppa_ctx, 0, sizeof(lvgl_port_ppa_t));
|
||||
|
||||
uint32_t buffer_caps = 0;
|
||||
if (cfg->flags.buff_dma) {
|
||||
buffer_caps |= MALLOC_CAP_DMA;
|
||||
}
|
||||
if (cfg->flags.buff_spiram) {
|
||||
buffer_caps |= MALLOC_CAP_SPIRAM;
|
||||
}
|
||||
if (buffer_caps == 0) {
|
||||
buffer_caps |= MALLOC_CAP_DEFAULT;
|
||||
}
|
||||
|
||||
ppa_ctx->buffer_size = ALIGN_UP(cfg->buffer_size, CONFIG_CACHE_L2_CACHE_LINE_SIZE);
|
||||
ppa_ctx->buffer = heap_caps_aligned_calloc(CONFIG_CACHE_L2_CACHE_LINE_SIZE, ppa_ctx->buffer_size, sizeof(uint8_t), buffer_caps);
|
||||
assert(ppa_ctx->buffer != NULL);
|
||||
|
||||
ppa_client_config_t ppa_client_config = {
|
||||
.oper_type = PPA_OPERATION_SRM,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(ppa_register_client(&ppa_client_config, &ppa_ctx->srm_handle), err, TAG, "Error when registering PPA client!");
|
||||
|
||||
#if PPA_LCD_ENABLE_CB
|
||||
ppa_event_callbacks_t ppa_cbs = {
|
||||
.on_trans_done = _lvgl_port_ppa_callback,
|
||||
};
|
||||
ESP_GOTO_ON_ERROR(ppa_client_register_event_callbacks(ppa_ctx->srm_handle, &ppa_cbs), err, TAG, "Error when registering PPA callbacks!");
|
||||
#endif
|
||||
|
||||
ppa_ctx->color_type_id = COLOR_TYPE_ID(cfg->color_space, cfg->pixel_format);
|
||||
|
||||
err:
|
||||
if (ret != ESP_OK) {
|
||||
if (ppa_ctx->buffer) {
|
||||
free(ppa_ctx->buffer);
|
||||
}
|
||||
if (ppa_ctx) {
|
||||
free(ppa_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
return ppa_ctx;
|
||||
}
|
||||
|
||||
void lvgl_port_ppa_delete(lvgl_port_ppa_handle_t handle)
|
||||
{
|
||||
lvgl_port_ppa_t *ppa_ctx = (lvgl_port_ppa_t *)handle;
|
||||
assert(ppa_ctx != NULL);
|
||||
|
||||
if (ppa_ctx->buffer) {
|
||||
free(ppa_ctx->buffer);
|
||||
}
|
||||
|
||||
ppa_unregister_client(ppa_ctx->srm_handle);
|
||||
|
||||
free(ppa_ctx);
|
||||
}
|
||||
|
||||
uint8_t *lvgl_port_ppa_get_output_buffer(lvgl_port_ppa_handle_t handle)
|
||||
{
|
||||
lvgl_port_ppa_t *ppa_ctx = (lvgl_port_ppa_t *)handle;
|
||||
assert(ppa_ctx != NULL);
|
||||
return ppa_ctx->buffer;
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_ppa_rotate(lvgl_port_ppa_handle_t handle, lvgl_port_ppa_disp_rotate_t *rotate_cfg)
|
||||
{
|
||||
lvgl_port_ppa_t *ppa_ctx = (lvgl_port_ppa_t *)handle;
|
||||
assert(ppa_ctx != NULL);
|
||||
assert(rotate_cfg != NULL);
|
||||
const int w = rotate_cfg->area.x2 - rotate_cfg->area.x1 + 1;
|
||||
const int h = rotate_cfg->area.y2 - rotate_cfg->area.y1 + 1;
|
||||
|
||||
/* Set dimension by screen size and rotation */
|
||||
int out_w = w;
|
||||
int out_h = h;
|
||||
|
||||
int x1 = rotate_cfg->area.x1;
|
||||
int x2 = rotate_cfg->area.x2;
|
||||
int y1 = rotate_cfg->area.y1;
|
||||
int y2 = rotate_cfg->area.y2;
|
||||
|
||||
/* Rotate coordinates */
|
||||
switch (rotate_cfg->rotation) {
|
||||
case PPA_SRM_ROTATION_ANGLE_0:
|
||||
break;
|
||||
case PPA_SRM_ROTATION_ANGLE_90:
|
||||
out_w = h;
|
||||
out_h = w;
|
||||
x1 = rotate_cfg->area.y1;
|
||||
x2 = rotate_cfg->area.y2;
|
||||
y1 = rotate_cfg->disp_size.hres - rotate_cfg->area.x2 - 1;
|
||||
y2 = rotate_cfg->disp_size.hres - rotate_cfg->area.x1 - 1;
|
||||
break;
|
||||
case PPA_SRM_ROTATION_ANGLE_180:
|
||||
x1 = rotate_cfg->disp_size.hres - rotate_cfg->area.x2 - 1;
|
||||
x2 = rotate_cfg->disp_size.hres - rotate_cfg->area.x1 - 1;
|
||||
y1 = rotate_cfg->disp_size.vres - rotate_cfg->area.y2 - 1;
|
||||
y2 = rotate_cfg->disp_size.vres - rotate_cfg->area.y1 - 1;
|
||||
break;
|
||||
case PPA_SRM_ROTATION_ANGLE_270:
|
||||
out_w = h;
|
||||
out_h = w;
|
||||
x1 = rotate_cfg->disp_size.vres - rotate_cfg->area.y2 - 1;
|
||||
x2 = rotate_cfg->disp_size.vres - rotate_cfg->area.y1 - 1;
|
||||
y1 = rotate_cfg->area.x1;
|
||||
y2 = rotate_cfg->area.x2;
|
||||
break;
|
||||
}
|
||||
/* Return new coordinates */
|
||||
rotate_cfg->area.x1 = x1;
|
||||
rotate_cfg->area.x2 = x2;
|
||||
rotate_cfg->area.y1 = y1;
|
||||
rotate_cfg->area.y2 = y2;
|
||||
|
||||
/* Prepare Operation */
|
||||
ppa_srm_oper_config_t srm_oper_config = {
|
||||
.in.buffer = rotate_cfg->in_buff,
|
||||
.in.pic_w = w,
|
||||
.in.pic_h = h,
|
||||
.in.block_w = w,
|
||||
.in.block_h = h,
|
||||
.in.block_offset_x = 0,
|
||||
.in.block_offset_y = 0,
|
||||
.in.srm_cm = ppa_ctx->color_type_id,
|
||||
|
||||
.out.buffer = ppa_ctx->buffer,
|
||||
.out.buffer_size = ppa_ctx->buffer_size,
|
||||
.out.pic_w = out_w,
|
||||
.out.pic_h = out_h,
|
||||
.out.block_offset_x = 0,
|
||||
.out.block_offset_y = 0,
|
||||
.out.srm_cm = ppa_ctx->color_type_id,
|
||||
|
||||
.rotation_angle = rotate_cfg->rotation,
|
||||
.scale_x = 1.0,
|
||||
.scale_y = 1.0,
|
||||
|
||||
.byte_swap = rotate_cfg->swap_bytes,
|
||||
|
||||
.mode = rotate_cfg->ppa_mode,
|
||||
.user_data = rotate_cfg->user_data,
|
||||
};
|
||||
|
||||
return ppa_do_scale_rotate_mirror(ppa_ctx->srm_handle, &srm_oper_config);
|
||||
}
|
||||
|
||||
#if PPA_LCD_ENABLE_CB
|
||||
static bool _lvgl_port_ppa_callback(ppa_client_handle_t ppa_client, ppa_event_data_t *event_data, void *user_data)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @file
|
||||
* @brief LCD PPA
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "driver/ppa.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct lvgl_port_ppa_t lvgl_port_ppa_t;
|
||||
typedef lvgl_port_ppa_t *lvgl_port_ppa_handle_t;
|
||||
|
||||
/**
|
||||
* @brief Init configuration structure
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t buffer_size; /*!< Size of the buffer for the PPA */
|
||||
color_space_t color_space; /*!< Color space of input/output data */
|
||||
uint32_t pixel_format; /*!< Pixel format of input/output data */
|
||||
struct {
|
||||
unsigned int buff_dma: 1; /*!< Allocated buffer will be DMA capable */
|
||||
unsigned int buff_spiram: 1; /*!< Allocated buffer will be in PSRAM */
|
||||
} flags;
|
||||
} lvgl_port_ppa_cfg_t;
|
||||
|
||||
/**
|
||||
* @brief Display area structure
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t x1;
|
||||
uint16_t x2;
|
||||
uint16_t y1;
|
||||
uint16_t y2;
|
||||
} lvgl_port_ppa_disp_area_t;
|
||||
|
||||
/**
|
||||
* @brief Display size structure
|
||||
*/
|
||||
typedef struct {
|
||||
uint32_t hres;
|
||||
uint32_t vres;
|
||||
} lvgl_port_ppa_disp_size_t;
|
||||
|
||||
/**
|
||||
* @brief Rotation configuration
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t *in_buff; /*!< Input buffer for rotation */
|
||||
lvgl_port_ppa_disp_area_t area; /*!< Coordinates of area */
|
||||
lvgl_port_ppa_disp_size_t disp_size; /*!< Display size */
|
||||
ppa_srm_rotation_angle_t rotation; /*!< Output rotation */
|
||||
ppa_trans_mode_t ppa_mode; /*!< Blocking or non-blocking mode */
|
||||
bool swap_bytes; /*!< SWAP bytes */
|
||||
void *user_data;
|
||||
} lvgl_port_ppa_disp_rotate_t;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Initialize PPA
|
||||
*
|
||||
* @note This function initialize PPA SRM Client and create buffer for process.
|
||||
*
|
||||
* @param cfg Configuration structure
|
||||
*
|
||||
* @return
|
||||
* - PPA LCD handle
|
||||
*/
|
||||
lvgl_port_ppa_handle_t lvgl_port_ppa_create(const lvgl_port_ppa_cfg_t *cfg);
|
||||
|
||||
/**
|
||||
* @brief Remove PPA
|
||||
*
|
||||
* @param handle PPA LCD handle
|
||||
*
|
||||
* @note This function free buffer and deinitialize PPA.
|
||||
*/
|
||||
void lvgl_port_ppa_delete(lvgl_port_ppa_handle_t handle);
|
||||
|
||||
/**
|
||||
* @brief Get output buffer
|
||||
*
|
||||
* @param handle PPA LCD handle
|
||||
*
|
||||
* @note This function get allocated buffer for output of PPA operation.
|
||||
*/
|
||||
uint8_t *lvgl_port_ppa_get_output_buffer(lvgl_port_ppa_handle_t handle);
|
||||
|
||||
/**
|
||||
* @brief Do rotation
|
||||
*
|
||||
* @param handle PPA LCD handle
|
||||
* @param rotate_cfg Rotation settings
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK on success
|
||||
* - ESP_ERR_NO_MEM if memory allocation fails
|
||||
*/
|
||||
esp_err_t lvgl_port_ppa_rotate(lvgl_port_ppa_handle_t handle, lvgl_port_ppa_disp_rotate_t *rotate_cfg);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "esp_system.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_timer.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_lvgl_port.h"
|
||||
#include "esp_lvgl_port_priv.h"
|
||||
#include "lvgl.h"
|
||||
|
||||
static const char *TAG = "LVGL";
|
||||
|
||||
#define ESP_LVGL_PORT_TASK_MUX_DELAY_MS 10000
|
||||
|
||||
/*******************************************************************************
|
||||
* Types definitions
|
||||
*******************************************************************************/
|
||||
|
||||
typedef struct lvgl_port_ctx_s {
|
||||
TaskHandle_t lvgl_task;
|
||||
SemaphoreHandle_t lvgl_mux;
|
||||
SemaphoreHandle_t task_mux;
|
||||
esp_timer_handle_t tick_timer;
|
||||
bool running;
|
||||
int task_max_sleep_ms;
|
||||
int timer_period_ms;
|
||||
} lvgl_port_ctx_t;
|
||||
|
||||
/*******************************************************************************
|
||||
* Local variables
|
||||
*******************************************************************************/
|
||||
static lvgl_port_ctx_t lvgl_port_ctx;
|
||||
|
||||
/*******************************************************************************
|
||||
* Function definitions
|
||||
*******************************************************************************/
|
||||
static void lvgl_port_task(void *arg);
|
||||
static esp_err_t lvgl_port_tick_init(void);
|
||||
static void lvgl_port_task_deinit(void);
|
||||
|
||||
/*******************************************************************************
|
||||
* Public API functions
|
||||
*******************************************************************************/
|
||||
|
||||
esp_err_t lvgl_port_init(const lvgl_port_cfg_t *cfg)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_GOTO_ON_FALSE(cfg, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE(cfg->task_affinity < (configNUM_CORES), ESP_ERR_INVALID_ARG, err, TAG, "Bad core number for task! Maximum core number is %d", (configNUM_CORES - 1));
|
||||
|
||||
memset(&lvgl_port_ctx, 0, sizeof(lvgl_port_ctx));
|
||||
|
||||
/* LVGL init */
|
||||
lv_init();
|
||||
/* Tick init */
|
||||
lvgl_port_ctx.timer_period_ms = cfg->timer_period_ms;
|
||||
ESP_RETURN_ON_ERROR(lvgl_port_tick_init(), TAG, "");
|
||||
/* Create task */
|
||||
lvgl_port_ctx.task_max_sleep_ms = cfg->task_max_sleep_ms;
|
||||
if (lvgl_port_ctx.task_max_sleep_ms == 0) {
|
||||
lvgl_port_ctx.task_max_sleep_ms = 500;
|
||||
}
|
||||
/* LVGL semaphore */
|
||||
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!");
|
||||
/* 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;
|
||||
if (cfg->task_affinity < 0) {
|
||||
res = xTaskCreate(lvgl_port_task, "taskLVGL", cfg->task_stack, NULL, cfg->task_priority, &lvgl_port_ctx.lvgl_task);
|
||||
} else {
|
||||
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!");
|
||||
|
||||
err:
|
||||
if (ret != ESP_OK) {
|
||||
lvgl_port_deinit();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_resume(void)
|
||||
{
|
||||
esp_err_t ret = ESP_ERR_INVALID_STATE;
|
||||
|
||||
if (lvgl_port_ctx.tick_timer != NULL) {
|
||||
lv_timer_enable(true);
|
||||
ret = esp_timer_start_periodic(lvgl_port_ctx.tick_timer, lvgl_port_ctx.timer_period_ms * 1000);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_stop(void)
|
||||
{
|
||||
esp_err_t ret = ESP_ERR_INVALID_STATE;
|
||||
|
||||
if (lvgl_port_ctx.tick_timer != NULL) {
|
||||
lv_timer_enable(false);
|
||||
ret = esp_timer_stop(lvgl_port_ctx.tick_timer);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_deinit(void)
|
||||
{
|
||||
/* Stop and delete timer */
|
||||
if (lvgl_port_ctx.tick_timer != NULL) {
|
||||
esp_timer_stop(lvgl_port_ctx.tick_timer);
|
||||
esp_timer_delete(lvgl_port_ctx.tick_timer);
|
||||
lvgl_port_ctx.tick_timer = NULL;
|
||||
}
|
||||
|
||||
/* Stop running task */
|
||||
if (lvgl_port_ctx.running) {
|
||||
lvgl_port_ctx.running = false;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
bool lvgl_port_lock(uint32_t timeout_ms)
|
||||
{
|
||||
assert(lvgl_port_ctx.lvgl_mux && "lvgl_port_init must be called first");
|
||||
|
||||
const TickType_t timeout_ticks = (timeout_ms == 0) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
|
||||
return xSemaphoreTakeRecursive(lvgl_port_ctx.lvgl_mux, timeout_ticks) == pdTRUE;
|
||||
}
|
||||
|
||||
void lvgl_port_unlock(void)
|
||||
{
|
||||
assert(lvgl_port_ctx.lvgl_mux && "lvgl_port_init must be called first");
|
||||
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
|
||||
*******************************************************************************/
|
||||
|
||||
static void lvgl_port_task(void *arg)
|
||||
{
|
||||
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");
|
||||
lvgl_port_ctx.running = true;
|
||||
while (lvgl_port_ctx.running) {
|
||||
if (lvgl_port_lock(0)) {
|
||||
task_delay_ms = lv_timer_handler();
|
||||
lvgl_port_unlock();
|
||||
}
|
||||
if (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 < 5) {
|
||||
task_delay_ms = 5;
|
||||
}
|
||||
vTaskDelay(pdMS_TO_TICKS(task_delay_ms));
|
||||
}
|
||||
|
||||
/* Give semaphore back */
|
||||
xSemaphoreGive(lvgl_port_ctx.task_mux);
|
||||
|
||||
/* Close task */
|
||||
vTaskDelete( NULL );
|
||||
}
|
||||
|
||||
static void lvgl_port_task_deinit(void)
|
||||
{
|
||||
if (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));
|
||||
#if LV_ENABLE_GC || !LV_MEM_CUSTOM
|
||||
/* Deinitialize LVGL */
|
||||
lv_deinit();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void lvgl_port_tick_increment(void *arg)
|
||||
{
|
||||
/* Tell LVGL how many milliseconds have elapsed */
|
||||
lv_tick_inc(lvgl_port_ctx.timer_period_ms);
|
||||
}
|
||||
|
||||
static esp_err_t lvgl_port_tick_init(void)
|
||||
{
|
||||
// Tick interface for LVGL (using esp_timer to generate 2ms periodic event)
|
||||
const esp_timer_create_args_t lvgl_tick_timer_args = {
|
||||
.callback = &lvgl_port_tick_increment,
|
||||
.name = "LVGL tick",
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(esp_timer_create(&lvgl_tick_timer_args, &lvgl_port_ctx.tick_timer), TAG, "Creating LVGL timer filed!");
|
||||
return esp_timer_start_periodic(lvgl_port_ctx.tick_timer, lvgl_port_ctx.timer_period_ms * 1000);
|
||||
}
|
||||
@@ -0,0 +1,210 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_lvgl_port.h"
|
||||
|
||||
static const char *TAG = "LVGL";
|
||||
|
||||
/*******************************************************************************
|
||||
* Types definitions
|
||||
*******************************************************************************/
|
||||
|
||||
typedef enum {
|
||||
LVGL_PORT_NAV_BTN_PREV,
|
||||
LVGL_PORT_NAV_BTN_NEXT,
|
||||
LVGL_PORT_NAV_BTN_ENTER,
|
||||
LVGL_PORT_NAV_BTN_CNT,
|
||||
} lvgl_port_nav_btns_t;
|
||||
|
||||
typedef struct {
|
||||
button_handle_t btn[LVGL_PORT_NAV_BTN_CNT]; /* Button handlers */
|
||||
lv_indev_drv_t indev_drv; /* LVGL input device driver */
|
||||
bool btn_prev; /* Button prev state */
|
||||
bool btn_next; /* Button next state */
|
||||
bool btn_enter; /* Button enter state */
|
||||
} lvgl_port_nav_btns_ctx_t;
|
||||
|
||||
/*******************************************************************************
|
||||
* Function definitions
|
||||
*******************************************************************************/
|
||||
|
||||
static void lvgl_port_navigation_buttons_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data);
|
||||
static void lvgl_port_btn_down_handler(void *arg, void *arg2);
|
||||
static void lvgl_port_btn_up_handler(void *arg, void *arg2);
|
||||
|
||||
/*******************************************************************************
|
||||
* Public API functions
|
||||
*******************************************************************************/
|
||||
|
||||
lv_indev_t *lvgl_port_add_navigation_buttons(const lvgl_port_nav_btns_cfg_t *buttons_cfg)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
lv_indev_t *indev = NULL;
|
||||
assert(buttons_cfg != NULL);
|
||||
assert(buttons_cfg->disp != NULL);
|
||||
|
||||
/* Touch context */
|
||||
lvgl_port_nav_btns_ctx_t *buttons_ctx = malloc(sizeof(lvgl_port_nav_btns_ctx_t));
|
||||
if (buttons_ctx == NULL) {
|
||||
ESP_LOGE(TAG, "Not enough memory for buttons context allocation!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if BUTTON_VER_MAJOR < 4
|
||||
/* Previous button */
|
||||
if (buttons_cfg->button_prev != NULL) {
|
||||
buttons_ctx->btn[LVGL_PORT_NAV_BTN_PREV] = iot_button_create(buttons_cfg->button_prev);
|
||||
ESP_GOTO_ON_FALSE(buttons_ctx->btn[LVGL_PORT_NAV_BTN_PREV], ESP_ERR_NO_MEM, err, TAG, "Not enough memory for button create!");
|
||||
}
|
||||
|
||||
/* Next button */
|
||||
if (buttons_cfg->button_next != NULL) {
|
||||
buttons_ctx->btn[LVGL_PORT_NAV_BTN_NEXT] = iot_button_create(buttons_cfg->button_next);
|
||||
ESP_GOTO_ON_FALSE(buttons_ctx->btn[LVGL_PORT_NAV_BTN_NEXT], ESP_ERR_NO_MEM, err, TAG, "Not enough memory for button create!");
|
||||
}
|
||||
|
||||
/* Enter button */
|
||||
if (buttons_cfg->button_enter != NULL) {
|
||||
buttons_ctx->btn[LVGL_PORT_NAV_BTN_ENTER] = iot_button_create(buttons_cfg->button_enter);
|
||||
ESP_GOTO_ON_FALSE(buttons_ctx->btn[LVGL_PORT_NAV_BTN_ENTER], ESP_ERR_NO_MEM, err, TAG, "Not enough memory for button create!");
|
||||
}
|
||||
#else
|
||||
ESP_GOTO_ON_FALSE(buttons_cfg->button_prev && buttons_cfg->button_next && buttons_cfg->button_enter, ESP_ERR_INVALID_ARG, err, TAG, "Invalid some button handler!");
|
||||
|
||||
buttons_ctx->btn[LVGL_PORT_NAV_BTN_PREV] = buttons_cfg->button_prev;
|
||||
buttons_ctx->btn[LVGL_PORT_NAV_BTN_NEXT] = buttons_cfg->button_next;
|
||||
buttons_ctx->btn[LVGL_PORT_NAV_BTN_ENTER] = buttons_cfg->button_enter;
|
||||
#endif
|
||||
|
||||
/* Button handlers */
|
||||
for (int i = 0; i < LVGL_PORT_NAV_BTN_CNT; i++) {
|
||||
#if BUTTON_VER_MAJOR < 4
|
||||
ESP_ERROR_CHECK(iot_button_register_cb(buttons_ctx->btn[i], BUTTON_PRESS_DOWN, lvgl_port_btn_down_handler, buttons_ctx));
|
||||
ESP_ERROR_CHECK(iot_button_register_cb(buttons_ctx->btn[i], BUTTON_PRESS_UP, lvgl_port_btn_up_handler, buttons_ctx));
|
||||
#else
|
||||
ESP_ERROR_CHECK(iot_button_register_cb(buttons_ctx->btn[i], BUTTON_PRESS_DOWN, NULL, lvgl_port_btn_down_handler, buttons_ctx));
|
||||
ESP_ERROR_CHECK(iot_button_register_cb(buttons_ctx->btn[i], BUTTON_PRESS_UP, NULL, lvgl_port_btn_up_handler, buttons_ctx));
|
||||
#endif
|
||||
}
|
||||
|
||||
buttons_ctx->btn_prev = false;
|
||||
buttons_ctx->btn_next = false;
|
||||
buttons_ctx->btn_enter = false;
|
||||
|
||||
/* Register a touchpad input device */
|
||||
lv_indev_drv_init(&buttons_ctx->indev_drv);
|
||||
buttons_ctx->indev_drv.type = LV_INDEV_TYPE_ENCODER;
|
||||
buttons_ctx->indev_drv.disp = buttons_cfg->disp;
|
||||
buttons_ctx->indev_drv.read_cb = lvgl_port_navigation_buttons_read;
|
||||
buttons_ctx->indev_drv.user_data = buttons_ctx;
|
||||
buttons_ctx->indev_drv.long_press_repeat_time = 300;
|
||||
indev = lv_indev_drv_register(&buttons_ctx->indev_drv);
|
||||
|
||||
err:
|
||||
if (ret != ESP_OK) {
|
||||
for (int i = 0; i < LVGL_PORT_NAV_BTN_CNT; i++) {
|
||||
if (buttons_ctx->btn[i] != NULL) {
|
||||
iot_button_delete(buttons_ctx->btn[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (buttons_ctx != NULL) {
|
||||
free(buttons_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
return indev;
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_remove_navigation_buttons(lv_indev_t *buttons)
|
||||
{
|
||||
assert(buttons);
|
||||
lv_indev_drv_t *indev_drv = buttons->driver;
|
||||
assert(indev_drv);
|
||||
lvgl_port_nav_btns_ctx_t *buttons_ctx = (lvgl_port_nav_btns_ctx_t *)indev_drv->user_data;
|
||||
|
||||
/* Remove input device driver */
|
||||
lv_indev_delete(buttons);
|
||||
|
||||
if (buttons_ctx) {
|
||||
free(buttons_ctx);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Private functions
|
||||
*******************************************************************************/
|
||||
|
||||
static void lvgl_port_navigation_buttons_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data)
|
||||
{
|
||||
static uint32_t last_key = 0;
|
||||
assert(indev_drv);
|
||||
lvgl_port_nav_btns_ctx_t *ctx = (lvgl_port_nav_btns_ctx_t *)indev_drv->user_data;
|
||||
assert(ctx);
|
||||
|
||||
/* Buttons */
|
||||
if (ctx->btn_prev) {
|
||||
data->key = LV_KEY_LEFT;
|
||||
last_key = LV_KEY_LEFT;
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
} else if (ctx->btn_next) {
|
||||
data->key = LV_KEY_RIGHT;
|
||||
last_key = LV_KEY_RIGHT;
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
} else if (ctx->btn_enter) {
|
||||
data->key = LV_KEY_ENTER;
|
||||
last_key = LV_KEY_ENTER;
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
} else {
|
||||
data->key = last_key;
|
||||
data->state = LV_INDEV_STATE_RELEASED;
|
||||
}
|
||||
}
|
||||
|
||||
static void lvgl_port_btn_down_handler(void *arg, void *arg2)
|
||||
{
|
||||
lvgl_port_nav_btns_ctx_t *ctx = (lvgl_port_nav_btns_ctx_t *) arg2;
|
||||
button_handle_t button = (button_handle_t)arg;
|
||||
if (ctx && button) {
|
||||
/* PREV */
|
||||
if (button == ctx->btn[LVGL_PORT_NAV_BTN_PREV]) {
|
||||
ctx->btn_prev = true;
|
||||
}
|
||||
/* NEXT */
|
||||
if (button == ctx->btn[LVGL_PORT_NAV_BTN_NEXT]) {
|
||||
ctx->btn_next = true;
|
||||
}
|
||||
/* ENTER */
|
||||
if (button == ctx->btn[LVGL_PORT_NAV_BTN_ENTER]) {
|
||||
ctx->btn_enter = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void lvgl_port_btn_up_handler(void *arg, void *arg2)
|
||||
{
|
||||
lvgl_port_nav_btns_ctx_t *ctx = (lvgl_port_nav_btns_ctx_t *) arg2;
|
||||
button_handle_t button = (button_handle_t)arg;
|
||||
if (ctx && button) {
|
||||
/* PREV */
|
||||
if (button == ctx->btn[LVGL_PORT_NAV_BTN_PREV]) {
|
||||
ctx->btn_prev = false;
|
||||
}
|
||||
/* NEXT */
|
||||
if (button == ctx->btn[LVGL_PORT_NAV_BTN_NEXT]) {
|
||||
ctx->btn_next = false;
|
||||
}
|
||||
/* ENTER */
|
||||
if (button == ctx->btn[LVGL_PORT_NAV_BTN_ENTER]) {
|
||||
ctx->btn_enter = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,561 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_idf_version.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_lcd_panel_ops.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))
|
||||
#define LVGL_PORT_HANDLE_FLUSH_READY 0
|
||||
#else
|
||||
#define LVGL_PORT_HANDLE_FLUSH_READY 1
|
||||
#endif
|
||||
|
||||
static const char *TAG = "LVGL";
|
||||
|
||||
/*******************************************************************************
|
||||
* Types definitions
|
||||
*******************************************************************************/
|
||||
|
||||
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_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 */
|
||||
lv_disp_drv_t disp_drv; /* LVGL display driver */
|
||||
lv_color_t *trans_buf; /* Buffer send to driver */
|
||||
uint32_t trans_size; /* Maximum size for one transport */
|
||||
SemaphoreHandle_t trans_sem; /* Idle transfer mutex */
|
||||
} lvgl_port_display_ctx_t;
|
||||
|
||||
/*******************************************************************************
|
||||
* 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
|
||||
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
|
||||
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_pix_monochrome_callback(lv_disp_drv_t *drv, uint8_t *buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, lv_color_t color, lv_opa_t opa);
|
||||
|
||||
/*******************************************************************************
|
||||
* Public API functions
|
||||
*******************************************************************************/
|
||||
|
||||
lv_disp_t *lvgl_port_add_disp(const lvgl_port_display_cfg_t *disp_cfg)
|
||||
{
|
||||
lv_disp_t *disp = lvgl_port_add_disp_priv(disp_cfg, NULL);
|
||||
|
||||
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_OTHER;
|
||||
|
||||
assert(disp_ctx->io_handle != NULL);
|
||||
|
||||
#if LVGL_PORT_HANDLE_FLUSH_READY
|
||||
const esp_lcd_panel_io_callbacks_t cbs = {
|
||||
.on_color_trans_done = lvgl_port_flush_io_ready_callback,
|
||||
};
|
||||
/* Register done callback */
|
||||
esp_lcd_panel_io_register_event_callbacks(disp_ctx->io_handle, &cbs, &disp_ctx->disp_drv);
|
||||
#endif
|
||||
}
|
||||
|
||||
return disp;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
assert(dsi_cfg != NULL);
|
||||
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;
|
||||
}
|
||||
/* Register done callback */
|
||||
esp_lcd_dpi_panel_register_event_callbacks(disp_ctx->panel_handle, &cbs, &disp_ctx->disp_drv);
|
||||
#else
|
||||
ESP_RETURN_ON_FALSE(false, NULL, TAG, "MIPI-DSI is supported only on ESP32P4 and from IDF 5.3!");
|
||||
#endif
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_remove_disp(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;
|
||||
if (disp_ctx->trans_sem) {
|
||||
vSemaphoreDelete(disp_ctx->trans_sem);
|
||||
}
|
||||
|
||||
lv_disp_remove(disp);
|
||||
|
||||
if (disp_drv) {
|
||||
if (disp_drv->draw_ctx) {
|
||||
disp_drv->draw_ctx_deinit(disp_drv, disp_drv->draw_ctx);
|
||||
lv_mem_free(disp_drv->draw_ctx);
|
||||
disp_drv->draw_ctx = NULL;
|
||||
}
|
||||
if (disp_drv->draw_buf && disp_drv->draw_buf->buf1) {
|
||||
free(disp_drv->draw_buf->buf1);
|
||||
disp_drv->draw_buf->buf1 = NULL;
|
||||
}
|
||||
if (disp_drv->draw_buf && disp_drv->draw_buf->buf2) {
|
||||
free(disp_drv->draw_buf->buf2);
|
||||
disp_drv->draw_buf->buf2 = NULL;
|
||||
}
|
||||
if (disp_drv->draw_buf) {
|
||||
free(disp_drv->draw_buf);
|
||||
disp_drv->draw_buf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
free(disp_ctx);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void lvgl_port_flush_ready(lv_disp_t *disp)
|
||||
{
|
||||
assert(disp);
|
||||
assert(disp->driver);
|
||||
lv_disp_flush_ready(disp->driver);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* 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
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#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
|
||||
|
||||
static void lvgl_port_flush_callback(lv_disp_drv_t *drv, const lv_area_t *area, lv_color_t *color_map)
|
||||
{
|
||||
assert(drv != NULL);
|
||||
lvgl_port_display_ctx_t *disp_ctx = (lvgl_port_display_ctx_t *)drv->user_data;
|
||||
assert(disp_ctx != NULL);
|
||||
|
||||
int x_draw_start;
|
||||
int x_draw_end;
|
||||
int y_draw_start;
|
||||
int y_draw_end;
|
||||
|
||||
int y_start_tmp;
|
||||
int y_end_tmp;
|
||||
|
||||
int trans_count;
|
||||
int trans_line;
|
||||
int max_line;
|
||||
|
||||
const int x_start = area->x1;
|
||||
const int x_end = area->x2;
|
||||
const int y_start = area->y1;
|
||||
const int y_end = area->y2;
|
||||
const int width = x_end - x_start + 1;
|
||||
const int height = y_end - y_start + 1;
|
||||
|
||||
lv_color_t *from = color_map;
|
||||
lv_color_t *to = NULL;
|
||||
|
||||
if (disp_ctx->trans_size == 0) {
|
||||
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 {
|
||||
y_start_tmp = y_start;
|
||||
max_line = ((disp_ctx->trans_size / width) > height) ? (height) : (disp_ctx->trans_size / width);
|
||||
trans_count = height / max_line + (height % max_line ? (1) : (0));
|
||||
|
||||
for (int i = 0; i < trans_count; i++) {
|
||||
trans_line = (y_end - y_start_tmp + 1) > max_line ? max_line : (y_end - y_start_tmp + 1);
|
||||
y_end_tmp = (y_end - y_start_tmp + 1) > max_line ? (y_start_tmp + max_line - 1) : y_end;
|
||||
|
||||
to = disp_ctx->trans_buf;
|
||||
for (int y = 0; y < trans_line; y++) {
|
||||
for (int x = 0; x < width; x++) {
|
||||
*(to + y * (width) + x) = *(from + y * (width) + x);
|
||||
}
|
||||
}
|
||||
x_draw_start = x_start;
|
||||
x_draw_end = x_end;
|
||||
y_draw_start = y_start_tmp;
|
||||
y_draw_end = y_end_tmp;
|
||||
esp_lcd_panel_draw_bitmap(disp_ctx->panel_handle, x_draw_start, y_draw_start, x_draw_end + 1, y_draw_end + 1, to);
|
||||
|
||||
from += max_line * width;
|
||||
y_start_tmp += max_line;
|
||||
xSemaphoreTake(disp_ctx->trans_sem, portMAX_DELAY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void lvgl_port_update_callback(lv_disp_drv_t *drv)
|
||||
{
|
||||
assert(drv);
|
||||
lvgl_port_display_ctx_t *disp_ctx = (lvgl_port_display_ctx_t *)drv->user_data;
|
||||
assert(disp_ctx != NULL);
|
||||
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 (drv->rotated) {
|
||||
case LV_DISP_ROT_NONE:
|
||||
/* 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_DISP_ROT_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_DISP_ROT_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_DISP_ROT_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;
|
||||
}
|
||||
}
|
||||
|
||||
static void lvgl_port_pix_monochrome_callback(lv_disp_drv_t *drv, uint8_t *buf, lv_coord_t buf_w, lv_coord_t x, lv_coord_t y, lv_color_t color, lv_opa_t opa)
|
||||
{
|
||||
if (drv->rotated == LV_DISP_ROT_90 || drv->rotated == LV_DISP_ROT_270) {
|
||||
lv_coord_t tmp_x = x;
|
||||
x = y;
|
||||
y = tmp_x;
|
||||
}
|
||||
|
||||
/* Write to the buffer as required for the display.
|
||||
* It writes only 1-bit for monochrome displays mapped vertically.*/
|
||||
buf += drv->hor_res * (y >> 3) + x;
|
||||
if (lv_color_to1(color)) {
|
||||
(*buf) &= ~(1 << (y % 8));
|
||||
} else {
|
||||
(*buf) |= (1 << (y % 8));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_lvgl_port.h"
|
||||
|
||||
static const char *TAG = "LVGL";
|
||||
|
||||
/*******************************************************************************
|
||||
* Types definitions
|
||||
*******************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
knob_handle_t knob_handle; /* Encoder knob handlers */
|
||||
button_handle_t btn_handle; /* Encoder button handlers */
|
||||
lv_indev_drv_t indev_drv; /* LVGL input device driver */
|
||||
bool btn_enter; /* Encoder button enter state */
|
||||
int32_t diff; /* Encoder diff */
|
||||
} lvgl_port_encoder_ctx_t;
|
||||
|
||||
/*******************************************************************************
|
||||
* Function definitions
|
||||
*******************************************************************************/
|
||||
|
||||
static void lvgl_port_encoder_read(lv_indev_drv_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_up_handler(void *arg, void *arg2);
|
||||
static void lvgl_port_encoder_left_handler(void *arg, void *arg2);
|
||||
static void lvgl_port_encoder_right_handler(void *arg, void *arg2);
|
||||
static int32_t lvgl_port_calculate_diff(knob_handle_t knob, knob_event_t event);
|
||||
|
||||
/*******************************************************************************
|
||||
* Public API functions
|
||||
*******************************************************************************/
|
||||
|
||||
lv_indev_t *lvgl_port_add_encoder(const lvgl_port_encoder_cfg_t *encoder_cfg)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
lv_indev_t *indev = NULL;
|
||||
assert(encoder_cfg != NULL);
|
||||
assert(encoder_cfg->disp != NULL);
|
||||
|
||||
/* Encoder context */
|
||||
lvgl_port_encoder_ctx_t *encoder_ctx = malloc(sizeof(lvgl_port_encoder_ctx_t));
|
||||
if (encoder_ctx == NULL) {
|
||||
ESP_LOGE(TAG, "Not enough memory for encoder context allocation!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Encoder_a/b */
|
||||
if (encoder_cfg->encoder_a_b != NULL) {
|
||||
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_ERROR_CHECK(iot_knob_register_cb(encoder_ctx->knob_handle, KNOB_LEFT, lvgl_port_encoder_left_handler, encoder_ctx));
|
||||
ESP_ERROR_CHECK(iot_knob_register_cb(encoder_ctx->knob_handle, KNOB_RIGHT, lvgl_port_encoder_right_handler, encoder_ctx));
|
||||
|
||||
/* Encoder Enter */
|
||||
if (encoder_cfg->encoder_enter != NULL) {
|
||||
#if BUTTON_VER_MAJOR < 4
|
||||
encoder_ctx->btn_handle = iot_button_create(encoder_cfg->encoder_enter);
|
||||
ESP_GOTO_ON_FALSE(encoder_ctx->btn_handle, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for button create!");
|
||||
#else
|
||||
ESP_GOTO_ON_FALSE(encoder_cfg->encoder_enter, ESP_ERR_INVALID_ARG, err, TAG, "Invalid button handler!");
|
||||
encoder_ctx->btn_handle = encoder_cfg->encoder_enter;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if BUTTON_VER_MAJOR < 4
|
||||
ESP_ERROR_CHECK(iot_button_register_cb(encoder_ctx->btn_handle, BUTTON_PRESS_DOWN, lvgl_port_encoder_btn_down_handler, encoder_ctx));
|
||||
ESP_ERROR_CHECK(iot_button_register_cb(encoder_ctx->btn_handle, BUTTON_PRESS_UP, lvgl_port_encoder_btn_up_handler, encoder_ctx));
|
||||
#else
|
||||
ESP_ERROR_CHECK(iot_button_register_cb(encoder_ctx->btn_handle, BUTTON_PRESS_DOWN, NULL, lvgl_port_encoder_btn_down_handler, encoder_ctx));
|
||||
ESP_ERROR_CHECK(iot_button_register_cb(encoder_ctx->btn_handle, BUTTON_PRESS_UP, NULL, lvgl_port_encoder_btn_up_handler, encoder_ctx));
|
||||
#endif
|
||||
|
||||
encoder_ctx->btn_enter = false;
|
||||
encoder_ctx->diff = 0;
|
||||
|
||||
/* Register a encoder input device */
|
||||
lv_indev_drv_init(&encoder_ctx->indev_drv);
|
||||
encoder_ctx->indev_drv.type = LV_INDEV_TYPE_ENCODER;
|
||||
encoder_ctx->indev_drv.disp = encoder_cfg->disp;
|
||||
encoder_ctx->indev_drv.read_cb = lvgl_port_encoder_read;
|
||||
encoder_ctx->indev_drv.user_data = encoder_ctx;
|
||||
indev = lv_indev_drv_register(&encoder_ctx->indev_drv);
|
||||
|
||||
err:
|
||||
if (ret != ESP_OK) {
|
||||
if (encoder_ctx->knob_handle != NULL) {
|
||||
iot_knob_delete(encoder_ctx->knob_handle);
|
||||
}
|
||||
|
||||
if (encoder_ctx->btn_handle != NULL) {
|
||||
iot_button_delete(encoder_ctx->btn_handle);
|
||||
}
|
||||
|
||||
if (encoder_ctx != NULL) {
|
||||
free(encoder_ctx);
|
||||
}
|
||||
}
|
||||
return indev;
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_remove_encoder(lv_indev_t *encoder)
|
||||
{
|
||||
assert(encoder);
|
||||
lv_indev_drv_t *indev_drv = encoder->driver;
|
||||
assert(indev_drv);
|
||||
lvgl_port_encoder_ctx_t *encoder_ctx = (lvgl_port_encoder_ctx_t *)indev_drv->user_data;
|
||||
|
||||
if (encoder_ctx->knob_handle != NULL) {
|
||||
iot_knob_delete(encoder_ctx->knob_handle);
|
||||
}
|
||||
|
||||
if (encoder_ctx->btn_handle != NULL) {
|
||||
iot_button_delete(encoder_ctx->btn_handle);
|
||||
}
|
||||
|
||||
if (encoder_ctx != NULL) {
|
||||
free(encoder_ctx);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Private functions
|
||||
*******************************************************************************/
|
||||
|
||||
static void lvgl_port_encoder_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data)
|
||||
{
|
||||
assert(indev_drv);
|
||||
lvgl_port_encoder_ctx_t *ctx = (lvgl_port_encoder_ctx_t *)indev_drv->user_data;
|
||||
assert(ctx);
|
||||
|
||||
data->enc_diff = ctx->diff;
|
||||
data->state = (true == ctx->btn_enter) ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
|
||||
ctx->diff = 0;
|
||||
}
|
||||
|
||||
static void lvgl_port_encoder_btn_down_handler(void *arg, void *arg2)
|
||||
{
|
||||
lvgl_port_encoder_ctx_t *ctx = (lvgl_port_encoder_ctx_t *) arg2;
|
||||
button_handle_t button = (button_handle_t)arg;
|
||||
if (ctx && button) {
|
||||
/* ENTER */
|
||||
if (button == ctx->btn_handle) {
|
||||
ctx->btn_enter = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void lvgl_port_encoder_btn_up_handler(void *arg, void *arg2)
|
||||
{
|
||||
lvgl_port_encoder_ctx_t *ctx = (lvgl_port_encoder_ctx_t *) arg2;
|
||||
button_handle_t button = (button_handle_t)arg;
|
||||
if (ctx && button) {
|
||||
/* ENTER */
|
||||
if (button == ctx->btn_handle) {
|
||||
ctx->btn_enter = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void lvgl_port_encoder_left_handler(void *arg, void *arg2)
|
||||
{
|
||||
lvgl_port_encoder_ctx_t *ctx = (lvgl_port_encoder_ctx_t *) arg2;
|
||||
knob_handle_t knob = (knob_handle_t)arg;
|
||||
if (ctx && knob) {
|
||||
/* LEFT */
|
||||
if (knob == ctx->knob_handle) {
|
||||
int32_t diff = lvgl_port_calculate_diff(knob, KNOB_LEFT);
|
||||
ctx->diff = (ctx->diff > 0) ? diff : ctx->diff + diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void lvgl_port_encoder_right_handler(void *arg, void *arg2)
|
||||
{
|
||||
lvgl_port_encoder_ctx_t *ctx = (lvgl_port_encoder_ctx_t *) arg2;
|
||||
knob_handle_t knob = (knob_handle_t)arg;
|
||||
if (ctx && knob) {
|
||||
/* RIGHT */
|
||||
if (knob == ctx->knob_handle) {
|
||||
int32_t diff = lvgl_port_calculate_diff(knob, KNOB_RIGHT);
|
||||
ctx->diff = (ctx->diff < 0) ? diff : ctx->diff + diff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int32_t lvgl_port_calculate_diff(knob_handle_t knob, knob_event_t event)
|
||||
{
|
||||
static int32_t last_v = 0;
|
||||
|
||||
int32_t diff = 0;
|
||||
int32_t invd = iot_knob_get_count_value(knob);
|
||||
|
||||
if (last_v ^ invd) {
|
||||
|
||||
diff = (int32_t)((uint32_t)invd - (uint32_t)last_v);
|
||||
diff += (event == KNOB_RIGHT && invd < last_v) ? CONFIG_KNOB_HIGH_LIMIT :
|
||||
(event == KNOB_LEFT && invd > last_v) ? CONFIG_KNOB_LOW_LIMIT : 0;
|
||||
last_v = invd;
|
||||
}
|
||||
|
||||
return diff;
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_lcd_touch.h"
|
||||
#include "esp_lvgl_port.h"
|
||||
|
||||
static const char *TAG = "LVGL";
|
||||
|
||||
/*******************************************************************************
|
||||
* Types definitions
|
||||
*******************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
esp_lcd_touch_handle_t handle; /* LCD touch IO handle */
|
||||
lv_indev_drv_t indev_drv; /* LVGL input device driver */
|
||||
struct {
|
||||
float x;
|
||||
float y;
|
||||
} scale; /* Touch scale */
|
||||
} lvgl_port_touch_ctx_t;
|
||||
|
||||
/*******************************************************************************
|
||||
* Function definitions
|
||||
*******************************************************************************/
|
||||
|
||||
static void lvgl_port_touchpad_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data);
|
||||
|
||||
/*******************************************************************************
|
||||
* Public API functions
|
||||
*******************************************************************************/
|
||||
|
||||
lv_indev_t *lvgl_port_add_touch(const lvgl_port_touch_cfg_t *touch_cfg)
|
||||
{
|
||||
assert(touch_cfg != NULL);
|
||||
assert(touch_cfg->disp != NULL);
|
||||
assert(touch_cfg->handle != NULL);
|
||||
|
||||
/* Touch context */
|
||||
lvgl_port_touch_ctx_t *touch_ctx = malloc(sizeof(lvgl_port_touch_ctx_t));
|
||||
if (touch_ctx == NULL) {
|
||||
ESP_LOGE(TAG, "Not enough memory for touch context allocation!");
|
||||
return NULL;
|
||||
}
|
||||
touch_ctx->handle = touch_cfg->handle;
|
||||
touch_ctx->scale.x = (touch_cfg->scale.x ? touch_cfg->scale.x : 1);
|
||||
touch_ctx->scale.y = (touch_cfg->scale.y ? touch_cfg->scale.y : 1);
|
||||
|
||||
/* Register a touchpad input device */
|
||||
lv_indev_drv_init(&touch_ctx->indev_drv);
|
||||
touch_ctx->indev_drv.type = LV_INDEV_TYPE_POINTER;
|
||||
touch_ctx->indev_drv.disp = touch_cfg->disp;
|
||||
touch_ctx->indev_drv.read_cb = lvgl_port_touchpad_read;
|
||||
touch_ctx->indev_drv.user_data = touch_ctx;
|
||||
return lv_indev_drv_register(&touch_ctx->indev_drv);
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_remove_touch(lv_indev_t *touch)
|
||||
{
|
||||
assert(touch);
|
||||
lv_indev_drv_t *indev_drv = touch->driver;
|
||||
assert(indev_drv);
|
||||
lvgl_port_touch_ctx_t *touch_ctx = (lvgl_port_touch_ctx_t *)indev_drv->user_data;
|
||||
|
||||
/* Remove input device driver */
|
||||
lv_indev_delete(touch);
|
||||
|
||||
if (touch_ctx) {
|
||||
free(touch_ctx);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Private functions
|
||||
*******************************************************************************/
|
||||
|
||||
static void lvgl_port_touchpad_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data)
|
||||
{
|
||||
assert(indev_drv);
|
||||
lvgl_port_touch_ctx_t *touch_ctx = (lvgl_port_touch_ctx_t *)indev_drv->user_data;
|
||||
assert(touch_ctx->handle);
|
||||
|
||||
uint16_t touchpad_x[1] = {0};
|
||||
uint16_t touchpad_y[1] = {0};
|
||||
uint8_t touchpad_cnt = 0;
|
||||
|
||||
/* Read data from touch controller into memory */
|
||||
esp_lcd_touch_read_data(touch_ctx->handle);
|
||||
|
||||
/* Read data from touch controller */
|
||||
bool touchpad_pressed = esp_lcd_touch_get_coordinates(touch_ctx->handle, touchpad_x, touchpad_y, NULL, &touchpad_cnt, 1);
|
||||
|
||||
if (touchpad_pressed && touchpad_cnt > 0) {
|
||||
data->point.x = touch_ctx->scale.x * touchpad_x[0];
|
||||
data->point.y = touch_ctx->scale.y * touchpad_y[0];
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
} else {
|
||||
data->state = LV_INDEV_STATE_RELEASED;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,469 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.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_usage_keyboard.h"
|
||||
#include "usb/hid_usage_mouse.h"
|
||||
|
||||
/* LVGL image of cursor */
|
||||
LV_IMG_DECLARE(img_cursor)
|
||||
|
||||
static const char *TAG = "LVGL";
|
||||
|
||||
/*******************************************************************************
|
||||
* Types definitions
|
||||
*******************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
QueueHandle_t queue; /* USB HID queue */
|
||||
TaskHandle_t task; /* USB HID task */
|
||||
bool running; /* USB HID task running */
|
||||
struct {
|
||||
lv_indev_drv_t drv; /* LVGL mouse input device driver */
|
||||
uint8_t sensitivity; /* Mouse sensitivity (cannot be zero) */
|
||||
int16_t x; /* Mouse X coordinate */
|
||||
int16_t y; /* Mouse Y coordinate */
|
||||
bool left_button; /* Mouse left button state */
|
||||
} mouse;
|
||||
struct {
|
||||
lv_indev_drv_t drv; /* LVGL keyboard input device driver */
|
||||
uint32_t last_key;
|
||||
bool pressed;
|
||||
} kb;
|
||||
} lvgl_port_usb_hid_ctx_t;
|
||||
|
||||
typedef struct {
|
||||
hid_host_device_handle_t hid_device_handle;
|
||||
hid_host_driver_event_t event;
|
||||
void *arg;
|
||||
} lvgl_port_usb_hid_event_t;
|
||||
|
||||
/*******************************************************************************
|
||||
* Local variables
|
||||
*******************************************************************************/
|
||||
|
||||
static lvgl_port_usb_hid_ctx_t lvgl_hid_ctx;
|
||||
|
||||
/*******************************************************************************
|
||||
* Function definitions
|
||||
*******************************************************************************/
|
||||
|
||||
static lvgl_port_usb_hid_ctx_t *lvgl_port_hid_init(void);
|
||||
static void lvgl_port_usb_hid_task(void *arg);
|
||||
static void lvgl_port_usb_hid_read_mouse(lv_indev_drv_t *indev_drv, lv_indev_data_t *data);
|
||||
static void lvgl_port_usb_hid_read_kb(lv_indev_drv_t *indev_drv, lv_indev_data_t *data);
|
||||
static void lvgl_port_usb_hid_callback(hid_host_device_handle_t hid_device_handle, const hid_host_driver_event_t event, void *arg);
|
||||
|
||||
/*******************************************************************************
|
||||
* Public API functions
|
||||
*******************************************************************************/
|
||||
|
||||
lv_indev_t *lvgl_port_add_usb_hid_mouse_input(const lvgl_port_hid_mouse_cfg_t *mouse_cfg)
|
||||
{
|
||||
lv_indev_t *indev;
|
||||
assert(mouse_cfg);
|
||||
assert(mouse_cfg->disp);
|
||||
assert(mouse_cfg->disp->driver);
|
||||
|
||||
/* Initialize USB HID */
|
||||
lvgl_port_usb_hid_ctx_t *hid_ctx = lvgl_port_hid_init();
|
||||
if (hid_ctx == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Mouse sensitivity cannot be zero */
|
||||
hid_ctx->mouse.sensitivity = (mouse_cfg->sensitivity == 0 ? 1 : mouse_cfg->sensitivity);
|
||||
/* Default coordinates to screen center */
|
||||
hid_ctx->mouse.x = (mouse_cfg->disp->driver->hor_res * hid_ctx->mouse.sensitivity) / 2;
|
||||
hid_ctx->mouse.y = (mouse_cfg->disp->driver->ver_res * hid_ctx->mouse.sensitivity) / 2;
|
||||
|
||||
/* Register a mouse input device */
|
||||
lv_indev_drv_init(&hid_ctx->mouse.drv);
|
||||
hid_ctx->mouse.drv.type = LV_INDEV_TYPE_POINTER;
|
||||
hid_ctx->mouse.drv.disp = mouse_cfg->disp;
|
||||
hid_ctx->mouse.drv.read_cb = lvgl_port_usb_hid_read_mouse;
|
||||
hid_ctx->mouse.drv.user_data = hid_ctx;
|
||||
indev = lv_indev_drv_register(&hid_ctx->mouse.drv);
|
||||
|
||||
/* Set image of cursor */
|
||||
lv_obj_t *cursor = mouse_cfg->cursor_img;
|
||||
if (cursor == NULL) {
|
||||
cursor = lv_img_create(lv_scr_act());
|
||||
lv_img_set_src(cursor, &img_cursor);
|
||||
}
|
||||
lv_indev_set_cursor(indev, cursor);
|
||||
|
||||
return indev;
|
||||
}
|
||||
|
||||
lv_indev_t *lvgl_port_add_usb_hid_keyboard_input(const lvgl_port_hid_keyboard_cfg_t *keyboard_cfg)
|
||||
{
|
||||
lv_indev_t *indev;
|
||||
assert(keyboard_cfg);
|
||||
assert(keyboard_cfg->disp);
|
||||
|
||||
/* Initialize USB HID */
|
||||
lvgl_port_usb_hid_ctx_t *hid_ctx = lvgl_port_hid_init();
|
||||
if (hid_ctx == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Register a keyboard input device */
|
||||
lv_indev_drv_init(&hid_ctx->kb.drv);
|
||||
hid_ctx->kb.drv.type = LV_INDEV_TYPE_KEYPAD;
|
||||
hid_ctx->kb.drv.disp = keyboard_cfg->disp;
|
||||
hid_ctx->kb.drv.read_cb = lvgl_port_usb_hid_read_kb;
|
||||
hid_ctx->kb.drv.user_data = hid_ctx;
|
||||
indev = lv_indev_drv_register(&hid_ctx->kb.drv);
|
||||
|
||||
return indev;
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_remove_usb_hid_input(lv_indev_t *hid)
|
||||
{
|
||||
assert(hid);
|
||||
lv_indev_drv_t *indev_drv = hid->driver;
|
||||
assert(indev_drv);
|
||||
lvgl_port_usb_hid_ctx_t *hid_ctx = (lvgl_port_usb_hid_ctx_t *)indev_drv->user_data;
|
||||
|
||||
/* Remove input device driver */
|
||||
lv_indev_delete(hid);
|
||||
|
||||
/* If all hid input devices are removed, stop task and clean all */
|
||||
if (lvgl_hid_ctx.mouse.drv.disp == NULL && lvgl_hid_ctx.kb.drv.disp) {
|
||||
hid_ctx->running = false;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Private functions
|
||||
*******************************************************************************/
|
||||
|
||||
static lvgl_port_usb_hid_ctx_t *lvgl_port_hid_init(void)
|
||||
{
|
||||
esp_err_t ret;
|
||||
|
||||
/* USB HID is already initialized */
|
||||
if (lvgl_hid_ctx.task) {
|
||||
return &lvgl_hid_ctx;
|
||||
}
|
||||
|
||||
/* USB HID host driver config */
|
||||
const hid_host_driver_config_t hid_host_driver_config = {
|
||||
.create_background_task = true,
|
||||
.task_priority = 5,
|
||||
.stack_size = 4096,
|
||||
.core_id = 0,
|
||||
.callback = lvgl_port_usb_hid_callback,
|
||||
.callback_arg = &lvgl_hid_ctx,
|
||||
};
|
||||
|
||||
ret = hid_host_install(&hid_host_driver_config);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "USB HID install failed!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lvgl_hid_ctx.queue = xQueueCreate(10, sizeof(lvgl_port_usb_hid_event_t));
|
||||
xTaskCreate(&lvgl_port_usb_hid_task, "hid_task", 4 * 1024, &lvgl_hid_ctx, 2, &lvgl_hid_ctx.task);
|
||||
|
||||
return &lvgl_hid_ctx;
|
||||
}
|
||||
|
||||
static char usb_hid_get_keyboard_char(uint8_t key, uint8_t shift)
|
||||
{
|
||||
char ret_key = 0;
|
||||
|
||||
const uint8_t keycode2ascii [57][2] = {
|
||||
{0, 0}, /* HID_KEY_NO_PRESS */
|
||||
{0, 0}, /* HID_KEY_ROLLOVER */
|
||||
{0, 0}, /* HID_KEY_POST_FAIL */
|
||||
{0, 0}, /* HID_KEY_ERROR_UNDEFINED */
|
||||
{'a', 'A'}, /* HID_KEY_A */
|
||||
{'b', 'B'}, /* HID_KEY_B */
|
||||
{'c', 'C'}, /* HID_KEY_C */
|
||||
{'d', 'D'}, /* HID_KEY_D */
|
||||
{'e', 'E'}, /* HID_KEY_E */
|
||||
{'f', 'F'}, /* HID_KEY_F */
|
||||
{'g', 'G'}, /* HID_KEY_G */
|
||||
{'h', 'H'}, /* HID_KEY_H */
|
||||
{'i', 'I'}, /* HID_KEY_I */
|
||||
{'j', 'J'}, /* HID_KEY_J */
|
||||
{'k', 'K'}, /* HID_KEY_K */
|
||||
{'l', 'L'}, /* HID_KEY_L */
|
||||
{'m', 'M'}, /* HID_KEY_M */
|
||||
{'n', 'N'}, /* HID_KEY_N */
|
||||
{'o', 'O'}, /* HID_KEY_O */
|
||||
{'p', 'P'}, /* HID_KEY_P */
|
||||
{'q', 'Q'}, /* HID_KEY_Q */
|
||||
{'r', 'R'}, /* HID_KEY_R */
|
||||
{'s', 'S'}, /* HID_KEY_S */
|
||||
{'t', 'T'}, /* HID_KEY_T */
|
||||
{'u', 'U'}, /* HID_KEY_U */
|
||||
{'v', 'V'}, /* HID_KEY_V */
|
||||
{'w', 'W'}, /* HID_KEY_W */
|
||||
{'x', 'X'}, /* HID_KEY_X */
|
||||
{'y', 'Y'}, /* HID_KEY_Y */
|
||||
{'z', 'Z'}, /* HID_KEY_Z */
|
||||
{'1', '!'}, /* HID_KEY_1 */
|
||||
{'2', '@'}, /* HID_KEY_2 */
|
||||
{'3', '#'}, /* HID_KEY_3 */
|
||||
{'4', '$'}, /* HID_KEY_4 */
|
||||
{'5', '%'}, /* HID_KEY_5 */
|
||||
{'6', '^'}, /* HID_KEY_6 */
|
||||
{'7', '&'}, /* HID_KEY_7 */
|
||||
{'8', '*'}, /* HID_KEY_8 */
|
||||
{'9', '('}, /* HID_KEY_9 */
|
||||
{'0', ')'}, /* HID_KEY_0 */
|
||||
{'\r', '\r'}, /* HID_KEY_ENTER */
|
||||
{0, 0}, /* HID_KEY_ESC */
|
||||
{'\b', 0}, /* HID_KEY_DEL */
|
||||
{0, 0}, /* HID_KEY_TAB */
|
||||
{' ', ' '}, /* HID_KEY_SPACE */
|
||||
{'-', '_'}, /* HID_KEY_MINUS */
|
||||
{'=', '+'}, /* HID_KEY_EQUAL */
|
||||
{'[', '{'}, /* HID_KEY_OPEN_BRACKET */
|
||||
{']', '}'}, /* HID_KEY_CLOSE_BRACKET */
|
||||
{'\\', '|'}, /* HID_KEY_BACK_SLASH */
|
||||
{'\\', '|'}, /* HID_KEY_SHARP */ // HOTFIX: for NonUS Keyboards repeat HID_KEY_BACK_SLASH
|
||||
{';', ':'}, /* HID_KEY_COLON */
|
||||
{'\'', '"'}, /* HID_KEY_QUOTE */
|
||||
{'`', '~'}, /* HID_KEY_TILDE */
|
||||
{',', '<'}, /* HID_KEY_LESS */
|
||||
{'.', '>'}, /* HID_KEY_GREATER */
|
||||
{'/', '?'} /* HID_KEY_SLASH */
|
||||
};
|
||||
|
||||
if (shift > 1) {
|
||||
shift = 1;
|
||||
}
|
||||
|
||||
if ((key >= HID_KEY_A) && (key <= HID_KEY_SLASH)) {
|
||||
ret_key = keycode2ascii[key][shift];
|
||||
}
|
||||
|
||||
return ret_key;
|
||||
}
|
||||
|
||||
static void lvgl_port_usb_hid_host_interface_callback(hid_host_device_handle_t hid_device_handle, const hid_host_interface_event_t event, void *arg)
|
||||
{
|
||||
hid_host_dev_params_t dev;
|
||||
hid_host_device_get_params(hid_device_handle, &dev);
|
||||
lvgl_port_usb_hid_ctx_t *hid_ctx = (lvgl_port_usb_hid_ctx_t *)arg;
|
||||
uint8_t data[10];
|
||||
unsigned int data_length = 0;
|
||||
|
||||
assert(hid_ctx != NULL);
|
||||
|
||||
switch (event) {
|
||||
case HID_HOST_INTERFACE_EVENT_INPUT_REPORT:
|
||||
hid_host_device_get_raw_input_report_data(hid_device_handle, data, sizeof(data), &data_length);
|
||||
if (dev.proto == HID_PROTOCOL_KEYBOARD) {
|
||||
hid_keyboard_input_report_boot_t *keyboard = (hid_keyboard_input_report_boot_t *)data;
|
||||
if (data_length < sizeof(hid_keyboard_input_report_boot_t)) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < HID_KEYBOARD_KEY_MAX; i++) {
|
||||
if (keyboard->key[i] > HID_KEY_ERROR_UNDEFINED) {
|
||||
char key = 0;
|
||||
|
||||
/* LVGL special keys */
|
||||
if (keyboard->key[i] == HID_KEY_TAB) {
|
||||
if ((keyboard->modifier.left_shift || keyboard->modifier.right_shift)) {
|
||||
key = LV_KEY_PREV;
|
||||
} else {
|
||||
key = LV_KEY_NEXT;
|
||||
}
|
||||
} else if (keyboard->key[i] == HID_KEY_RIGHT) {
|
||||
key = LV_KEY_RIGHT;
|
||||
} else if (keyboard->key[i] == HID_KEY_LEFT) {
|
||||
key = LV_KEY_LEFT;
|
||||
} else if (keyboard->key[i] == HID_KEY_DOWN) {
|
||||
key = LV_KEY_DOWN;
|
||||
} else if (keyboard->key[i] == HID_KEY_UP) {
|
||||
key = LV_KEY_UP;
|
||||
} else if (keyboard->key[i] == HID_KEY_ENTER || keyboard->key[i] == HID_KEY_KEYPAD_ENTER) {
|
||||
key = LV_KEY_ENTER;
|
||||
} else if (keyboard->key[i] == HID_KEY_DELETE) {
|
||||
key = LV_KEY_DEL;
|
||||
} else if (keyboard->key[i] == HID_KEY_HOME) {
|
||||
key = LV_KEY_HOME;
|
||||
} else if (keyboard->key[i] == HID_KEY_END) {
|
||||
key = LV_KEY_END;
|
||||
} else {
|
||||
/* Get ASCII char */
|
||||
key = usb_hid_get_keyboard_char(keyboard->key[i], (keyboard->modifier.left_shift || keyboard->modifier.right_shift));
|
||||
}
|
||||
|
||||
if (key == 0) {
|
||||
ESP_LOGI(TAG, "Not recognized key: %c (%d)", keyboard->key[i], keyboard->key[i]);
|
||||
}
|
||||
hid_ctx->kb.last_key = key;
|
||||
hid_ctx->kb.pressed = true;
|
||||
}
|
||||
}
|
||||
|
||||
} else if (dev.proto == HID_PROTOCOL_MOUSE) {
|
||||
hid_mouse_input_report_boot_t *mouse = (hid_mouse_input_report_boot_t *)data;
|
||||
if (data_length < sizeof(hid_mouse_input_report_boot_t)) {
|
||||
break;
|
||||
}
|
||||
hid_ctx->mouse.left_button = mouse->buttons.button1;
|
||||
hid_ctx->mouse.x += mouse->x_displacement;
|
||||
hid_ctx->mouse.y += mouse->y_displacement;
|
||||
}
|
||||
break;
|
||||
case HID_HOST_INTERFACE_EVENT_TRANSFER_ERROR:
|
||||
break;
|
||||
case HID_HOST_INTERFACE_EVENT_DISCONNECTED:
|
||||
hid_host_device_close(hid_device_handle);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void lvgl_port_usb_hid_task(void *arg)
|
||||
{
|
||||
hid_host_dev_params_t dev;
|
||||
lvgl_port_usb_hid_ctx_t *ctx = (lvgl_port_usb_hid_ctx_t *)arg;
|
||||
hid_host_device_handle_t hid_device_handle = NULL;
|
||||
lvgl_port_usb_hid_event_t msg;
|
||||
|
||||
assert(ctx);
|
||||
|
||||
ctx->running = true;
|
||||
|
||||
while (ctx->running) {
|
||||
if (xQueueReceive(ctx->queue, &msg, pdMS_TO_TICKS(50))) {
|
||||
hid_device_handle = msg.hid_device_handle;
|
||||
hid_host_device_get_params(hid_device_handle, &dev);
|
||||
|
||||
switch (msg.event) {
|
||||
case HID_HOST_DRIVER_EVENT_CONNECTED:
|
||||
/* Handle mouse or keyboard */
|
||||
if (dev.proto == HID_PROTOCOL_KEYBOARD || dev.proto == HID_PROTOCOL_MOUSE) {
|
||||
const hid_host_device_config_t dev_config = {
|
||||
.callback = lvgl_port_usb_hid_host_interface_callback,
|
||||
.callback_arg = ctx
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK( hid_host_device_open(hid_device_handle, &dev_config) );
|
||||
ESP_ERROR_CHECK( hid_class_request_set_idle(hid_device_handle, 0, 0) );
|
||||
ESP_ERROR_CHECK( hid_class_request_set_protocol(hid_device_handle, HID_REPORT_PROTOCOL_BOOT) );
|
||||
ESP_ERROR_CHECK( hid_host_device_start(hid_device_handle) );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xQueueReset(ctx->queue);
|
||||
vQueueDelete(ctx->queue);
|
||||
|
||||
hid_host_uninstall();
|
||||
|
||||
memset(&lvgl_hid_ctx, 0, sizeof(lvgl_port_usb_hid_ctx_t));
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void lvgl_port_usb_hid_read_mouse(lv_indev_drv_t *indev_drv, lv_indev_data_t *data)
|
||||
{
|
||||
int16_t width = 0;
|
||||
int16_t height = 0;
|
||||
assert(indev_drv);
|
||||
lvgl_port_usb_hid_ctx_t *ctx = (lvgl_port_usb_hid_ctx_t *)indev_drv->user_data;
|
||||
assert(ctx);
|
||||
|
||||
if (indev_drv->disp->driver->rotated == LV_DISP_ROT_NONE || indev_drv->disp->driver->rotated == LV_DISP_ROT_180) {
|
||||
width = indev_drv->disp->driver->hor_res;
|
||||
height = indev_drv->disp->driver->ver_res;
|
||||
} else {
|
||||
width = indev_drv->disp->driver->ver_res;
|
||||
height = indev_drv->disp->driver->hor_res;
|
||||
}
|
||||
|
||||
/* Screen borders */
|
||||
if (ctx->mouse.x < 0) {
|
||||
ctx->mouse.x = 0;
|
||||
} else if (ctx->mouse.x > width * ctx->mouse.sensitivity) {
|
||||
ctx->mouse.x = width * ctx->mouse.sensitivity;
|
||||
}
|
||||
if (ctx->mouse.y < 0) {
|
||||
ctx->mouse.y = 0;
|
||||
} else if (ctx->mouse.y > height * ctx->mouse.sensitivity) {
|
||||
ctx->mouse.y = height * ctx->mouse.sensitivity;
|
||||
}
|
||||
|
||||
/* Get coordinates by rotation with sensitivity */
|
||||
switch (indev_drv->disp->driver->rotated) {
|
||||
case LV_DISP_ROT_NONE:
|
||||
data->point.x = ctx->mouse.x / ctx->mouse.sensitivity;
|
||||
data->point.y = ctx->mouse.y / ctx->mouse.sensitivity;
|
||||
break;
|
||||
case LV_DISP_ROT_90:
|
||||
data->point.y = width - ctx->mouse.x / ctx->mouse.sensitivity;
|
||||
data->point.x = ctx->mouse.y / ctx->mouse.sensitivity;
|
||||
break;
|
||||
case LV_DISP_ROT_180:
|
||||
data->point.x = width - ctx->mouse.x / ctx->mouse.sensitivity;
|
||||
data->point.y = height - ctx->mouse.y / ctx->mouse.sensitivity;
|
||||
break;
|
||||
case LV_DISP_ROT_270:
|
||||
data->point.y = ctx->mouse.x / ctx->mouse.sensitivity;
|
||||
data->point.x = height - ctx->mouse.y / ctx->mouse.sensitivity;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ctx->mouse.left_button) {
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
} else {
|
||||
data->state = LV_INDEV_STATE_RELEASED;
|
||||
}
|
||||
}
|
||||
|
||||
static void lvgl_port_usb_hid_read_kb(lv_indev_drv_t *indev_drv, lv_indev_data_t *data)
|
||||
{
|
||||
assert(indev_drv);
|
||||
lvgl_port_usb_hid_ctx_t *ctx = (lvgl_port_usb_hid_ctx_t *)indev_drv->user_data;
|
||||
assert(ctx);
|
||||
|
||||
data->key = ctx->kb.last_key;
|
||||
if (ctx->kb.pressed) {
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
ctx->kb.pressed = false;
|
||||
} else {
|
||||
data->state = LV_INDEV_STATE_RELEASED;
|
||||
ctx->kb.last_key = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void lvgl_port_usb_hid_callback(hid_host_device_handle_t hid_device_handle, const hid_host_driver_event_t event, void *arg)
|
||||
{
|
||||
lvgl_port_usb_hid_ctx_t *hid_ctx = (lvgl_port_usb_hid_ctx_t *)arg;
|
||||
|
||||
const lvgl_port_usb_hid_event_t msg = {
|
||||
.hid_device_handle = hid_device_handle,
|
||||
.event = event,
|
||||
.arg = arg
|
||||
};
|
||||
|
||||
xQueueSend(hid_ctx->queue, &msg, 0);
|
||||
}
|
||||
@@ -0,0 +1,321 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "esp_system.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_timer.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/portmacro.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "esp_lvgl_port.h"
|
||||
#include "esp_lvgl_port_priv.h"
|
||||
#include "lvgl.h"
|
||||
|
||||
static const char *TAG = "LVGL";
|
||||
|
||||
#define ESP_LVGL_PORT_TASK_MUX_DELAY_MS 10000
|
||||
|
||||
/*******************************************************************************
|
||||
* Types definitions
|
||||
*******************************************************************************/
|
||||
|
||||
typedef struct lvgl_port_ctx_s {
|
||||
TaskHandle_t lvgl_task;
|
||||
SemaphoreHandle_t lvgl_mux;
|
||||
SemaphoreHandle_t timer_mux;
|
||||
EventGroupHandle_t lvgl_events;
|
||||
SemaphoreHandle_t task_init_mux;
|
||||
esp_timer_handle_t tick_timer;
|
||||
bool running;
|
||||
int task_max_sleep_ms;
|
||||
int timer_period_ms;
|
||||
} lvgl_port_ctx_t;
|
||||
|
||||
/*******************************************************************************
|
||||
* Local variables
|
||||
*******************************************************************************/
|
||||
static lvgl_port_ctx_t lvgl_port_ctx;
|
||||
|
||||
/*******************************************************************************
|
||||
* Function definitions
|
||||
*******************************************************************************/
|
||||
static void lvgl_port_task(void *arg);
|
||||
static esp_err_t lvgl_port_tick_init(void);
|
||||
static void lvgl_port_task_deinit(void);
|
||||
|
||||
/*******************************************************************************
|
||||
* Public API functions
|
||||
*******************************************************************************/
|
||||
|
||||
esp_err_t lvgl_port_init(const lvgl_port_cfg_t *cfg)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
ESP_GOTO_ON_FALSE(cfg, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
ESP_GOTO_ON_FALSE(cfg->task_affinity < (configNUM_CORES), ESP_ERR_INVALID_ARG, err, TAG, "Bad core number for task! Maximum core number is %d", (configNUM_CORES - 1));
|
||||
|
||||
memset(&lvgl_port_ctx, 0, sizeof(lvgl_port_ctx));
|
||||
|
||||
/* Tick init */
|
||||
lvgl_port_ctx.timer_period_ms = cfg->timer_period_ms;
|
||||
/* Create task */
|
||||
lvgl_port_ctx.task_max_sleep_ms = cfg->task_max_sleep_ms;
|
||||
if (lvgl_port_ctx.task_max_sleep_ms == 0) {
|
||||
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();
|
||||
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_events = xEventGroupCreate();
|
||||
ESP_GOTO_ON_FALSE(lvgl_port_ctx.lvgl_events, ESP_ERR_NO_MEM, err, TAG, "Create LVGL Event Group fail!");
|
||||
|
||||
BaseType_t res;
|
||||
if (cfg->task_affinity < 0) {
|
||||
res = xTaskCreate(lvgl_port_task, "taskLVGL", cfg->task_stack, xTaskGetCurrentTaskHandle(), cfg->task_priority, &lvgl_port_ctx.lvgl_task);
|
||||
} else {
|
||||
res = xTaskCreatePinnedToCore(lvgl_port_task, "taskLVGL", cfg->task_stack, xTaskGetCurrentTaskHandle(), 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!");
|
||||
|
||||
// Wait until taskLVGL starts
|
||||
if (ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(5000)) == 0) {
|
||||
ret = ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
err:
|
||||
if (ret != ESP_OK) {
|
||||
lvgl_port_deinit();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_resume(void)
|
||||
{
|
||||
esp_err_t ret = ESP_ERR_INVALID_STATE;
|
||||
|
||||
if (lvgl_port_ctx.tick_timer != NULL) {
|
||||
lv_timer_enable(true);
|
||||
ret = esp_timer_start_periodic(lvgl_port_ctx.tick_timer, lvgl_port_ctx.timer_period_ms * 1000);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_stop(void)
|
||||
{
|
||||
esp_err_t ret = ESP_ERR_INVALID_STATE;
|
||||
|
||||
if (lvgl_port_ctx.tick_timer != NULL) {
|
||||
lv_timer_enable(false);
|
||||
ret = esp_timer_stop(lvgl_port_ctx.tick_timer);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_deinit(void)
|
||||
{
|
||||
/* Stop and delete timer */
|
||||
if (lvgl_port_ctx.tick_timer != NULL) {
|
||||
esp_timer_stop(lvgl_port_ctx.tick_timer);
|
||||
esp_timer_delete(lvgl_port_ctx.tick_timer);
|
||||
lvgl_port_ctx.tick_timer = NULL;
|
||||
}
|
||||
|
||||
/* Stop running task */
|
||||
if (lvgl_port_ctx.running) {
|
||||
lvgl_port_ctx.running = false;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
bool lvgl_port_lock(uint32_t timeout_ms)
|
||||
{
|
||||
assert(lvgl_port_ctx.lvgl_mux && "lvgl_port_init must be called first");
|
||||
|
||||
const TickType_t timeout_ticks = (timeout_ms == 0) ? portMAX_DELAY : pdMS_TO_TICKS(timeout_ms);
|
||||
return xSemaphoreTakeRecursive(lvgl_port_ctx.lvgl_mux, timeout_ticks) == pdTRUE;
|
||||
}
|
||||
|
||||
void lvgl_port_unlock(void)
|
||||
{
|
||||
assert(lvgl_port_ctx.lvgl_mux && "lvgl_port_init must be called first");
|
||||
xSemaphoreGiveRecursive(lvgl_port_ctx.lvgl_mux);
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_task_wake(lvgl_port_event_type_t event, void *param)
|
||||
{
|
||||
EventBits_t bits = 0;
|
||||
if (!lvgl_port_ctx.lvgl_events) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
/* Get unprocessed bits */
|
||||
if (xPortInIsrContext() == pdTRUE) {
|
||||
bits = xEventGroupGetBitsFromISR(lvgl_port_ctx.lvgl_events);
|
||||
} else {
|
||||
bits = xEventGroupGetBits(lvgl_port_ctx.lvgl_events);
|
||||
}
|
||||
|
||||
/* Set event */
|
||||
bits |= event;
|
||||
|
||||
/* Save */
|
||||
if (xPortInIsrContext() == pdTRUE) {
|
||||
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
|
||||
xEventGroupSetBitsFromISR(lvgl_port_ctx.lvgl_events, bits, &xHigherPriorityTaskWoken);
|
||||
if (xHigherPriorityTaskWoken) {
|
||||
portYIELD_FROM_ISR( );
|
||||
}
|
||||
} else {
|
||||
xEventGroupSetBits(lvgl_port_ctx.lvgl_events, bits);
|
||||
}
|
||||
|
||||
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
|
||||
*******************************************************************************/
|
||||
|
||||
static void lvgl_port_task(void *arg)
|
||||
{
|
||||
TaskHandle_t task_to_notify = (TaskHandle_t)arg;
|
||||
EventBits_t events = 0;
|
||||
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();
|
||||
/* LVGL is initialized, notify lvgl_port_init() function about it */
|
||||
xTaskNotifyGive(task_to_notify);
|
||||
/* Tick init */
|
||||
lvgl_port_tick_init();
|
||||
|
||||
ESP_LOGI(TAG, "Starting LVGL task");
|
||||
lvgl_port_ctx.running = true;
|
||||
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);
|
||||
events = xEventGroupWaitBits(lvgl_port_ctx.lvgl_events, 0xFF, pdTRUE, pdFALSE, wait);
|
||||
|
||||
if (lv_display_get_default() && lvgl_port_lock(0)) {
|
||||
|
||||
/* Call read input devices */
|
||||
if (events & LVGL_PORT_EVENT_TOUCH) {
|
||||
xSemaphoreTake(lvgl_port_ctx.timer_mux, portMAX_DELAY);
|
||||
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();
|
||||
lvgl_port_unlock();
|
||||
} else {
|
||||
task_delay_ms = 1; /*Keep trying*/
|
||||
}
|
||||
|
||||
if (task_delay_ms == LV_NO_TIMER_READY) {
|
||||
task_delay_ms = lvgl_port_ctx.task_max_sleep_ms;
|
||||
}
|
||||
|
||||
/* Minimal dealy for the task. When there is too much events, it takes time for other tasks and interrupts. */
|
||||
vTaskDelay(1);
|
||||
}
|
||||
|
||||
/* Give semaphore back */
|
||||
xSemaphoreGive(lvgl_port_ctx.task_init_mux);
|
||||
|
||||
/* Close task */
|
||||
vTaskDelete( NULL );
|
||||
}
|
||||
|
||||
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) {
|
||||
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_events) {
|
||||
vEventGroupDelete(lvgl_port_ctx.lvgl_events);
|
||||
}
|
||||
memset(&lvgl_port_ctx, 0, sizeof(lvgl_port_ctx));
|
||||
#if LV_ENABLE_GC || !LV_MEM_CUSTOM
|
||||
/* Deinitialize LVGL */
|
||||
lv_deinit();
|
||||
#endif
|
||||
}
|
||||
|
||||
static void lvgl_port_tick_increment(void *arg)
|
||||
{
|
||||
xSemaphoreTake(lvgl_port_ctx.timer_mux, portMAX_DELAY);
|
||||
/* Tell LVGL how many milliseconds have elapsed */
|
||||
lv_tick_inc(lvgl_port_ctx.timer_period_ms);
|
||||
xSemaphoreGive(lvgl_port_ctx.timer_mux);
|
||||
}
|
||||
|
||||
static esp_err_t lvgl_port_tick_init(void)
|
||||
{
|
||||
// Tick interface for LVGL (using esp_timer to generate 2ms periodic event)
|
||||
const esp_timer_create_args_t lvgl_tick_timer_args = {
|
||||
.callback = &lvgl_port_tick_increment,
|
||||
.name = "LVGL tick",
|
||||
};
|
||||
ESP_RETURN_ON_ERROR(esp_timer_create(&lvgl_tick_timer_args, &lvgl_port_ctx.tick_timer), TAG, "Creating LVGL timer filed!");
|
||||
return esp_timer_start_periodic(lvgl_port_ctx.tick_timer, lvgl_port_ctx.timer_period_ms * 1000);
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_lvgl_port.h"
|
||||
|
||||
static const char *TAG = "LVGL";
|
||||
|
||||
/*******************************************************************************
|
||||
* Types definitions
|
||||
*******************************************************************************/
|
||||
|
||||
typedef enum {
|
||||
LVGL_PORT_NAV_BTN_PREV,
|
||||
LVGL_PORT_NAV_BTN_NEXT,
|
||||
LVGL_PORT_NAV_BTN_ENTER,
|
||||
LVGL_PORT_NAV_BTN_CNT,
|
||||
} lvgl_port_nav_btns_t;
|
||||
|
||||
typedef struct {
|
||||
button_handle_t btn[LVGL_PORT_NAV_BTN_CNT]; /* Button handlers */
|
||||
lv_indev_t *indev; /* LVGL input device driver */
|
||||
bool btn_prev; /* Button prev state */
|
||||
bool btn_next; /* Button next state */
|
||||
bool btn_enter; /* Button enter state */
|
||||
} lvgl_port_nav_btns_ctx_t;
|
||||
|
||||
/*******************************************************************************
|
||||
* Function definitions
|
||||
*******************************************************************************/
|
||||
|
||||
static void lvgl_port_navigation_buttons_read(lv_indev_t *indev_drv, lv_indev_data_t *data);
|
||||
static void lvgl_port_btn_down_handler(void *arg, void *arg2);
|
||||
static void lvgl_port_btn_up_handler(void *arg, void *arg2);
|
||||
|
||||
/*******************************************************************************
|
||||
* Public API functions
|
||||
*******************************************************************************/
|
||||
|
||||
lv_indev_t *lvgl_port_add_navigation_buttons(const lvgl_port_nav_btns_cfg_t *buttons_cfg)
|
||||
{
|
||||
lv_indev_t *indev;
|
||||
esp_err_t ret = ESP_OK;
|
||||
assert(buttons_cfg != NULL);
|
||||
assert(buttons_cfg->disp != NULL);
|
||||
|
||||
/* Touch context */
|
||||
lvgl_port_nav_btns_ctx_t *buttons_ctx = malloc(sizeof(lvgl_port_nav_btns_ctx_t));
|
||||
if (buttons_ctx == NULL) {
|
||||
ESP_LOGE(TAG, "Not enough memory for buttons context allocation!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if BUTTON_VER_MAJOR < 4
|
||||
/* Previous button */
|
||||
if (buttons_cfg->button_prev != NULL) {
|
||||
buttons_ctx->btn[LVGL_PORT_NAV_BTN_PREV] = iot_button_create(buttons_cfg->button_prev);
|
||||
ESP_GOTO_ON_FALSE(buttons_ctx->btn[LVGL_PORT_NAV_BTN_PREV], ESP_ERR_NO_MEM, err, TAG, "Not enough memory for button create!");
|
||||
}
|
||||
|
||||
/* Next button */
|
||||
if (buttons_cfg->button_next != NULL) {
|
||||
buttons_ctx->btn[LVGL_PORT_NAV_BTN_NEXT] = iot_button_create(buttons_cfg->button_next);
|
||||
ESP_GOTO_ON_FALSE(buttons_ctx->btn[LVGL_PORT_NAV_BTN_NEXT], ESP_ERR_NO_MEM, err, TAG, "Not enough memory for button create!");
|
||||
}
|
||||
|
||||
/* Enter button */
|
||||
if (buttons_cfg->button_enter != NULL) {
|
||||
buttons_ctx->btn[LVGL_PORT_NAV_BTN_ENTER] = iot_button_create(buttons_cfg->button_enter);
|
||||
ESP_GOTO_ON_FALSE(buttons_ctx->btn[LVGL_PORT_NAV_BTN_ENTER], ESP_ERR_NO_MEM, err, TAG, "Not enough memory for button create!");
|
||||
}
|
||||
#else
|
||||
ESP_GOTO_ON_FALSE(buttons_cfg->button_prev && buttons_cfg->button_next && buttons_cfg->button_enter, ESP_ERR_INVALID_ARG, err, TAG, "Invalid some button handler!");
|
||||
|
||||
buttons_ctx->btn[LVGL_PORT_NAV_BTN_PREV] = buttons_cfg->button_prev;
|
||||
buttons_ctx->btn[LVGL_PORT_NAV_BTN_NEXT] = buttons_cfg->button_next;
|
||||
buttons_ctx->btn[LVGL_PORT_NAV_BTN_ENTER] = buttons_cfg->button_enter;
|
||||
#endif
|
||||
|
||||
/* Button handlers */
|
||||
for (int i = 0; i < LVGL_PORT_NAV_BTN_CNT; i++) {
|
||||
#if BUTTON_VER_MAJOR < 4
|
||||
ESP_ERROR_CHECK(iot_button_register_cb(buttons_ctx->btn[i], BUTTON_PRESS_DOWN, lvgl_port_btn_down_handler, buttons_ctx));
|
||||
ESP_ERROR_CHECK(iot_button_register_cb(buttons_ctx->btn[i], BUTTON_PRESS_UP, lvgl_port_btn_up_handler, buttons_ctx));
|
||||
#else
|
||||
ESP_ERROR_CHECK(iot_button_register_cb(buttons_ctx->btn[i], BUTTON_PRESS_DOWN, NULL, lvgl_port_btn_down_handler, buttons_ctx));
|
||||
ESP_ERROR_CHECK(iot_button_register_cb(buttons_ctx->btn[i], BUTTON_PRESS_UP, NULL, lvgl_port_btn_up_handler, buttons_ctx));
|
||||
#endif
|
||||
}
|
||||
|
||||
buttons_ctx->btn_prev = false;
|
||||
buttons_ctx->btn_next = false;
|
||||
buttons_ctx->btn_enter = false;
|
||||
|
||||
lvgl_port_lock(0);
|
||||
/* Register a touchpad input device */
|
||||
indev = lv_indev_create();
|
||||
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_disp(indev, buttons_cfg->disp);
|
||||
lv_indev_set_driver_data(indev, buttons_ctx);
|
||||
//buttons_ctx->indev->long_press_repeat_time = 300;
|
||||
buttons_ctx->indev = indev;
|
||||
lvgl_port_unlock();
|
||||
|
||||
return indev;
|
||||
|
||||
err:
|
||||
if (ret != ESP_OK) {
|
||||
for (int i = 0; i < LVGL_PORT_NAV_BTN_CNT; i++) {
|
||||
if (buttons_ctx->btn[i] != NULL) {
|
||||
iot_button_delete(buttons_ctx->btn[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (buttons_ctx != NULL) {
|
||||
free(buttons_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_remove_navigation_buttons(lv_indev_t *buttons)
|
||||
{
|
||||
assert(buttons);
|
||||
lvgl_port_nav_btns_ctx_t *buttons_ctx = (lvgl_port_nav_btns_ctx_t *)lv_indev_get_driver_data(buttons);
|
||||
|
||||
lvgl_port_lock(0);
|
||||
/* Remove input device driver */
|
||||
lv_indev_delete(buttons);
|
||||
lvgl_port_unlock();
|
||||
|
||||
if (buttons_ctx) {
|
||||
free(buttons_ctx);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Private functions
|
||||
*******************************************************************************/
|
||||
|
||||
|
||||
static void lvgl_port_navigation_buttons_read(lv_indev_t *indev_drv, lv_indev_data_t *data)
|
||||
{
|
||||
static uint32_t last_key = 0;
|
||||
|
||||
assert(indev_drv);
|
||||
lvgl_port_nav_btns_ctx_t *ctx = (lvgl_port_nav_btns_ctx_t *)lv_indev_get_driver_data(indev_drv);
|
||||
assert(ctx);
|
||||
|
||||
/* Buttons */
|
||||
if (ctx->btn_prev) {
|
||||
data->key = LV_KEY_LEFT;
|
||||
last_key = LV_KEY_LEFT;
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
} else if (ctx->btn_next) {
|
||||
data->key = LV_KEY_RIGHT;
|
||||
last_key = LV_KEY_RIGHT;
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
} else if (ctx->btn_enter) {
|
||||
data->key = LV_KEY_ENTER;
|
||||
last_key = LV_KEY_ENTER;
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
} else {
|
||||
data->key = last_key;
|
||||
data->state = LV_INDEV_STATE_RELEASED;
|
||||
}
|
||||
}
|
||||
|
||||
static void lvgl_port_btn_down_handler(void *arg, void *arg2)
|
||||
{
|
||||
lvgl_port_nav_btns_ctx_t *ctx = (lvgl_port_nav_btns_ctx_t *) arg2;
|
||||
button_handle_t button = (button_handle_t)arg;
|
||||
if (ctx && button) {
|
||||
/* PREV */
|
||||
if (button == ctx->btn[LVGL_PORT_NAV_BTN_PREV]) {
|
||||
ctx->btn_prev = true;
|
||||
}
|
||||
/* NEXT */
|
||||
if (button == ctx->btn[LVGL_PORT_NAV_BTN_NEXT]) {
|
||||
ctx->btn_next = true;
|
||||
}
|
||||
/* ENTER */
|
||||
if (button == ctx->btn[LVGL_PORT_NAV_BTN_ENTER]) {
|
||||
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)
|
||||
{
|
||||
lvgl_port_nav_btns_ctx_t *ctx = (lvgl_port_nav_btns_ctx_t *) arg2;
|
||||
button_handle_t button = (button_handle_t)arg;
|
||||
if (ctx && button) {
|
||||
/* PREV */
|
||||
if (button == ctx->btn[LVGL_PORT_NAV_BTN_PREV]) {
|
||||
ctx->btn_prev = false;
|
||||
}
|
||||
/* NEXT */
|
||||
if (button == ctx->btn[LVGL_PORT_NAV_BTN_NEXT]) {
|
||||
ctx->btn_next = false;
|
||||
}
|
||||
/* ENTER */
|
||||
if (button == ctx->btn[LVGL_PORT_NAV_BTN_ENTER]) {
|
||||
ctx->btn_enter = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Wake LVGL task, if needed */
|
||||
lvgl_port_task_wake(LVGL_PORT_EVENT_TOUCH, ctx->indev);
|
||||
}
|
||||
@@ -0,0 +1,754 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_heap_caps.h"
|
||||
#include "esp_idf_version.h"
|
||||
#include "esp_lcd_panel_io.h"
|
||||
#include "esp_lcd_panel_ops.h"
|
||||
#include "esp_lvgl_port.h"
|
||||
#include "esp_lvgl_port_priv.h"
|
||||
|
||||
#define LVGL_PORT_PPA (CONFIG_LVGL_PORT_ENABLE_PPA)
|
||||
|
||||
#if LVGL_PORT_PPA
|
||||
#include "../common/ppa/lcd_ppa.h"
|
||||
#endif
|
||||
|
||||
#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))
|
||||
#define LVGL_PORT_HANDLE_FLUSH_READY 0
|
||||
#else
|
||||
#define LVGL_PORT_HANDLE_FLUSH_READY 1
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_LV_DRAW_BUF_ALIGN
|
||||
#define CONFIG_LV_DRAW_BUF_ALIGN 1
|
||||
#endif
|
||||
|
||||
static const char *TAG = "LVGL";
|
||||
|
||||
/*******************************************************************************
|
||||
* Types definitions
|
||||
*******************************************************************************/
|
||||
|
||||
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_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 */
|
||||
lv_color_t *draw_buffs[3]; /* Display draw buffers */
|
||||
uint8_t *oled_buffer;
|
||||
lv_display_t *disp_drv; /* LVGL display driver */
|
||||
lv_display_rotation_t current_rotation;
|
||||
SemaphoreHandle_t trans_sem; /* Idle transfer mutex */
|
||||
#if LVGL_PORT_PPA
|
||||
lvgl_port_ppa_handle_t ppa_handle;
|
||||
#endif //LVGL_PORT_PPA
|
||||
struct {
|
||||
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 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;
|
||||
} lvgl_port_display_ctx_t;
|
||||
|
||||
/*******************************************************************************
|
||||
* 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
|
||||
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
|
||||
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_rotation_update(lvgl_port_display_ctx_t *disp_ctx);
|
||||
static void lvgl_port_display_invalidate_callback(lv_event_t *e);
|
||||
|
||||
/*******************************************************************************
|
||||
* Public API functions
|
||||
*******************************************************************************/
|
||||
|
||||
lv_display_t *lvgl_port_add_disp(const lvgl_port_display_cfg_t *disp_cfg)
|
||||
{
|
||||
lvgl_port_lock(0);
|
||||
lv_disp_t *disp = lvgl_port_add_disp_priv(disp_cfg, NULL);
|
||||
|
||||
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_OTHER;
|
||||
|
||||
assert(disp_cfg->io_handle != NULL);
|
||||
|
||||
#if LVGL_PORT_HANDLE_FLUSH_READY
|
||||
const esp_lcd_panel_io_callbacks_t cbs = {
|
||||
.on_color_trans_done = lvgl_port_flush_io_ready_callback,
|
||||
};
|
||||
/* Register done callback */
|
||||
esp_lcd_panel_io_register_event_callbacks(disp_ctx->io_handle, &cbs, disp);
|
||||
#endif
|
||||
|
||||
/* Apply rotation from initial display configuration */
|
||||
lvgl_port_disp_rotation_update(disp_ctx);
|
||||
}
|
||||
lvgl_port_unlock();
|
||||
|
||||
return disp;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
assert(dsi_cfg != NULL);
|
||||
const lvgl_port_disp_priv_cfg_t priv_cfg = {
|
||||
.avoid_tearing = dsi_cfg->flags.avoid_tearing,
|
||||
};
|
||||
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;
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_remove_disp(lv_display_t *disp)
|
||||
{
|
||||
assert(disp);
|
||||
lvgl_port_display_ctx_t *disp_ctx = (lvgl_port_display_ctx_t *)lv_display_get_driver_data(disp);
|
||||
|
||||
lvgl_port_lock(0);
|
||||
lv_disp_remove(disp);
|
||||
lvgl_port_unlock();
|
||||
|
||||
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->trans_sem) {
|
||||
vSemaphoreDelete(disp_ctx->trans_sem);
|
||||
}
|
||||
#if LVGL_PORT_PPA
|
||||
if (disp_ctx->ppa_handle) {
|
||||
lvgl_port_ppa_delete(disp_ctx->ppa_handle);
|
||||
}
|
||||
#endif //LVGL_PORT_PPA
|
||||
|
||||
free(disp_ctx);
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
void lvgl_port_flush_ready(lv_display_t *disp)
|
||||
{
|
||||
assert(disp);
|
||||
lv_disp_flush_ready(disp);
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* 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);
|
||||
uint8_t color_bytes = lv_color_format_get_size(display_color_format);
|
||||
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 aligned 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_aligned_alloc(CONFIG_LV_DRAW_BUF_ALIGN, buffer_size * color_bytes, 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_aligned_alloc(CONFIG_LV_DRAW_BUF_ALIGN, buffer_size * color_bytes, 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 * color_bytes, 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 * color_bytes, 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 * color_bytes, LV_DISPLAY_RENDER_MODE_FULL);
|
||||
} else {
|
||||
lv_display_set_buffers(disp, buf1, buf2, buffer_size * color_bytes, 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) {
|
||||
#if LVGL_PORT_PPA
|
||||
ESP_LOGI(TAG, "Setting PPA context for SW rotation");
|
||||
uint32_t pixel_format = COLOR_PIXEL_RGB565;
|
||||
if (disp_cfg->color_format == LV_COLOR_FORMAT_RGB888) {
|
||||
pixel_format = COLOR_PIXEL_RGB888;
|
||||
}
|
||||
|
||||
/* Create LCD PPA for rotation */
|
||||
lvgl_port_ppa_cfg_t ppa_cfg = {
|
||||
.buffer_size = disp_cfg->buffer_size * color_bytes,
|
||||
.color_space = COLOR_SPACE_RGB,
|
||||
.pixel_format = pixel_format,
|
||||
.flags = {
|
||||
.buff_dma = disp_cfg->flags.buff_dma,
|
||||
.buff_spiram = disp_cfg->flags.buff_spiram,
|
||||
}
|
||||
};
|
||||
disp_ctx->ppa_handle = lvgl_port_ppa_create(&ppa_cfg);
|
||||
assert(disp_ctx->ppa_handle != NULL);
|
||||
#else
|
||||
disp_ctx->draw_buffs[2] = heap_caps_malloc(buffer_size * color_bytes, 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!");
|
||||
|
||||
#endif //LVGL_PORT_PPA
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
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;
|
||||
assert(disp_drv != NULL);
|
||||
lv_disp_flush_ready(disp_drv);
|
||||
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
|
||||
|
||||
#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_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 ver_res = lv_display_get_physical_vertical_resolution(display);
|
||||
uint16_t res = hor_res;
|
||||
bool swap_xy = (lv_display_get_rotation(display) == LV_DISPLAY_ROTATION_90 || lv_display_get_rotation(display) == LV_DISPLAY_ROTATION_270);
|
||||
|
||||
int x1 = area->x1;
|
||||
int x2 = area->x2;
|
||||
int y1 = area->y1;
|
||||
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;
|
||||
for (int y = y1; y <= y2; y++) {
|
||||
for (int x = x1; x <= x2; x++) {
|
||||
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) {
|
||||
out_x = y;
|
||||
out_y = x;
|
||||
res = ver_res;
|
||||
} else {
|
||||
out_x = x;
|
||||
out_y = y;
|
||||
res = hor_res;
|
||||
}
|
||||
|
||||
/* Write to the buffer as required for the display.
|
||||
* It writes only 1-bit for monochrome displays mapped vertically.*/
|
||||
uint8_t *outbuf = NULL;
|
||||
outbuf = *color_map + res * (out_y >> 3) + (out_x);
|
||||
if (chroma_color) {
|
||||
(*outbuf) &= ~(1 << (out_y % 8));
|
||||
} else {
|
||||
(*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)
|
||||
{
|
||||
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);
|
||||
assert(disp_ctx != NULL);
|
||||
|
||||
int offsetx1 = area->x1;
|
||||
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)) {
|
||||
#if LVGL_PORT_PPA
|
||||
if (disp_ctx->ppa_handle) {
|
||||
/* Screen vertical size */
|
||||
int32_t hres = lv_display_get_horizontal_resolution(drv);
|
||||
int32_t vres = lv_display_get_vertical_resolution(drv);
|
||||
lvgl_port_ppa_disp_rotate_t rotate_cfg = {
|
||||
.in_buff = color_map,
|
||||
.area = {
|
||||
.x1 = area->x1,
|
||||
.x2 = area->x2,
|
||||
.y1 = area->y1,
|
||||
.y2 = area->y2,
|
||||
},
|
||||
.disp_size = {
|
||||
.hres = hres,
|
||||
.vres = vres,
|
||||
},
|
||||
.rotation = disp_ctx->current_rotation,
|
||||
.ppa_mode = PPA_TRANS_MODE_BLOCKING,
|
||||
.swap_bytes = (disp_ctx->flags.swap_bytes ? true : false),
|
||||
.user_data = disp_ctx
|
||||
};
|
||||
/* Do operation */
|
||||
esp_err_t err = lvgl_port_ppa_rotate(disp_ctx->ppa_handle, &rotate_cfg);
|
||||
if (err == ESP_OK) {
|
||||
color_map = lvgl_port_ppa_get_output_buffer(disp_ctx->ppa_handle);
|
||||
offsetx1 = rotate_cfg.area.x1;
|
||||
offsetx2 = rotate_cfg.area.x2;
|
||||
offsety1 = rotate_cfg.area.y1;
|
||||
offsety2 = rotate_cfg.area.y2;
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* 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;
|
||||
}
|
||||
#endif //LVGL_PORT_PPA
|
||||
}
|
||||
|
||||
if (disp_ctx->flags.swap_bytes) {
|
||||
size_t len = lv_area_get_size(area);
|
||||
lv_draw_sw_rgb565_swap(color_map, len);
|
||||
}
|
||||
/* Transfer data in buffer for monochromatic screen */
|
||||
if (disp_ctx->flags.monochrome) {
|
||||
_lvgl_port_transform_monochrome(drv, area, &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)) {
|
||||
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, 0, 0, lv_disp_get_hor_res(drv), lv_disp_get_ver_res(drv), 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, 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)
|
||||
{
|
||||
assert(e);
|
||||
lvgl_port_display_ctx_t *disp_ctx = (lvgl_port_display_ctx_t *)lv_event_get_user_data(e);
|
||||
lvgl_port_disp_rotation_update(disp_ctx);
|
||||
}
|
||||
|
||||
static void lvgl_port_display_invalidate_callback(lv_event_t *e)
|
||||
{
|
||||
/* Wake LVGL task, if needed */
|
||||
lvgl_port_task_wake(LVGL_PORT_EVENT_DISPLAY, NULL);
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_lvgl_port.h"
|
||||
|
||||
static const char *TAG = "LVGL";
|
||||
|
||||
/*******************************************************************************
|
||||
* Types definitions
|
||||
*******************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
knob_handle_t knob_handle; /* Encoder knob handlers */
|
||||
button_handle_t btn_handle; /* Encoder button handlers */
|
||||
lv_indev_t *indev; /* LVGL input device driver */
|
||||
bool btn_enter; /* Encoder button enter state */
|
||||
int32_t diff; /* Encoder diff */
|
||||
} lvgl_port_encoder_ctx_t;
|
||||
|
||||
/*******************************************************************************
|
||||
* Function definitions
|
||||
*******************************************************************************/
|
||||
|
||||
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 *button_handle, void *usr_data);
|
||||
static void lvgl_port_encoder_btn_up_handler(void *button_handle, void *usr_data);
|
||||
static void lvgl_port_encoder_left_handler(void *arg, void *arg2);
|
||||
static void lvgl_port_encoder_right_handler(void *arg, void *arg2);
|
||||
static int32_t lvgl_port_calculate_diff(knob_handle_t knob, knob_event_t event);
|
||||
|
||||
/*******************************************************************************
|
||||
* Public API functions
|
||||
*******************************************************************************/
|
||||
|
||||
lv_indev_t *lvgl_port_add_encoder(const lvgl_port_encoder_cfg_t *encoder_cfg)
|
||||
{
|
||||
lv_indev_t *indev;
|
||||
esp_err_t ret = ESP_OK;
|
||||
assert(encoder_cfg != NULL);
|
||||
assert(encoder_cfg->disp != NULL);
|
||||
|
||||
/* Encoder context */
|
||||
lvgl_port_encoder_ctx_t *encoder_ctx = malloc(sizeof(lvgl_port_encoder_ctx_t));
|
||||
if (encoder_ctx == NULL) {
|
||||
ESP_LOGE(TAG, "Not enough memory for encoder context allocation!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Encoder_a/b */
|
||||
if (encoder_cfg->encoder_a_b != NULL) {
|
||||
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_ERROR_CHECK(iot_knob_register_cb(encoder_ctx->knob_handle, KNOB_LEFT, lvgl_port_encoder_left_handler, encoder_ctx));
|
||||
ESP_ERROR_CHECK(iot_knob_register_cb(encoder_ctx->knob_handle, KNOB_RIGHT, lvgl_port_encoder_right_handler, encoder_ctx));
|
||||
}
|
||||
|
||||
/* Encoder Enter */
|
||||
if (encoder_cfg->encoder_enter != NULL) {
|
||||
#if BUTTON_VER_MAJOR < 4
|
||||
encoder_ctx->btn_handle = iot_button_create(encoder_cfg->encoder_enter);
|
||||
ESP_GOTO_ON_FALSE(encoder_ctx->btn_handle, ESP_ERR_NO_MEM, err, TAG, "Not enough memory for button create!");
|
||||
#else
|
||||
ESP_GOTO_ON_FALSE(encoder_cfg->encoder_enter, ESP_ERR_INVALID_ARG, err, TAG, "Invalid button handler!");
|
||||
encoder_ctx->btn_handle = encoder_cfg->encoder_enter;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if BUTTON_VER_MAJOR < 4
|
||||
ESP_ERROR_CHECK(iot_button_register_cb(encoder_ctx->btn_handle, BUTTON_PRESS_DOWN, lvgl_port_encoder_btn_down_handler, encoder_ctx));
|
||||
ESP_ERROR_CHECK(iot_button_register_cb(encoder_ctx->btn_handle, BUTTON_PRESS_UP, lvgl_port_encoder_btn_up_handler, encoder_ctx));
|
||||
#else
|
||||
ESP_ERROR_CHECK(iot_button_register_cb(encoder_ctx->btn_handle, BUTTON_PRESS_DOWN, NULL, lvgl_port_encoder_btn_down_handler, encoder_ctx));
|
||||
ESP_ERROR_CHECK(iot_button_register_cb(encoder_ctx->btn_handle, BUTTON_PRESS_UP, NULL, lvgl_port_encoder_btn_up_handler, encoder_ctx));
|
||||
#endif
|
||||
|
||||
encoder_ctx->btn_enter = false;
|
||||
encoder_ctx->diff = 0;
|
||||
|
||||
lvgl_port_lock(0);
|
||||
/* Register a encoder input device */
|
||||
indev = lv_indev_create();
|
||||
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_disp(indev, encoder_cfg->disp);
|
||||
lv_indev_set_driver_data(indev, encoder_ctx);
|
||||
encoder_ctx->indev = indev;
|
||||
lvgl_port_unlock();
|
||||
|
||||
return indev;
|
||||
|
||||
err:
|
||||
if (ret != ESP_OK) {
|
||||
if (encoder_ctx->knob_handle != NULL) {
|
||||
iot_knob_delete(encoder_ctx->knob_handle);
|
||||
}
|
||||
|
||||
if (encoder_ctx->btn_handle != NULL) {
|
||||
iot_button_delete(encoder_ctx->btn_handle);
|
||||
}
|
||||
|
||||
if (encoder_ctx != NULL) {
|
||||
free(encoder_ctx);
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_remove_encoder(lv_indev_t *encoder)
|
||||
{
|
||||
assert(encoder);
|
||||
lvgl_port_encoder_ctx_t *encoder_ctx = (lvgl_port_encoder_ctx_t *)lv_indev_get_driver_data(encoder);
|
||||
|
||||
if (encoder_ctx->knob_handle != NULL) {
|
||||
iot_knob_delete(encoder_ctx->knob_handle);
|
||||
}
|
||||
|
||||
if (encoder_ctx->btn_handle != NULL) {
|
||||
iot_button_delete(encoder_ctx->btn_handle);
|
||||
}
|
||||
|
||||
lvgl_port_lock(0);
|
||||
/* Remove input device driver */
|
||||
lv_indev_delete(encoder);
|
||||
lvgl_port_unlock();
|
||||
|
||||
if (encoder_ctx != NULL) {
|
||||
free(encoder_ctx);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Private functions
|
||||
*******************************************************************************/
|
||||
|
||||
static void lvgl_port_encoder_read(lv_indev_t *indev_drv, lv_indev_data_t *data)
|
||||
{
|
||||
assert(indev_drv);
|
||||
lvgl_port_encoder_ctx_t *ctx = (lvgl_port_encoder_ctx_t *)lv_indev_get_driver_data(indev_drv);
|
||||
assert(ctx);
|
||||
|
||||
data->enc_diff = ctx->diff;
|
||||
data->state = (true == ctx->btn_enter) ? LV_INDEV_STATE_PRESSED : LV_INDEV_STATE_RELEASED;
|
||||
ctx->diff = 0;
|
||||
}
|
||||
|
||||
static void lvgl_port_encoder_btn_down_handler(void *button_handle, void *usr_data)
|
||||
{
|
||||
lvgl_port_encoder_ctx_t *ctx = (lvgl_port_encoder_ctx_t *) usr_data;
|
||||
button_handle_t button = (button_handle_t)button_handle;
|
||||
if (ctx && button) {
|
||||
/* ENTER */
|
||||
if (button == ctx->btn_handle) {
|
||||
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 *button_handle, void *usr_data)
|
||||
{
|
||||
lvgl_port_encoder_ctx_t *ctx = (lvgl_port_encoder_ctx_t *) usr_data;
|
||||
button_handle_t button = (button_handle_t)button_handle;
|
||||
if (ctx && button) {
|
||||
/* ENTER */
|
||||
if (button == ctx->btn_handle) {
|
||||
ctx->btn_enter = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Wake LVGL task, if needed */
|
||||
lvgl_port_task_wake(LVGL_PORT_EVENT_TOUCH, ctx->indev);
|
||||
}
|
||||
|
||||
static void lvgl_port_encoder_left_handler(void *arg, void *arg2)
|
||||
{
|
||||
lvgl_port_encoder_ctx_t *ctx = (lvgl_port_encoder_ctx_t *) arg2;
|
||||
knob_handle_t knob = (knob_handle_t)arg;
|
||||
if (ctx && knob) {
|
||||
/* LEFT */
|
||||
if (knob == ctx->knob_handle) {
|
||||
int32_t diff = lvgl_port_calculate_diff(knob, KNOB_LEFT);
|
||||
ctx->diff = (ctx->diff > 0) ? diff : ctx->diff + diff;
|
||||
}
|
||||
/* Wake LVGL task, if needed */
|
||||
lvgl_port_task_wake(LVGL_PORT_EVENT_TOUCH, ctx->indev);
|
||||
}
|
||||
}
|
||||
|
||||
static void lvgl_port_encoder_right_handler(void *arg, void *arg2)
|
||||
{
|
||||
lvgl_port_encoder_ctx_t *ctx = (lvgl_port_encoder_ctx_t *) arg2;
|
||||
knob_handle_t knob = (knob_handle_t)arg;
|
||||
if (ctx && knob) {
|
||||
/* RIGHT */
|
||||
if (knob == ctx->knob_handle) {
|
||||
int32_t diff = lvgl_port_calculate_diff(knob, KNOB_RIGHT);
|
||||
ctx->diff = (ctx->diff < 0) ? diff : ctx->diff + diff;
|
||||
}
|
||||
/* Wake LVGL task, if needed */
|
||||
lvgl_port_task_wake(LVGL_PORT_EVENT_TOUCH, ctx->indev);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static int32_t lvgl_port_calculate_diff(knob_handle_t knob, knob_event_t event)
|
||||
{
|
||||
static int32_t last_v = 0;
|
||||
|
||||
int32_t diff = 0;
|
||||
int32_t invd = iot_knob_get_count_value(knob);
|
||||
|
||||
if (last_v ^ invd) {
|
||||
|
||||
diff = (int32_t)((uint32_t)invd - (uint32_t)last_v);
|
||||
diff += (event == KNOB_RIGHT && invd < last_v) ? CONFIG_KNOB_HIGH_LIMIT :
|
||||
(event == KNOB_LEFT && invd > last_v) ? CONFIG_KNOB_LOW_LIMIT : 0;
|
||||
last_v = invd;
|
||||
}
|
||||
|
||||
return diff;
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.h"
|
||||
#include "esp_lcd_touch.h"
|
||||
#include "esp_lvgl_port.h"
|
||||
|
||||
static const char *TAG = "LVGL";
|
||||
|
||||
/*******************************************************************************
|
||||
* Types definitions
|
||||
*******************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
esp_lcd_touch_handle_t handle; /* LCD touch IO handle */
|
||||
lv_indev_t *indev; /* LVGL input device driver */
|
||||
struct {
|
||||
float x;
|
||||
float y;
|
||||
} scale; /* Touch scale */
|
||||
} lvgl_port_touch_ctx_t;
|
||||
|
||||
/*******************************************************************************
|
||||
* Function definitions
|
||||
*******************************************************************************/
|
||||
|
||||
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
|
||||
*******************************************************************************/
|
||||
|
||||
lv_indev_t *lvgl_port_add_touch(const lvgl_port_touch_cfg_t *touch_cfg)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
lv_indev_t *indev = NULL;
|
||||
assert(touch_cfg != NULL);
|
||||
assert(touch_cfg->disp != NULL);
|
||||
assert(touch_cfg->handle != NULL);
|
||||
|
||||
/* Touch context */
|
||||
lvgl_port_touch_ctx_t *touch_ctx = malloc(sizeof(lvgl_port_touch_ctx_t));
|
||||
if (touch_ctx == NULL) {
|
||||
ESP_LOGE(TAG, "Not enough memory for touch context allocation!");
|
||||
return NULL;
|
||||
}
|
||||
touch_ctx->handle = touch_cfg->handle;
|
||||
touch_ctx->scale.x = (touch_cfg->scale.x ? touch_cfg->scale.x : 1);
|
||||
touch_ctx->scale.y = (touch_cfg->scale.y ? touch_cfg->scale.y : 1);
|
||||
|
||||
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);
|
||||
/* Register a touchpad input device */
|
||||
indev = lv_indev_create();
|
||||
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_disp(indev, touch_cfg->disp);
|
||||
lv_indev_set_driver_data(indev, touch_ctx);
|
||||
touch_ctx->indev = indev;
|
||||
lvgl_port_unlock();
|
||||
|
||||
err:
|
||||
if (ret != ESP_OK) {
|
||||
if (touch_ctx) {
|
||||
free(touch_ctx);
|
||||
}
|
||||
}
|
||||
|
||||
return indev;
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_remove_touch(lv_indev_t *touch)
|
||||
{
|
||||
assert(touch);
|
||||
lvgl_port_touch_ctx_t *touch_ctx = (lvgl_port_touch_ctx_t *)lv_indev_get_driver_data(touch);
|
||||
|
||||
lvgl_port_lock(0);
|
||||
/* Remove input device driver */
|
||||
lv_indev_delete(touch);
|
||||
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) {
|
||||
free(touch_ctx);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Private functions
|
||||
*******************************************************************************/
|
||||
|
||||
static void lvgl_port_touchpad_read(lv_indev_t *indev_drv, lv_indev_data_t *data)
|
||||
{
|
||||
assert(indev_drv);
|
||||
lvgl_port_touch_ctx_t *touch_ctx = (lvgl_port_touch_ctx_t *)lv_indev_get_driver_data(indev_drv);
|
||||
assert(touch_ctx);
|
||||
assert(touch_ctx->handle);
|
||||
|
||||
uint16_t touchpad_x[1] = {0};
|
||||
uint16_t touchpad_y[1] = {0};
|
||||
uint8_t touchpad_cnt = 0;
|
||||
|
||||
/* Read data from touch controller into memory */
|
||||
esp_lcd_touch_read_data(touch_ctx->handle);
|
||||
|
||||
/* Read data from touch controller */
|
||||
bool touchpad_pressed = esp_lcd_touch_get_coordinates(touch_ctx->handle, touchpad_x, touchpad_y, NULL, &touchpad_cnt, 1);
|
||||
|
||||
if (touchpad_pressed && touchpad_cnt > 0) {
|
||||
data->point.x = touch_ctx->scale.x * touchpad_x[0];
|
||||
data->point.y = touch_ctx->scale.y * touchpad_y[0];
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,490 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_check.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_usage_keyboard.h"
|
||||
#include "usb/hid_usage_mouse.h"
|
||||
|
||||
/* LVGL image of cursor */
|
||||
LV_IMG_DECLARE(img_cursor)
|
||||
|
||||
static const char *TAG = "LVGL";
|
||||
|
||||
/*******************************************************************************
|
||||
* Types definitions
|
||||
*******************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
QueueHandle_t queue; /* USB HID queue */
|
||||
TaskHandle_t task; /* USB HID task */
|
||||
bool running; /* USB HID task running */
|
||||
struct {
|
||||
lv_indev_t *indev; /* LVGL mouse input device driver */
|
||||
uint8_t sensitivity; /* Mouse sensitivity (cannot be zero) */
|
||||
int16_t x; /* Mouse X coordinate */
|
||||
int16_t y; /* Mouse Y coordinate */
|
||||
bool left_button; /* Mouse left button state */
|
||||
} mouse;
|
||||
struct {
|
||||
lv_indev_t *indev; /* LVGL keyboard input device driver */
|
||||
uint32_t last_key;
|
||||
bool pressed;
|
||||
} kb;
|
||||
} lvgl_port_usb_hid_ctx_t;
|
||||
|
||||
typedef struct {
|
||||
hid_host_device_handle_t hid_device_handle;
|
||||
hid_host_driver_event_t event;
|
||||
void *arg;
|
||||
} lvgl_port_usb_hid_event_t;
|
||||
|
||||
/*******************************************************************************
|
||||
* Function definitions
|
||||
*******************************************************************************/
|
||||
|
||||
static lvgl_port_usb_hid_ctx_t *lvgl_port_hid_init(void);
|
||||
static void lvgl_port_usb_hid_task(void *arg);
|
||||
static void lvgl_port_usb_hid_read_mouse(lv_indev_t *indev_drv, lv_indev_data_t *data);
|
||||
static void lvgl_port_usb_hid_read_kb(lv_indev_t *indev_drv, lv_indev_data_t *data);
|
||||
static void lvgl_port_usb_hid_callback(hid_host_device_handle_t hid_device_handle, const hid_host_driver_event_t event, void *arg);
|
||||
|
||||
/*******************************************************************************
|
||||
* Local variables
|
||||
*******************************************************************************/
|
||||
static lvgl_port_usb_hid_ctx_t lvgl_hid_ctx;
|
||||
|
||||
/*******************************************************************************
|
||||
* Public API functions
|
||||
*******************************************************************************/
|
||||
|
||||
lv_indev_t *lvgl_port_add_usb_hid_mouse_input(const lvgl_port_hid_mouse_cfg_t *mouse_cfg)
|
||||
{
|
||||
lv_indev_t *indev;
|
||||
assert(mouse_cfg);
|
||||
assert(mouse_cfg->disp);
|
||||
|
||||
/* Initialize USB HID */
|
||||
lvgl_port_usb_hid_ctx_t *hid_ctx = lvgl_port_hid_init();
|
||||
if (hid_ctx == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Mouse sensitivity cannot be zero */
|
||||
hid_ctx->mouse.sensitivity = (mouse_cfg->sensitivity == 0 ? 1 : mouse_cfg->sensitivity);
|
||||
|
||||
int32_t ver_res = lv_display_get_vertical_resolution(mouse_cfg->disp);
|
||||
int32_t hor_res = lv_display_get_physical_horizontal_resolution(mouse_cfg->disp);
|
||||
|
||||
/* Default coordinates to screen center */
|
||||
hid_ctx->mouse.x = (hor_res * hid_ctx->mouse.sensitivity) / 2;
|
||||
hid_ctx->mouse.y = (ver_res * hid_ctx->mouse.sensitivity) / 2;
|
||||
|
||||
lvgl_port_lock(0);
|
||||
/* Register a mouse input device */
|
||||
indev = lv_indev_create();
|
||||
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_disp(indev, mouse_cfg->disp);
|
||||
lv_indev_set_driver_data(indev, hid_ctx);
|
||||
hid_ctx->mouse.indev = indev;
|
||||
lvgl_port_unlock();
|
||||
|
||||
/* Set image of cursor */
|
||||
lv_obj_t *cursor = mouse_cfg->cursor_img;
|
||||
if (cursor == NULL) {
|
||||
cursor = lv_img_create(lv_scr_act());
|
||||
lv_img_set_src(cursor, &img_cursor);
|
||||
}
|
||||
lv_indev_set_cursor(indev, cursor);
|
||||
|
||||
return indev;
|
||||
}
|
||||
|
||||
lv_indev_t *lvgl_port_add_usb_hid_keyboard_input(const lvgl_port_hid_keyboard_cfg_t *keyboard_cfg)
|
||||
{
|
||||
lv_indev_t *indev;
|
||||
assert(keyboard_cfg);
|
||||
assert(keyboard_cfg->disp);
|
||||
|
||||
/* Initialize USB HID */
|
||||
lvgl_port_usb_hid_ctx_t *hid_ctx = lvgl_port_hid_init();
|
||||
if (hid_ctx == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lvgl_port_lock(0);
|
||||
/* Register a mouse input device */
|
||||
indev = lv_indev_create();
|
||||
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_disp(indev, keyboard_cfg->disp);
|
||||
lv_indev_set_driver_data(indev, hid_ctx);
|
||||
hid_ctx->kb.indev = indev;
|
||||
lvgl_port_unlock();
|
||||
|
||||
return indev;
|
||||
}
|
||||
|
||||
esp_err_t lvgl_port_remove_usb_hid_input(lv_indev_t *hid)
|
||||
{
|
||||
assert(hid);
|
||||
lvgl_port_usb_hid_ctx_t *hid_ctx = (lvgl_port_usb_hid_ctx_t *)lv_indev_get_driver_data(hid);
|
||||
|
||||
lvgl_port_lock(0);
|
||||
/* Remove input device driver */
|
||||
lv_indev_delete(hid);
|
||||
lvgl_port_unlock();
|
||||
|
||||
if (lvgl_hid_ctx.mouse.indev == hid) {
|
||||
lvgl_hid_ctx.mouse.indev = NULL;
|
||||
} else if (lvgl_hid_ctx.kb.indev == hid) {
|
||||
lvgl_hid_ctx.kb.indev = NULL;
|
||||
}
|
||||
|
||||
/* If all hid input devices are removed, stop task and clean all */
|
||||
if (lvgl_hid_ctx.mouse.indev == NULL && lvgl_hid_ctx.kb.indev) {
|
||||
hid_ctx->running = false;
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Private functions
|
||||
*******************************************************************************/
|
||||
|
||||
static lvgl_port_usb_hid_ctx_t *lvgl_port_hid_init(void)
|
||||
{
|
||||
esp_err_t ret;
|
||||
|
||||
/* USB HID is already initialized */
|
||||
if (lvgl_hid_ctx.task) {
|
||||
return &lvgl_hid_ctx;
|
||||
}
|
||||
|
||||
/* USB HID host driver config */
|
||||
const hid_host_driver_config_t hid_host_driver_config = {
|
||||
.create_background_task = true,
|
||||
.task_priority = 5,
|
||||
.stack_size = 4096,
|
||||
.core_id = 0,
|
||||
.callback = lvgl_port_usb_hid_callback,
|
||||
.callback_arg = &lvgl_hid_ctx,
|
||||
};
|
||||
|
||||
ret = hid_host_install(&hid_host_driver_config);
|
||||
if (ret != ESP_OK) {
|
||||
ESP_LOGE(TAG, "USB HID install failed!");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
lvgl_hid_ctx.queue = xQueueCreate(10, sizeof(lvgl_port_usb_hid_event_t));
|
||||
xTaskCreate(&lvgl_port_usb_hid_task, "hid_task", 4 * 1024, &lvgl_hid_ctx, 2, &lvgl_hid_ctx.task);
|
||||
|
||||
return &lvgl_hid_ctx;
|
||||
}
|
||||
|
||||
static char usb_hid_get_keyboard_char(uint8_t key, uint8_t shift)
|
||||
{
|
||||
char ret_key = 0;
|
||||
|
||||
const uint8_t keycode2ascii [57][2] = {
|
||||
{0, 0}, /* HID_KEY_NO_PRESS */
|
||||
{0, 0}, /* HID_KEY_ROLLOVER */
|
||||
{0, 0}, /* HID_KEY_POST_FAIL */
|
||||
{0, 0}, /* HID_KEY_ERROR_UNDEFINED */
|
||||
{'a', 'A'}, /* HID_KEY_A */
|
||||
{'b', 'B'}, /* HID_KEY_B */
|
||||
{'c', 'C'}, /* HID_KEY_C */
|
||||
{'d', 'D'}, /* HID_KEY_D */
|
||||
{'e', 'E'}, /* HID_KEY_E */
|
||||
{'f', 'F'}, /* HID_KEY_F */
|
||||
{'g', 'G'}, /* HID_KEY_G */
|
||||
{'h', 'H'}, /* HID_KEY_H */
|
||||
{'i', 'I'}, /* HID_KEY_I */
|
||||
{'j', 'J'}, /* HID_KEY_J */
|
||||
{'k', 'K'}, /* HID_KEY_K */
|
||||
{'l', 'L'}, /* HID_KEY_L */
|
||||
{'m', 'M'}, /* HID_KEY_M */
|
||||
{'n', 'N'}, /* HID_KEY_N */
|
||||
{'o', 'O'}, /* HID_KEY_O */
|
||||
{'p', 'P'}, /* HID_KEY_P */
|
||||
{'q', 'Q'}, /* HID_KEY_Q */
|
||||
{'r', 'R'}, /* HID_KEY_R */
|
||||
{'s', 'S'}, /* HID_KEY_S */
|
||||
{'t', 'T'}, /* HID_KEY_T */
|
||||
{'u', 'U'}, /* HID_KEY_U */
|
||||
{'v', 'V'}, /* HID_KEY_V */
|
||||
{'w', 'W'}, /* HID_KEY_W */
|
||||
{'x', 'X'}, /* HID_KEY_X */
|
||||
{'y', 'Y'}, /* HID_KEY_Y */
|
||||
{'z', 'Z'}, /* HID_KEY_Z */
|
||||
{'1', '!'}, /* HID_KEY_1 */
|
||||
{'2', '@'}, /* HID_KEY_2 */
|
||||
{'3', '#'}, /* HID_KEY_3 */
|
||||
{'4', '$'}, /* HID_KEY_4 */
|
||||
{'5', '%'}, /* HID_KEY_5 */
|
||||
{'6', '^'}, /* HID_KEY_6 */
|
||||
{'7', '&'}, /* HID_KEY_7 */
|
||||
{'8', '*'}, /* HID_KEY_8 */
|
||||
{'9', '('}, /* HID_KEY_9 */
|
||||
{'0', ')'}, /* HID_KEY_0 */
|
||||
{'\r', '\r'}, /* HID_KEY_ENTER */
|
||||
{0, 0}, /* HID_KEY_ESC */
|
||||
{'\b', 0}, /* HID_KEY_DEL */
|
||||
{0, 0}, /* HID_KEY_TAB */
|
||||
{' ', ' '}, /* HID_KEY_SPACE */
|
||||
{'-', '_'}, /* HID_KEY_MINUS */
|
||||
{'=', '+'}, /* HID_KEY_EQUAL */
|
||||
{'[', '{'}, /* HID_KEY_OPEN_BRACKET */
|
||||
{']', '}'}, /* HID_KEY_CLOSE_BRACKET */
|
||||
{'\\', '|'}, /* HID_KEY_BACK_SLASH */
|
||||
{'\\', '|'}, /* HID_KEY_SHARP */ // HOTFIX: for NonUS Keyboards repeat HID_KEY_BACK_SLASH
|
||||
{';', ':'}, /* HID_KEY_COLON */
|
||||
{'\'', '"'}, /* HID_KEY_QUOTE */
|
||||
{'`', '~'}, /* HID_KEY_TILDE */
|
||||
{',', '<'}, /* HID_KEY_LESS */
|
||||
{'.', '>'}, /* HID_KEY_GREATER */
|
||||
{'/', '?'} /* HID_KEY_SLASH */
|
||||
};
|
||||
|
||||
if (shift > 1) {
|
||||
shift = 1;
|
||||
}
|
||||
|
||||
if ((key >= HID_KEY_A) && (key <= HID_KEY_SLASH)) {
|
||||
ret_key = keycode2ascii[key][shift];
|
||||
}
|
||||
|
||||
return ret_key;
|
||||
}
|
||||
|
||||
static void lvgl_port_usb_hid_host_interface_callback(hid_host_device_handle_t hid_device_handle, const hid_host_interface_event_t event, void *arg)
|
||||
{
|
||||
hid_host_dev_params_t dev;
|
||||
hid_host_device_get_params(hid_device_handle, &dev);
|
||||
lvgl_port_usb_hid_ctx_t *hid_ctx = (lvgl_port_usb_hid_ctx_t *)arg;
|
||||
uint8_t data[10];
|
||||
unsigned int data_length = 0;
|
||||
|
||||
assert(hid_ctx != NULL);
|
||||
|
||||
switch (event) {
|
||||
case HID_HOST_INTERFACE_EVENT_INPUT_REPORT:
|
||||
hid_host_device_get_raw_input_report_data(hid_device_handle, data, sizeof(data), &data_length);
|
||||
if (dev.proto == HID_PROTOCOL_KEYBOARD) {
|
||||
hid_keyboard_input_report_boot_t *keyboard = (hid_keyboard_input_report_boot_t *)data;
|
||||
if (data_length < sizeof(hid_keyboard_input_report_boot_t)) {
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < HID_KEYBOARD_KEY_MAX; i++) {
|
||||
if (keyboard->key[i] > HID_KEY_ERROR_UNDEFINED) {
|
||||
char key = 0;
|
||||
|
||||
/* LVGL special keys */
|
||||
if (keyboard->key[i] == HID_KEY_TAB) {
|
||||
if ((keyboard->modifier.left_shift || keyboard->modifier.right_shift)) {
|
||||
key = LV_KEY_PREV;
|
||||
} else {
|
||||
key = LV_KEY_NEXT;
|
||||
}
|
||||
} else if (keyboard->key[i] == HID_KEY_RIGHT) {
|
||||
key = LV_KEY_RIGHT;
|
||||
} else if (keyboard->key[i] == HID_KEY_LEFT) {
|
||||
key = LV_KEY_LEFT;
|
||||
} else if (keyboard->key[i] == HID_KEY_DOWN) {
|
||||
key = LV_KEY_DOWN;
|
||||
} else if (keyboard->key[i] == HID_KEY_UP) {
|
||||
key = LV_KEY_UP;
|
||||
} else if (keyboard->key[i] == HID_KEY_ENTER || keyboard->key[i] == HID_KEY_KEYPAD_ENTER) {
|
||||
key = LV_KEY_ENTER;
|
||||
} else if (keyboard->key[i] == HID_KEY_DELETE) {
|
||||
key = LV_KEY_DEL;
|
||||
} else if (keyboard->key[i] == HID_KEY_HOME) {
|
||||
key = LV_KEY_HOME;
|
||||
} else if (keyboard->key[i] == HID_KEY_END) {
|
||||
key = LV_KEY_END;
|
||||
} else {
|
||||
/* Get ASCII char */
|
||||
key = usb_hid_get_keyboard_char(keyboard->key[i], (keyboard->modifier.left_shift || keyboard->modifier.right_shift));
|
||||
}
|
||||
|
||||
if (key == 0) {
|
||||
ESP_LOGI(TAG, "Not recognized key: %c (%d)", keyboard->key[i], keyboard->key[i]);
|
||||
}
|
||||
hid_ctx->kb.last_key = key;
|
||||
hid_ctx->kb.pressed = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Wake LVGL task, if needed */
|
||||
lvgl_port_task_wake(LVGL_PORT_EVENT_TOUCH, hid_ctx->kb.indev);
|
||||
} else if (dev.proto == HID_PROTOCOL_MOUSE) {
|
||||
hid_mouse_input_report_boot_t *mouse = (hid_mouse_input_report_boot_t *)data;
|
||||
if (data_length < sizeof(hid_mouse_input_report_boot_t)) {
|
||||
break;
|
||||
}
|
||||
hid_ctx->mouse.left_button = mouse->buttons.button1;
|
||||
hid_ctx->mouse.x += mouse->x_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;
|
||||
case HID_HOST_INTERFACE_EVENT_TRANSFER_ERROR:
|
||||
break;
|
||||
case HID_HOST_INTERFACE_EVENT_DISCONNECTED:
|
||||
hid_host_device_close(hid_device_handle);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void lvgl_port_usb_hid_task(void *arg)
|
||||
{
|
||||
hid_host_dev_params_t dev;
|
||||
lvgl_port_usb_hid_ctx_t *ctx = (lvgl_port_usb_hid_ctx_t *)arg;
|
||||
hid_host_device_handle_t hid_device_handle = NULL;
|
||||
lvgl_port_usb_hid_event_t msg;
|
||||
|
||||
assert(ctx);
|
||||
|
||||
ctx->running = true;
|
||||
|
||||
while (ctx->running) {
|
||||
if (xQueueReceive(ctx->queue, &msg, pdMS_TO_TICKS(50))) {
|
||||
hid_device_handle = msg.hid_device_handle;
|
||||
hid_host_device_get_params(hid_device_handle, &dev);
|
||||
|
||||
switch (msg.event) {
|
||||
case HID_HOST_DRIVER_EVENT_CONNECTED:
|
||||
/* Handle mouse or keyboard */
|
||||
if (dev.proto == HID_PROTOCOL_KEYBOARD || dev.proto == HID_PROTOCOL_MOUSE) {
|
||||
const hid_host_device_config_t dev_config = {
|
||||
.callback = lvgl_port_usb_hid_host_interface_callback,
|
||||
.callback_arg = ctx
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK( hid_host_device_open(hid_device_handle, &dev_config) );
|
||||
ESP_ERROR_CHECK( hid_class_request_set_idle(hid_device_handle, 0, 0) );
|
||||
ESP_ERROR_CHECK( hid_class_request_set_protocol(hid_device_handle, HID_REPORT_PROTOCOL_BOOT) );
|
||||
ESP_ERROR_CHECK( hid_host_device_start(hid_device_handle) );
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
xQueueReset(ctx->queue);
|
||||
vQueueDelete(ctx->queue);
|
||||
|
||||
hid_host_uninstall();
|
||||
|
||||
memset(&lvgl_hid_ctx, 0, sizeof(lvgl_port_usb_hid_ctx_t));
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void lvgl_port_usb_hid_read_mouse(lv_indev_t *indev_drv, lv_indev_data_t *data)
|
||||
{
|
||||
int16_t width = 0;
|
||||
int16_t height = 0;
|
||||
assert(indev_drv);
|
||||
lvgl_port_usb_hid_ctx_t *ctx = (lvgl_port_usb_hid_ctx_t *)lv_indev_get_driver_data(indev_drv);
|
||||
assert(ctx);
|
||||
|
||||
lv_display_t *disp = lv_indev_get_display(indev_drv);
|
||||
assert(disp);
|
||||
if (lv_display_get_rotation(disp) == LV_DISPLAY_ROTATION_0 || lv_display_get_rotation(disp) == LV_DISPLAY_ROTATION_180) {
|
||||
width = lv_display_get_physical_horizontal_resolution(disp);
|
||||
height = lv_display_get_vertical_resolution(disp);
|
||||
} else {
|
||||
width = lv_display_get_vertical_resolution(disp);
|
||||
height = lv_display_get_physical_horizontal_resolution(disp);
|
||||
}
|
||||
|
||||
/* Screen borders */
|
||||
if (ctx->mouse.x < 0) {
|
||||
ctx->mouse.x = 0;
|
||||
} else if (ctx->mouse.x > width * ctx->mouse.sensitivity) {
|
||||
ctx->mouse.x = width * ctx->mouse.sensitivity;
|
||||
}
|
||||
if (ctx->mouse.y < 0) {
|
||||
ctx->mouse.y = 0;
|
||||
} else if (ctx->mouse.y > height * ctx->mouse.sensitivity) {
|
||||
ctx->mouse.y = height * ctx->mouse.sensitivity;
|
||||
}
|
||||
|
||||
/* Get coordinates by rotation with sensitivity */
|
||||
switch (lv_display_get_rotation(disp)) {
|
||||
case LV_DISPLAY_ROTATION_0:
|
||||
data->point.x = ctx->mouse.x / ctx->mouse.sensitivity;
|
||||
data->point.y = ctx->mouse.y / ctx->mouse.sensitivity;
|
||||
break;
|
||||
case LV_DISPLAY_ROTATION_90:
|
||||
data->point.y = width - ctx->mouse.x / ctx->mouse.sensitivity;
|
||||
data->point.x = ctx->mouse.y / ctx->mouse.sensitivity;
|
||||
break;
|
||||
case LV_DISPLAY_ROTATION_180:
|
||||
data->point.x = width - ctx->mouse.x / ctx->mouse.sensitivity;
|
||||
data->point.y = height - ctx->mouse.y / ctx->mouse.sensitivity;
|
||||
break;
|
||||
case LV_DISPLAY_ROTATION_270:
|
||||
data->point.y = ctx->mouse.x / ctx->mouse.sensitivity;
|
||||
data->point.x = height - ctx->mouse.y / ctx->mouse.sensitivity;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ctx->mouse.left_button) {
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
} else {
|
||||
data->state = LV_INDEV_STATE_RELEASED;
|
||||
}
|
||||
}
|
||||
|
||||
static void lvgl_port_usb_hid_read_kb(lv_indev_t *indev_drv, lv_indev_data_t *data)
|
||||
{
|
||||
assert(indev_drv);
|
||||
lvgl_port_usb_hid_ctx_t *ctx = (lvgl_port_usb_hid_ctx_t *)lv_indev_get_driver_data(indev_drv);
|
||||
assert(ctx);
|
||||
|
||||
data->key = ctx->kb.last_key;
|
||||
if (ctx->kb.pressed) {
|
||||
data->state = LV_INDEV_STATE_PRESSED;
|
||||
ctx->kb.pressed = false;
|
||||
} else {
|
||||
data->state = LV_INDEV_STATE_RELEASED;
|
||||
ctx->kb.last_key = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void lvgl_port_usb_hid_callback(hid_host_device_handle_t hid_device_handle, const hid_host_driver_event_t event, void *arg)
|
||||
{
|
||||
lvgl_port_usb_hid_ctx_t *hid_ctx = (lvgl_port_usb_hid_ctx_t *)arg;
|
||||
|
||||
const lvgl_port_usb_hid_event_t msg = {
|
||||
.hid_device_handle = hid_device_handle,
|
||||
.event = event,
|
||||
.arg = arg
|
||||
};
|
||||
|
||||
xQueueSend(hid_ctx->queue, &msg, 0);
|
||||
}
|
||||
@@ -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,325 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
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,404 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
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,105 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// This is LVGL RGB888 simple fill for ESP32 processor
|
||||
|
||||
.section .text
|
||||
.align 4
|
||||
.global lv_color_blend_to_rgb888_esp
|
||||
.type lv_color_blend_to_rgb888_esp,@function
|
||||
// The function implements the following C code:
|
||||
// void lv_color_blend_to_rgb888(_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_rgb888_esp:
|
||||
|
||||
entry a1, 32
|
||||
|
||||
l32i.n a3, a2, 4 // a3 - dest_buff
|
||||
l32i.n a4, a2, 8 // a4 - dest_w in uint24_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
|
||||
|
||||
// a11 - dest_w_bytes = sizeof(uint24_t) * dest_w = 3 * a4
|
||||
slli a11, a4, 1 // a11 - dest_w_bytes = sizeof(uint16_t) * dest_w
|
||||
add a11, a11, a4 // a11 - dest_w_bytes = a11 + a4
|
||||
|
||||
// Prepare register combinations
|
||||
// a13 - 0xBBRRGGBB a14 - 0xGGBBRRGG a15 - 0xRRGGBBRR
|
||||
l8ui a13, a7, 0 // blue 000B
|
||||
slli a13, a13, 24 // shift to B000
|
||||
or a13, a13, a8 // a13 BRGB
|
||||
|
||||
srli a14, a8, 8 // a14 00RG
|
||||
slli a10, a8, 16 // a10 GB00
|
||||
or a14, a14, a10 // a14 GBRG
|
||||
|
||||
slli a15, a8, 8 // a15 RGB0
|
||||
l8ui a10, a7, 2 // a7 000R
|
||||
or a15, a15, a10 // a15 RGBR
|
||||
|
||||
sub a6, a6, a11 // dest_stride = dest_stride - dest_w_bytes
|
||||
|
||||
// Prepare main loop length and dest_w_bytes
|
||||
srli a9, a4, 2 // a9 = loop_len = dest_w / 4, calculate main loop_len for original dest_w
|
||||
movi.n a8, 0x3 // a8 = 0x3, remainder mask
|
||||
and a10, a4, a8 // a10 - remainder after division by 4 = a4 and 0x3
|
||||
|
||||
.outer_loop:
|
||||
|
||||
// Run main loop which sets 12 bytes (4 rgb888) in one loop run
|
||||
loopnez a9, ._main_loop
|
||||
s32i.n a13, a3, 0 // save 32 bits from 32-bit color a13 to dest_buff a3, offset 0
|
||||
s32i.n a14, a3, 4 // save 32 bits from 32-bit color a14 to dest_buff a3, offset 4
|
||||
s32i.n a15, a3, 8 // save 32 bits from 32-bit color a15 to dest_buff a3, offset 8
|
||||
addi.n a3, a3, 12 // increment dest_buff pointer by 12
|
||||
._main_loop:
|
||||
|
||||
bnei a10, 0x3, _less_than_3 // branch if less than 3 values left
|
||||
s32i.n a13, a3, 0 // save 32 bits from a13 to dest_buff a3, offset 0 bytes
|
||||
s32i.n a14, a3, 4 // save 32 bits from a14 to dest_buff a3, offset 4 bytes
|
||||
s8i a15, a3, 8 // save 8 bits from a15 to dest_buff a3, offset 8 bytes
|
||||
addi.n a3, a3, 9 // increment dest_buff pointer by 9 bytes
|
||||
j _less_than_1
|
||||
_less_than_3:
|
||||
|
||||
bnei a10, 0x2, _less_than_2 // branch if less than 2 values left
|
||||
s32i.n a13, a3, 0 // save 32 bits from a13 to dest_buff a3, offset 0 bytes
|
||||
s16i a14, a3, 4 // save 16 bits from a14 to dest_buff a3, offset 4 bytes
|
||||
addi.n a3, a3, 6 // increment dest_buff pointer by 6 bytes
|
||||
j _less_than_1
|
||||
_less_than_2:
|
||||
|
||||
bnei a10, 0x1, _less_than_1 // branch if less than 1 value left
|
||||
s16i a13, a3, 0 // save 16 bits from a13 to dest_buff a3, offset 0 bytes
|
||||
s8i a15, a3, 2 // save 8 bits from a15 to dest_buff a3, offset 2 bytes
|
||||
addi.n a3, a3, 3 // increment dest_buff pointer by 3 bytes
|
||||
_less_than_1:
|
||||
|
||||
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 (check 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,346 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// This is LVGL RGB888 simple fill for ESP32S3 processor
|
||||
|
||||
.section .text
|
||||
.align 4
|
||||
.global lv_color_blend_to_rgb888_esp
|
||||
.type lv_color_blend_to_rgb888_esp,@function
|
||||
// The function implements the following C code:
|
||||
// void lv_color_blend_to_rgb888(_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_rgb888_esp:
|
||||
|
||||
entry a1, 32
|
||||
|
||||
l32i.n a3, a2, 4 // a3 - dest_buff
|
||||
l32i.n a4, a2, 8 // a4 - dest_w in uint24_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
|
||||
|
||||
// a11 - dest_w_bytes = sizeof(uint24_t) * dest_w = 3 * a4
|
||||
slli a11, a4, 1 // a11 - dest_w_bytes = 2 * dest_w
|
||||
add a11, a11, a4 // a11 - dest_w_bytes = a11 + a4
|
||||
|
||||
// Prepare register combinations
|
||||
// a13 - 0xBBRRGGBB a14 - 0xGGBBRRGG a15 - 0xRRGGBBRR
|
||||
l8ui a13, a7, 0 // blue 000B
|
||||
slli a13, a13, 24 // shift to B000
|
||||
or a13, a13, a8 // a13 BRGB
|
||||
|
||||
srli a14, a8, 8 // a14 00RG
|
||||
slli a10, a8, 16 // a10 GB00
|
||||
or a14, a14, a10 // a14 GBRG
|
||||
|
||||
slli a15, a8, 8 // a15 RGB0
|
||||
l8ui a10, a7, 2 // a7 000R
|
||||
or a15, a15, a10 // a15 RGBR
|
||||
|
||||
sub a6, a6, a11 // dest_stride = dest_stride - dest_w_bytes
|
||||
|
||||
// Check for short lengths
|
||||
// dest_w should be at least 12, othewise it's not worth using esp32s3 TIE
|
||||
bgei a4, 12, _esp32s3_implementation // Branch if dest_w is greater than or equal to 12
|
||||
j .lv_color_blend_to_rgb888_esp32_body // Jump to esp32 implementation
|
||||
|
||||
_esp32s3_implementation:
|
||||
|
||||
// Prepare q registers for the main loop
|
||||
ee.movi.32.q q3, a13, 0 // fill q3 register from a13 by 32 bits
|
||||
ee.movi.32.q q3, a14, 1 // fill q3 register from a14 by 32 bits
|
||||
ee.movi.32.q q3, a15, 2 // fill q3 register from a15 by 32 bits
|
||||
ee.movi.32.q q3, a13, 3 // fill q3 register from a13 by 32 bits
|
||||
|
||||
ee.movi.32.q q4, a14, 0 // fill q4 register from a14 by 32 bits
|
||||
ee.movi.32.q q4, a15, 1 // fill q4 register from a15 by 32 bits
|
||||
ee.movi.32.q q4, a13, 2 // fill q4 register from a13 by 32 bits
|
||||
ee.movi.32.q q4, a14, 3 // fill q4 register from a14 by 32 bits
|
||||
|
||||
ee.movi.32.q q5, a15, 0 // fill q5 register from a15 by 32 bits
|
||||
ee.movi.32.q q5, a13, 1 // fill q5 register from a13 by 32 bits
|
||||
ee.movi.32.q q5, a14, 2 // fill q5 register from a14 by 32 bits
|
||||
ee.movi.32.q q5, a15, 3 // fill q5 register from a15 by 32 bits
|
||||
|
||||
.outer_loop_aligned:
|
||||
|
||||
// q registers will get shifted and clobbered, need to reinitialize them before using them again
|
||||
// Clear q registers
|
||||
ee.zero.q q0 // clear q0
|
||||
ee.zero.q q1 // clear q1
|
||||
ee.zero.q q2 // clear q2
|
||||
|
||||
// Reinitialize q registers
|
||||
ee.orq q0, q0, q3 // copy q3 to q0
|
||||
ee.orq q1, q1, q4 // copy q4 to q1
|
||||
ee.orq q2, q2, q5 // copy q5 to q2
|
||||
|
||||
// alignment check
|
||||
extui a8, a3, 0, 4 // address_alignment (a8) = dest_buff address (a3) AND 0xf
|
||||
|
||||
movi.n a12, 16 // a12 = 16
|
||||
mov.n a2, a8 // unalignment (a2) = a8
|
||||
// following instruction is here to avoid branching
|
||||
// need to adjust a8 == 0 to 16 to make the unalignment computation work
|
||||
moveqz a2, a12, a8 // modified unalignment (a2) = 16 if unalignment (a8) == 0
|
||||
|
||||
sub a2, a12, a2 // a2 = 16 - unalignment (lower 4 bits of dest_buff address)
|
||||
sub a10, a11, a2 // local_dest_w_bytes = len - (16 - unalignment)
|
||||
|
||||
movi.n a12, 48 // a12 = 48 (main loop copies 48 bytes)
|
||||
quou a9, a10, a12 // main_loop counter (a9) = local_dest_w_bytes (a10) DIV 48 (a12)
|
||||
remu a10, a10, a12 // a10 = local_dest_w_bytes (a10) MOD 48 (a12)
|
||||
|
||||
beqz a8, _dest_buff_aligned // If already aligned, skip aligning
|
||||
|
||||
movi.n a7, unalignment_table // Load unalignment_table address
|
||||
|
||||
addx4 a7, a8, a7 // jump_table handle (a7) = offset (a8) * 4 + jump_table address (a7)
|
||||
l32i a7, a7, 0 // Load target address from jump table
|
||||
jx a7 // Jump to the corresponding handler
|
||||
|
||||
|
||||
// a13 - 0xBBRRGGBB a14 - 0xGGBBRRGG a15 - 0xRRGGBBRR
|
||||
handle_0:
|
||||
handle_1:
|
||||
s8i a13, a3, 0 // save 8 bits from a13 to dest_buff a3, offset 0 bytes
|
||||
addi.n a3, a3, 1 // increment dest_buff pointer by 1 byte
|
||||
s16i a14, a3, 0 // save 16 bits from a14 to dest_buff a3, offset 0 bytes
|
||||
addi.n a3, a3, 2 // increment dest_buff pointer by 2 bytes
|
||||
s32i a13, a3, 0 // save 32 bits from a13 to dest_buff a3, offset 0 bytes
|
||||
addi.n a3, a3, 4 // increment dest_buff pointer by 4 bytes
|
||||
ee.vst.l.64.ip q1, a3, 8 // save lower 64 bits from q0 to dest_buff a3, increase dest_buff pointer by 8 bytes
|
||||
j _shift_q_regs
|
||||
handle_2:
|
||||
s16i a13, a3, 0 // save 16 bits from a13 to dest_buff a3, offset 0 bytes
|
||||
addi.n a3, a3, 2 // increment dest_buff pointer by 2 bytes
|
||||
s32i a15, a3, 0 // save 32 bits from a15 to dest_buff a3, offset 0 bytes
|
||||
addi.n a3, a3, 4 // increment dest_buff pointer by 4 bytes
|
||||
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
|
||||
j _shift_q_regs
|
||||
handle_3:
|
||||
s8i a13, a3, 0 // save 8 bits from a13 to dest_buff a3, offset 0 bytes
|
||||
addi.n a3, a3, 1 // increment dest_buff pointer by 1 byte
|
||||
s32i a14, a3, 0 // save 32 bits from a14 to dest_buff a3, offset 0 bytes
|
||||
addi.n a3, a3, 4 // 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
|
||||
j _shift_q_regs
|
||||
handle_4:
|
||||
s32i a13, a3, 0 // save 32 bits from a13 to dest_buff a3, offset 0 bytes
|
||||
addi.n a3, a3, 4 // increment dest_buff pointer by 4 bytes
|
||||
ee.vst.l.64.ip q1, a3, 8 // save lower 64 bits from q0 to dest_buff a3, increase dest_buff pointer by 8 bytes
|
||||
j _shift_q_regs
|
||||
handle_5:
|
||||
s8i a13, a3, 0 // save 8 bits from a13 to dest_buff a3, offset 0 bytes
|
||||
addi.n a3, a3, 1 // increment dest_buff pointer by 1 byte
|
||||
s16i a14, a3, 0 // save 16 bits from a14 to dest_buff a3, offset 0 bytes
|
||||
addi.n a3, a3, 2 // increment dest_buff pointer by 2 bytes
|
||||
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
|
||||
j _shift_q_regs
|
||||
handle_6:
|
||||
s16i a13, a3, 0 // save 16 bits from a13 to dest_buff a3, offset 0 byte
|
||||
addi.n a3, a3, 2 // increment dest_buff pointer by 2 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
|
||||
j _shift_q_regs
|
||||
handle_7:
|
||||
s8i a13, a3, 0 // save 8 bits from a13 to dest_buff a3, offset 0 bytes
|
||||
addi.n a3, a3, 1 // increment dest_buff pointer by 1 byte
|
||||
ee.vst.l.64.ip q1, a3, 8 // save lower 64 bits from q0 to dest_buff a3, increase dest_buff pointer by 8 bytes
|
||||
j _shift_q_regs
|
||||
handle_8:
|
||||
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
|
||||
j _shift_q_regs
|
||||
|
||||
handle_9:
|
||||
s8i a13, a3, 0 // save 8 bits from a13 to dest_buff a3, offset 0 bytes
|
||||
addi.n a3, a3, 1 // increment dest_buff pointer by 1 byte
|
||||
s16i a14, a3, 0 // save 16 bits from a14 to dest_buff a3, offset 0 bytes
|
||||
addi.n a3, a3, 2 // increment dest_buff pointer by 2 bytes
|
||||
s32i a13, a3, 0 // save 32 bits from a13 to dest_buff a3, offset 0 bytes
|
||||
addi.n a3, a3, 4 // increment dest_buff pointer by 4 bytes
|
||||
j _shift_q_regs
|
||||
handle_10:
|
||||
s16i a13, a3, 0 // save 16 bits from a13 to dest_buff a3, offset 0 bytes
|
||||
addi.n a3, a3, 2 // increment dest_buff pointer by 2 bytes
|
||||
s32i a15, a3, 0 // save 32 bits from a15 to dest_buff a3, offset 0 bytes
|
||||
addi.n a3, a3, 4 // increment dest_buff pointer by 4 bytes
|
||||
j _shift_q_regs
|
||||
handle_11:
|
||||
s8i a13, a3, 0 // save 8 bits from a13 to dest_buff a3, offset 0 bytes
|
||||
addi.n a3, a3, 1 // increment dest_buff pointer by 1 byte
|
||||
s32i a14, a3, 0 // save 32 bits from a14 to dest_buff a3, offset 0 bytes
|
||||
addi.n a3, a3, 4 // increment dest_buff pointer by 4 bytes
|
||||
j _shift_q_regs
|
||||
handle_12:
|
||||
s32i a13, a3, 0 // save 32 bits from a13 to dest_buff a3, offset 0 bytes
|
||||
addi.n a3, a3, 4 // increment dest_buff pointer by 4 bytes
|
||||
j _shift_q_regs
|
||||
handle_13:
|
||||
s8i a13, a3, 0 // save 8 bits from a13 to dest_buff a3, offset 0 bytes
|
||||
addi.n a3, a3, 1 // increment dest_buff pointer by 1 byte
|
||||
s16i a14, a3, 0 // save 16 bits from a14 to dest_buff a3, offset 0 bytes
|
||||
addi.n a3, a3, 2 // increment dest_buff pointer by 2 bytes
|
||||
j _shift_q_regs
|
||||
handle_14:
|
||||
s16i a13, a3, 0 // save 16 bits from a13 to dest_buff a3, offset 0 bytes
|
||||
addi.n a3, a3, 2 // increment dest_buff pointer by 2 bytes
|
||||
j _shift_q_regs
|
||||
handle_15:
|
||||
s8i a13, a3, 0 // save 8 bits from a13 to dest_buff a3, offset 0 bytes
|
||||
addi.n a3, a3, 1 // increment dest_buff pointer by 1 byte
|
||||
j _shift_q_regs
|
||||
|
||||
.align 4
|
||||
|
||||
unalignment_table:
|
||||
.word handle_0 // Case 0: Dummy case for easier address computation
|
||||
.word handle_1 // Case 1: Align 15 bytes
|
||||
.word handle_2 // Case 2: Align 14 bytes
|
||||
.word handle_3 // Case 3: Align 13 bytes
|
||||
.word handle_4 // Case 4: Align 12 bytes
|
||||
.word handle_5 // Case 5: Align 11 bytes
|
||||
.word handle_6 // Case 6: Align 10 bytes
|
||||
.word handle_7 // Case 7: Align 9 bytes
|
||||
.word handle_8 // Case 8: Align 8 bytes
|
||||
.word handle_9 // Case 9: Align 7 bytes
|
||||
.word handle_10 // Case 10: Align 6 bytes
|
||||
.word handle_11 // Case 11: Align 5 bytes
|
||||
.word handle_12 // Case 12: Align 4 bytes
|
||||
.word handle_13 // Case 13: Align 3 bytes
|
||||
.word handle_14 // Case 14: Align 2 bytes
|
||||
.word handle_15 // Case 15: Align 1 byte
|
||||
|
||||
|
||||
_shift_q_regs:
|
||||
wur.sar_byte a2 // apply unalignment to the SAR_BYTE
|
||||
ee.src.q q0, q0, q1 // shift concat. of q0 and q1 to q0 by SAR_BYTE amount
|
||||
ee.src.q q1, q1, q2 // shift concat. of q1 and q2 to q1 by SAR_BYTE amount
|
||||
ee.src.q q2, q2, q3 // shift concat. of q2 and q3 to q2 by SAR_BYTE amount
|
||||
|
||||
_dest_buff_aligned:
|
||||
loopnez a9, ._main_loop_aligned // 48 bytes (16 rgb888) in one loop
|
||||
ee.vst.128.ip q0, a3, 16 // store 16 bytes from q0 to dest_buff a3
|
||||
ee.vst.128.ip q1, a3, 16 // store 16 bytes from q1 to dest_buff a3
|
||||
ee.vst.128.ip q2, a3, 16 // store 16 bytes from q2 to dest_buff a3
|
||||
._main_loop_aligned:
|
||||
|
||||
// Check modulo 32 of the unalignment, if - then set 32 bytes
|
||||
bbci a10, 5, .lt_32 // branch if 5-th bit of local_dest_w_bytes a10 is clear
|
||||
ee.vst.128.ip q0, a3, 16 // store 16 bytes from q0 to dest_buff a3
|
||||
ee.vst.128.ip q1, a3, 16 // store 16 bytes from q1 to dest_buff a3
|
||||
|
||||
ee.srci.2q q0, q1, 1 // shift q0 register to have next bytes to store ready from LSB
|
||||
.lt_32:
|
||||
|
||||
// Check modulo 16 of the unalignment, if - then set 16 bytes
|
||||
bbci a10, 4, .lt_16 // branch if 4-th bit of local_dest_w_bytes a10 is clear
|
||||
ee.vst.128.ip q0, a3, 16 // store 16 bytes from q0 to dest_buff a3
|
||||
|
||||
ee.srci.2q q0, q1, 0 // shift q0 register to have next bytes to store ready from LSB
|
||||
.lt_16:
|
||||
|
||||
// Check modulo 8 of the unalignment, if - then set 8 bytes
|
||||
bbci a10, 3, .lt_8
|
||||
ee.vst.l.64.ip q0, a3, 8 // store 8 bytes from q0 to dest_buff a3
|
||||
|
||||
ee.srci.2q q0, q1, 1 // shift q0 register to have next bytes to store ready from LSB
|
||||
.lt_8:
|
||||
|
||||
// Check modulo 4 of the unalignment, if - then set 4 bytes
|
||||
bbci a10, 2, .lt_4
|
||||
ee.movi.32.a q0, a2, 0 // move lowest 32 bits of q0 to a2
|
||||
s32i.n a2, a3, 0 // save 32 bits from a2 to dest_buff a3, offset 0
|
||||
addi.n a3, a3, 4 // increment dest_buff pointer by 4 bytes
|
||||
|
||||
ee.srci.2q q0, q1, 0 // shift q0 register to have next bytes to store ready from LSB
|
||||
.lt_4:
|
||||
|
||||
// Check modulo 2 of the unalignment, if - then set 2 bytes
|
||||
bbci a10, 1, .lt_2
|
||||
ee.movi.32.a q0, a2, 0 // move lowest 32 bits of q0 to a2
|
||||
s16i a2, a3, 0 // save 16 bits from a2 to dest_buff a3, offset 0
|
||||
addi.n a3, a3, 2 // increment dest_buff pointer by 2 bytes
|
||||
|
||||
ee.srci.2q q0, q1, 1 // shift q0 register to have next bytes to store ready from LSB
|
||||
.lt_2:
|
||||
|
||||
// Check modulo 1 of the unalignment, if - then set 1 byte
|
||||
bbci a10, 0, .lt_1
|
||||
ee.movi.32.a q0, a2, 0 // move lowest 32 bits of q0 to a2
|
||||
s8i a2, a3, 0 // save 8 bits from a2 to dest_buff a3, offset 0
|
||||
addi.n a3, a3, 1 // increment dest_buff pointer by 1 byte
|
||||
.lt_1:
|
||||
|
||||
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
|
||||
|
||||
.lv_color_blend_to_rgb888_esp32_body:
|
||||
|
||||
// Prepare main loop length and dest_w_bytes
|
||||
srli a9, a4, 2 // a9 = loop_len = dest_w / 4, calculate main loop_len for original dest_w
|
||||
movi.n a8, 0x3 // a8 = 0x3, remainder mask
|
||||
and a10, a4, a8 // a10 - remainder after division by 4 = a4 & 0x3
|
||||
|
||||
.outer_loop:
|
||||
|
||||
// Run main loop which sets 12 bytes (4 rgb888) in one loop run
|
||||
loopnez a9, ._main_loop
|
||||
s32i.n a13, a3, 0 // save 32 bits from 32-bit color a13 to dest_buff a3, offset 0
|
||||
s32i.n a14, a3, 4 // save 32 bits from 32-bit color a14 to dest_buff a3, offset 4
|
||||
s32i.n a15, a3, 8 // save 32 bits from 32-bit color a15 to dest_buff a3, offset 8
|
||||
addi.n a3, a3, 12 // increment dest_buff pointer by 12
|
||||
._main_loop:
|
||||
|
||||
bnei a10, 0x3, _less_than_3 // branch if less than 3 values left
|
||||
s32i.n a13, a3, 0 // save 32 bits from a13 to dest_buff a3, offset 0 bytes
|
||||
s32i.n a14, a3, 4 // save 32 bits from a14 to dest_buff a3, offset 4 bytes
|
||||
s8i a15, a3, 8 // save 8 bits from a15 to dest_buff a3, offset 8 bytes
|
||||
addi.n a3, a3, 9 // increment dest_buff pointer by 9 bytes
|
||||
j _less_than_1
|
||||
_less_than_3:
|
||||
|
||||
bnei a10, 0x2, _less_than_2 // branch if less than 2 values left
|
||||
s32i.n a13, a3, 0 // save 32 bits from a13 to dest_buff a3, offset 0 bytes
|
||||
s16i a14, a3, 4 // save 16 bits from a14 to dest_buff a3, offset 4 bytes
|
||||
addi.n a3, a3, 6 // increment dest_buff pointer by 6 bytes
|
||||
j _less_than_1
|
||||
_less_than_2:
|
||||
|
||||
bnei a10, 0x1, _less_than_1 // branch if less than 1 value left
|
||||
s16i a13, a3, 0 // save 16 bits from a13 to dest_buff a3, offset 0 bytes
|
||||
s8i a15, a3, 2 // save 8 bits from a15 to dest_buff a3, offset 2 bytes
|
||||
addi.n a3, a3, 3 // increment dest_buff pointer by 3 bytes
|
||||
_less_than_1:
|
||||
|
||||
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,60 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// Memcpy macros for modulo checking
|
||||
// After running the main loop, there is need to check remaining bytes to be copied out of the main loop
|
||||
// Macros work with both, aligned and unaligned (4-byte boundary) memories
|
||||
// but performance is significantly lower when using unaligned memory, because of the unaligned memory access exception
|
||||
|
||||
|
||||
// Macro for checking modulo 8
|
||||
.macro macro_memcpy_mod_8 src_buf, dest_buf, condition, x1, x2, JUMP_TAG
|
||||
// Check modulo 8 of the \condition, if - then copy 8 bytes
|
||||
bbci \condition, 3, ._mod_8_check_\JUMP_TAG // Branch if 3-rd bit of \condition is clear
|
||||
l32i.n \x1, \src_buf, 0 // Load 32 bits from \src_buff to \x1, offset 0
|
||||
l32i.n \x2, \src_buf, 4 // Load 32 bits from \src_buff to \x2, offset 4
|
||||
s32i.n \x1, \dest_buf, 0 // Save 32 bits from \x1 to \dest_buff, offset 0
|
||||
s32i.n \x2, \dest_buf, 4 // Save 32 bits from \x2 to \dest_buff, offset 4
|
||||
addi.n \src_buf, \src_buf, 8 // Increment \src_buff pointer by 8
|
||||
addi.n \dest_buf, \dest_buf, 8 // Increment \dest_buff pointer 8
|
||||
._mod_8_check_\JUMP_TAG:
|
||||
.endm // macro_memcpy_mod_8
|
||||
|
||||
|
||||
// Macro for checking modulo 4
|
||||
.macro macro_memcpy_mod_4 src_buf, dest_buf, condition, x1, JUMP_TAG
|
||||
// Check modulo 4 of the \condition, if - then copy 4 bytes
|
||||
bbci \condition, 2, ._mod_4_check_\JUMP_TAG // Branch if 2-nd bit of \condition is clear
|
||||
l32i.n \x1, \src_buf, 0 // Load 32 bits from \src_buff to \x1, offset 0
|
||||
addi.n \src_buf, \src_buf, 4 // Increment \src_buff pointer by 4
|
||||
s32i.n \x1, \dest_buf, 0 // Save 32 bits from \x1 to \dest_buff, offset 0
|
||||
addi.n \dest_buf, \dest_buf, 4 // Increment \dest_buff pointer 4
|
||||
._mod_4_check_\JUMP_TAG:
|
||||
.endm // macro_memcpy_mod_4
|
||||
|
||||
|
||||
// Macro for checking modulo 2
|
||||
.macro macro_memcpy_mod_2 src_buf, dest_buf, condition, x1, JUMP_TAG
|
||||
// Check modulo 2 of the \condition, if - then copy 2 bytes
|
||||
bbci \condition, 1, ._mod_2_check_\JUMP_TAG // Branch if 1-st bit of \condition is clear
|
||||
l16ui \x1, \src_buf, 0 // Load 16 bits from \src_buff to \x1, offset 0
|
||||
addi.n \src_buf, \src_buf, 2 // Increment \src_buff pointer by 2
|
||||
s16i \x1, \dest_buf, 0 // Save 16 bits from \x1 to \dest_buff, offset 0
|
||||
addi.n \dest_buf, \dest_buf, 2 // Increment \dest_buff pointer 2
|
||||
._mod_2_check_\JUMP_TAG:
|
||||
.endm // macro_memcpy_mod_2
|
||||
|
||||
|
||||
// Macro for checking modulo 1
|
||||
.macro macro_memcpy_mod_1 src_buf, dest_buf, condition, x1, JUMP_TAG
|
||||
// Check modulo 1 of the \condition, if - then copy 1 byte
|
||||
bbci \condition, 0, ._mod_1_check_\JUMP_TAG // Branch if 0-th bit of \condition is clear
|
||||
l8ui \x1, \src_buf, 0 // Load 8 bits from \src_buff to \x1, offset 0
|
||||
addi.n \src_buf, \src_buf, 1 // Increment \src_buff pointer by 1
|
||||
s8i \x1, \dest_buf, 0 // Save 8 bits from \x1 to \dest_buff, offset 0
|
||||
addi.n \dest_buf, \dest_buf, 1 // Increment \dest_buff pointer 1
|
||||
._mod_1_check_\JUMP_TAG:
|
||||
.endm // macro_memcpy_mod_1
|
||||
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "lv_macro_memcpy.S" // Memcpy macros
|
||||
|
||||
// This is LVGL RGB565 image blend to RGB565 for ESP32 processor
|
||||
|
||||
.section .text
|
||||
.align 4
|
||||
.global lv_rgb565_blend_normal_to_rgb565_esp
|
||||
.type lv_rgb565_blend_normal_to_rgb565_esp,@function
|
||||
// The function implements the following C code:
|
||||
// void rgb565_image_blend(_lv_draw_sw_blend_image_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_rgb565_blend_normal_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
|
||||
l32i.n a8, a2, 24 // a8 - src_stride in bytes
|
||||
slli a11, a4, 1 // a11 - dest_w_bytes = sizeof(uint16_t) * dest_w
|
||||
|
||||
// No need to convert any colors here, we are copying from rgb565 to rgb565
|
||||
|
||||
// Check dest_w length
|
||||
bltui a4, 8, _matrix_width_check // Branch if dest_w (a4) is lower than 8
|
||||
|
||||
// Check memory alignment and input parameters lengths and decide which implementation to use
|
||||
movi.n a10, 0x3 // a10 = 0x3 alignment mask (4-byte alignment)
|
||||
or a15, a7, a3 // a15 = src_buff (a7) OR dest_buff (a3)
|
||||
or a15, a15, a6 // a15 = a15 OR dest_stride (a6)
|
||||
or a15, a15, a8 // a15 = a15 OR src_stride (a8)
|
||||
or a15, a15, a11 // a15 = a15 OR dest_w_bytes (a11)
|
||||
and a15, a15, a10 // a15 = a15 AND alignment mask (a10)
|
||||
bnez a15, _alignment_check // Branch if a15 not equals to zero
|
||||
|
||||
//**********************************************************************************************************************
|
||||
|
||||
// The most ideal case - both arrays aligned, both strides and dest_w are multiples of 4
|
||||
|
||||
// dest_buff (a3) - 4-byte aligned
|
||||
// src_buff (a7) - 4-byte aligned
|
||||
// dest_stride (a6) - 4-byte multiple
|
||||
// src_stride (a8) - 4-byte multiple
|
||||
// dest_w (a4) - 4-byte multiple
|
||||
|
||||
srli a9, a4, 3 // a9 - loop_len = dest_w / 8
|
||||
// Convert strides to matrix paddings
|
||||
sub a6, a6, a11 // dest_matrix_padding (a6) = dest_stride (a6) - dest_w_bytes (a11)
|
||||
sub a8, a8, a11 // src_matrix_padding (a8) = src_stride (a8) - dest_w_bytes (a11)
|
||||
|
||||
.outer_loop_align:
|
||||
|
||||
// Run main loop which copies 16 bytes (8 RGB565 pixels) in one loop run
|
||||
loopnez a9, ._main_loop_aligned
|
||||
l32i.n a15, a7, 0 // Load 32 bits from src_buff a7 to a15, offset 0
|
||||
l32i.n a14, a7, 4 // Load 32 bits from src_buff a7 to a14, offset 4
|
||||
l32i.n a13, a7, 8 // Load 32 bits from src_buff a7 to a13, offset 8
|
||||
l32i.n a12, a7, 12 // Load 32 bits from src_buff a7 to a12, offset 12
|
||||
s32i.n a15, a3, 0 // Save 32 bits from a15 to dest_buff a3, offset 0
|
||||
s32i.n a14, a3, 4 // Save 32 bits from a15 to dest_buff a3, offset 4
|
||||
s32i.n a13, a3, 8 // Save 32 bits from a15 to dest_buff a3, offset 8
|
||||
s32i.n a12, a3, 12 // Save 32 bits from a15 to dest_buff a3, offset 12
|
||||
addi.n a7, a7, 16 // Increment src_buff pointer a7 by 16
|
||||
addi.n a3, a3, 16 // Increment dest_buff pointer a3 by 16
|
||||
._main_loop_aligned:
|
||||
|
||||
// Finish the remaining bytes out of the main loop
|
||||
|
||||
// Check modulo 8 of the dest_w_bytes (a11), if - then copy 8 bytes (4 RGB565 pixels)
|
||||
// src_buff a7, dest_buff a3, dest_w_bytes a11, copy registers a14 a15
|
||||
macro_memcpy_mod_8 a7, a3, a11, a14, a15 __LINE__
|
||||
|
||||
// Check modulo 4 of the dest_w_bytes (a11), if - then copy 4 bytes (2 RGB565 pixels)
|
||||
// src_buff a7, dest_buff a3, dest_w_bytes a11, copy register a15
|
||||
macro_memcpy_mod_4 a7, a3, a11, a15 __LINE__
|
||||
|
||||
// Check modulo 2 of the dest_w_bytes (a11), if - then copy 2 bytes (1 RGB565 pixel)
|
||||
// src_buff a7, dest_buff a3, dest_w_bytes a11, copy register a15
|
||||
macro_memcpy_mod_2 a7, a3, a11, a15 __LINE__
|
||||
|
||||
// Check modulo 1 of the dest_w_bytes (a11), if - then copy 1 byte (1/2 RGB565 pixel)
|
||||
// src_buff a7, dest_buff a3, dest_w_bytes a11, copy register a15
|
||||
macro_memcpy_mod_1 a7, a3, a11, a15 __LINE__
|
||||
|
||||
add a3, a3, a6 // dest_buff (a3) = dest_buff (a3) + dest_matrix_padding (a6)
|
||||
add a7, a7, a8 // src_buff (a7) = src_buff (a7) + src_matrix_padding (a8)
|
||||
addi.n a5, a5, -1 // Decrease the outer loop
|
||||
bnez a5, .outer_loop_align
|
||||
|
||||
movi.n a2, 1 // Return LV_RESULT_OK = 1
|
||||
retw.n // Return
|
||||
|
||||
|
||||
//**********************************************************************************************************************
|
||||
|
||||
// The most general case - at leas one array is not aligned, or one parameter is not multiple of 4
|
||||
_alignment_check:
|
||||
|
||||
// dest_buff (a3) - 4-byte aligned, or not
|
||||
// src_buff (a7) - 4-byte aligned, or not
|
||||
// dest_stride (a6) - 4-byte multiple, or not
|
||||
// src_stride (a8) - 4-byte multiple, or not
|
||||
// dest_w (a4) - 4-byte multiple, or not
|
||||
|
||||
// Convert strides to matrix paddings
|
||||
sub a6, a6, a11 // dest_matrix_padding (a6) = dest_stride (a6) - dest_w_bytes (a11)
|
||||
sub a8, a8, a11 // src_matrix_padding (a8) = src_stride (a8) - dest_w_bytes (a11)
|
||||
|
||||
.outer_loop_unalign:
|
||||
|
||||
extui a13, a3, 0, 2 // Get last two bits of the dest_buff address a3, to a13
|
||||
movi.n a15, 4 // Move 4 to a15, for calculation of the destination alignment loop
|
||||
sub a14, a15, a13 // Calculate destination alignment loop length (a14 = 4 - a13)
|
||||
|
||||
// In case of the dest_buff a3 being already aligned (for example by matrix padding), correct a14 value,
|
||||
// to prevent the destination aligning loop to run 4 times (to prevent aligning already aligned memory)
|
||||
moveqz a14, a13, a13 // If a13 is zero, move a13 to a14, move 0 to a14
|
||||
|
||||
sub a10, a11, a14 // Get the dest_w_bytes after the aligning loop
|
||||
srli a9, a10, 4 // Calculate main loop len (a9 = dest_w_bytes_local / 16)
|
||||
|
||||
// Run dest_buff aligning loop byte by byte
|
||||
loopnez a14, ._dest_aligning_loop
|
||||
l8ui a15, a7, 0 // Load 8 bits from src_buff a7 to a15, offset 0
|
||||
addi.n a7, a7, 1 // Increment src_buff pointer a7 by 1
|
||||
s8i a15, a3, 0 // Save 8 bits from a15 to dest_buff a3, offset 0
|
||||
addi.n a3, a3, 1 // Increment dest_buff pointer a3 by 1
|
||||
._dest_aligning_loop:
|
||||
|
||||
// Destination is aligned, source is unaligned
|
||||
|
||||
// For more information about this implementation, see chapter 3.3.2 Shifts and the Shift Amount Register (SAR)
|
||||
// in Xtensa Instruction Set Architecture (ISA) Reference Manual
|
||||
|
||||
ssa8l a7 // Set SAR_BYTE from src_buff a7 unalignment
|
||||
extui a4, a7, 0, 2 // Get last 2 bits of the src_buff, a4 = src_buff_unalignment
|
||||
sub a7, a7, a4 // "align" the src_buff a7, to 4-byte boundary by decreasing it's pointer to the nearest aligned boundary
|
||||
|
||||
// First preload for the loopnez cycle
|
||||
l32i.n a15, a7, 0 // Load 32 bits from 4-byte aligned src_buff a7 to a15, offset 0
|
||||
|
||||
// Run main loop which copies 16 bytes (8 RGB565 pixels) in one loop run
|
||||
loopnez a9, ._main_loop_unalign
|
||||
l32i.n a14, a7, 4 // Load 32 bits from 4-byte aligned src_buff a7 to a14, offset 4
|
||||
l32i.n a13, a7, 8 // Load 32 bits from 4-byte aligned src_buff a7 to a13, offset 8
|
||||
src a15, a14, a15 // Concatenate a14 and a15 and shift by SAR_BYTE amount to a15
|
||||
s32i.n a15, a3, 0 // Save 32 bits from shift-corrected a15 to dest_buff a3, offset 0
|
||||
l32i.n a12, a7, 12 // Load 32 bits from 4-byte aligned src_buff a7 to a12, offset 12
|
||||
src a14, a13, a14 // Concatenate a13 and a14 and shift by SAR_BYTE amount to a14
|
||||
s32i.n a14, a3, 4 // Save 32 bits from shift-corrected a14 to dest_buff a3, offset 4
|
||||
l32i.n a15, a7, 16 // Load 32 bits from 4-byte aligned src_buff a7 to a15, offset 16
|
||||
src a13, a12, a13 // Concatenate a12 and a13 and shift by SAR_BYTE amount to a13
|
||||
s32i.n a13, a3, 8 // Save 32 bits from shift-corrected a13 to dest_buff a3, offset 8
|
||||
addi.n a7, a7, 16 // Increment src_buff pointer a7 by 16
|
||||
src a12, a15, a12 // Concatenate a15 and a12 and shift by SAR_BYTE amount to a12
|
||||
s32i.n a12, a3, 12 // Save 32 bits from shift-corrected a12 to dest_buff a3, offset 12
|
||||
addi.n a3, a3, 16 // Increment dest_buff pointer a3 by 16
|
||||
._main_loop_unalign:
|
||||
|
||||
// Finish the remaining bytes out of the loop
|
||||
// Check modulo 8 of the dest_w_bytes_local (a10), if - then copy 8 bytes
|
||||
bbci a10, 3, _mod_8_check // Branch if 3-rd bit of dest_w_bytes_local is clear
|
||||
l32i.n a14, a7, 4 // Load 32 bits from 4-byte aligned src_buff a7 to a14, offset 4
|
||||
l32i.n a13, a7, 8 // Load 32 bits from 4-byte aligned src_buff a7 to a13, offset 8
|
||||
src a15, a14, a15 // Concatenate a14 and a15 and shift by SAR_BYTE amount to a15 (value in a15 is already prepared from previous steps)
|
||||
s32i.n a15, a3, 0 // Save 32 bits from shift-corrected a15 to dest_buff a3, offset 0
|
||||
addi.n a7, a7, 8 // Increment src_buff pointer a7 by 8
|
||||
src a14, a13, a14 // Concatenate a13 and a14 and shift by SAR_BYTE amount to a14
|
||||
s32i.n a14, a3, 4 // Save 32 bits from shift-corrected a14 to dest_buff a3, offset 4
|
||||
addi.n a3, a3, 8 // Increment dest_buff pointer a3 by 8
|
||||
mov a15, a13 // Prepare a15 for the next steps (copy a13 to a15)
|
||||
_mod_8_check:
|
||||
|
||||
// Check modulo 4 of the dest_w_bytes_local (a10), if - then copy 4 bytes
|
||||
bbci a10, 2, _mod_4_check // Branch if 2-nd bit of dest_w_bytes_local is clear
|
||||
l32i.n a14, a7, 4 // Load 32 bits from 4-byte aligned src_buff a7 to a14, offset 4
|
||||
addi.n a7, a7, 4 // Increment src_buff pointer a7 by 4
|
||||
src a15, a14, a15 // Concatenate a14 and a15 and shift by SAR_BYTE amount to a15 (value in a15 is already prepared from previous steps)
|
||||
s32i.n a15, a3, 0 // Save 32 bits from shift-corrected a15 to dest_buff a3, offset 0
|
||||
addi.n a3, a3, 4 // Increment dest_buff pointer a3 by 4
|
||||
mov a15, a14 // Prepare a15 for the next steps (copy a14 to a15)
|
||||
_mod_4_check:
|
||||
|
||||
extui a13, a10, 0, 2 // Get the last 2 bytes of the dest_w_bytes_local (a10), a13 = a10[1:0], to find out how many bytes are needs copied and to increase src and dest pointer accordingly
|
||||
beqz a13, _mod_1_2_check // Branch if a13 equal to zero, E.G. if there are no bytes to be copied
|
||||
l32i.n a14, a7, 4 // Load 32 bits from 4-byte aligned src_buff a7 to a14, offset 4
|
||||
l32i.n a12, a3, 0 // Get dest_buff value: Load 32 bits from 4-byte aligned dest_buff a3 to a12, offset 0
|
||||
src a15, a14, a15 // Concatenate a14 and a15 and shift by SAR_BYTE amount to a15 (value in a15 is already prepared from previous steps)
|
||||
ssa8l a10 // Set SAR_BYTE from dest_w_bytes_local a10 length
|
||||
sll a15, a15 // Shift the dest word a15 by SAR_BYTE amount
|
||||
srl a12, a12 // Shift the src word a12 by SAR_BYTE amount
|
||||
ssa8b a10 // Set SAR_BYTE from dest_w_bytes_local a10 length
|
||||
src a12, a12, a15 // Concatenate a12 and a15 and shift by SAR_BYTE amount to a12
|
||||
s32i.n a12, a3, 0 // Save 32 bits from shift-corrected a12 to dest_buff a3, offset 0
|
||||
add a7, a7, a13 // Increment src_buff pointer a7, by amount of copied bytes (a13)
|
||||
add a3, a3, a13 // Increment dest_buff pointer a3, by amount of copied bytes (a13)
|
||||
_mod_1_2_check:
|
||||
|
||||
add a7, a7, a4 // Correct the src_buff back by src_buff_unalignment (a4), after we have force-aligned it to 4-byte boundary before the main loop
|
||||
add a3, a3, a6 // dest_buff + dest_stride
|
||||
add a7, a7, a8 // src_buff + src_stride
|
||||
addi.n a5, a5, -1 // Decrease the outer loop
|
||||
bnez a5, .outer_loop_unalign
|
||||
|
||||
movi.n a2, 1 // Return LV_RESULT_OK = 1
|
||||
retw.n // Return
|
||||
|
||||
//**********************************************************************************************************************
|
||||
|
||||
// Small matrix width, keep it simple for lengths less than 8 pixels
|
||||
_matrix_width_check: // Matrix width is greater or equal 8 pixels
|
||||
|
||||
// Convert strides to matrix paddings
|
||||
sub a6, a6, a11 // dest_matrix_padding (a6) = dest_stride (a6) - dest_w_bytes (a11)
|
||||
sub a8, a8, a11 // src_matrix_padding (a8) = src_stride (a8) - dest_w_bytes (a11)
|
||||
|
||||
.outer_loop_short_matrix_length:
|
||||
|
||||
// Run main loop which copies 2 bytes (one RGB565 pixel) in one loop run
|
||||
loopnez a4, ._main_loop_short_matrix_length
|
||||
l8ui a15, a7, 0 // Load 8 bits from src_buff a7 to a15, offset 0
|
||||
l8ui a14, a7, 1 // Load 8 bits from src_buff a7 to a14, offset 1
|
||||
s8i a15, a3, 0 // Save 8 bits from a15 to dest_buff a3, offset 0
|
||||
s8i a14, a3, 1 // Save 8 bits from a14 to dest_buff a3, offset 1
|
||||
addi.n a7, a7, 2 // Increment src_buff pointer a7 by 1
|
||||
addi.n a3, a3, 2 // Increment dest_buff pointer a3 by 2
|
||||
._main_loop_short_matrix_length:
|
||||
|
||||
// Finish remaining byte out of the main loop
|
||||
|
||||
// Check modulo 1 of the dest_w_bytes (a11), if - then copy 1 byte (1/2 RGB565 pixel)
|
||||
// src_buff a7, dest_buff a3, dest_w_bytes a11, copy register a15
|
||||
macro_memcpy_mod_1 a7, a3, a11, a15, __LINE__
|
||||
|
||||
add a3, a3, a6 // dest_buff (a3) = dest_buff (a3) + dest_matrix_padding (a6)
|
||||
add a7, a7, a8 // src_buff (a7) = src_buff (a7) + src_matrix_padding (a8)
|
||||
addi.n a5, a5, -1 // Decrease the outer loop
|
||||
bnez a5, .outer_loop_short_matrix_length
|
||||
|
||||
movi.n a2, 1 // Return LV_RESULT_OK = 1
|
||||
retw.n // Return
|
||||
@@ -0,0 +1,372 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "lv_macro_memcpy.S" // Memcpy macros
|
||||
|
||||
// This is LVGL RGB565 image blend to RGB565 for ESP32S3 processor
|
||||
|
||||
.section .text
|
||||
.align 4
|
||||
.global lv_rgb565_blend_normal_to_rgb565_esp
|
||||
.type lv_rgb565_blend_normal_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_rgb565_blend_normal_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
|
||||
l32i.n a8, a2, 24 // a8 - src_stride in bytes
|
||||
movi.n a10, 0xf // 0xf alignment mask (16-byte alignment)
|
||||
slli a11, a4, 1 // a11 - dest_w_bytes = sizeof(uint16_t) * dest_w
|
||||
|
||||
// No need to convert any colors here, we are copying from rgb565 to rgb565
|
||||
|
||||
// Check dest_w length
|
||||
bltui a4, 8, _matrix_width_check // Branch if dest_w (a4) is lower than 8
|
||||
|
||||
// Check dest_buff alignment fist
|
||||
and a15, a10, a3 // 16-byte alignment mask AND dest_buff pointer a3
|
||||
bnez a15, _src_unalign_dest_unalign // Branch if a15 not equals to zero
|
||||
// Jump straight to the last implementation, since this is the only one which deals with unaligned destination arrays
|
||||
|
||||
// Check src_buff alignment
|
||||
and a15, a10, a7 // 16-byte alignment mask AND src_buff pointer a7
|
||||
bnez a15, _src_align_dest_unalign // Branch if a15 not equals to zero
|
||||
// Jump to check, if the second or third implementation can be used (depends on both strides and dest_w)
|
||||
|
||||
// Check dest_stride alignment
|
||||
and a15, a10, a6 // 16-byte alignment mask AND dest_stride a6
|
||||
bnez a15, _src_unalign_dest_unalign // Branch if a15 not equals to zero
|
||||
// Jump straight to the last implementation, since this is the only one which deals with destination stride not aligned
|
||||
|
||||
// Check src_stride alignment
|
||||
and a15, a10, a8 // 16-byte alignment mask AND src_stride a8
|
||||
bnez a15, _src_align_dest_unalign // Branch if a15 not equals to zero
|
||||
// Jump to check, if the second or third implementation can be used (depends on dest_w_bytes)
|
||||
|
||||
// Check dest_w_bytes alignment
|
||||
and a15, a10, a11 // 16-byte alignment mask AND dest_w_bytes
|
||||
bnez a15, _src_unalign_dest_unalign // Branch if a15 not equals to zero
|
||||
// Jump straight to the last implementation, since this is the only one which deals with dest_w_bytes not aligned
|
||||
|
||||
//**********************************************************************************************************************
|
||||
|
||||
// The most ideal case - both arrays aligned, both strides and dest_w are multiples of 16
|
||||
|
||||
// dest_buff (a3) - 16-byte aligned
|
||||
// src_buff (a7) - 16-byte aligned
|
||||
// dest_stride (a6) - 16-byte multiple
|
||||
// src_stride (a8) - 16-byte multiple
|
||||
// dest_w (a4) - 16-byte multiple
|
||||
|
||||
srli a9, a4, 4 // a9 - loop_len = dest_w / 16
|
||||
// Convert strides to matrix paddings
|
||||
sub a6, a6, a11 // dest_matrix_padding (a6) = dest_stride (a6) - dest_w_bytes (a11)
|
||||
sub a8, a8, a11 // src_matrix_padding (a8) = src_stride (a8) - dest_w_bytes (a11)
|
||||
|
||||
.outer_loop_align:
|
||||
|
||||
// Run main loop which copies 32 bytes (16 RGB565 pixels) in one loop run
|
||||
loopnez a9, ._main_loop_align // 32 bytes (16 RGB565 pixels) in one loop run
|
||||
ee.vld.128.ip q0, a7, 16 // Load 16 bytes from src_buff a7 to q0, increase src_buf pointer a7 by 16
|
||||
ee.vld.128.ip q1, a7, 16 // Load 16 bytes from src_buff a7 to q1, increase src_buf pointer a7 by 16
|
||||
ee.vst.128.ip q0, a3, 16 // Store 16 bytes from q0 to dest_buff a3, increase dest_buff pointer a3 by 16
|
||||
ee.vst.128.ip q1, a3, 16 // Store 16 bytes from q1 to dest_buff a3, increase dest_buff pointer a3 by 16
|
||||
._main_loop_align:
|
||||
|
||||
// Finish remaining bytes out of the main loop
|
||||
|
||||
// Check modulo 16 of the dest_w, if - then copy 16 bytes (8 RGB565 pixels)
|
||||
bbci a11, 4, _align_mod_16_check // Branch if 4-th bit of dest_w_bytes a11 is clear
|
||||
ee.vld.128.ip q0, a7, 16 // Load 16 bytes from src_buff a7 to q0, increase src_buf pointer a7 by 16
|
||||
ee.vst.128.ip q0, a3, 16 // Store 16 bytes from q0 to dest_buff a3, increase dest_buff pointer a3 by 16
|
||||
_align_mod_16_check:
|
||||
|
||||
add a3, a3, a6 // dest_buff (a3) = dest_buff (a3) + dest_matrix_padding (a6)
|
||||
add a7, a7, a8 // src_buff (a7) = src_buff (a7) + src_matrix_padding (a8)
|
||||
addi.n a5, a5, -1 // Decrease the outer loop
|
||||
bnez a5, .outer_loop_align
|
||||
|
||||
movi.n a2, 1 // Return LV_RESULT_OK = 1
|
||||
retw.n // Return
|
||||
|
||||
|
||||
_src_align_dest_unalign:
|
||||
|
||||
// Check dest_stride alignment
|
||||
and a15, a10, a6 // 16-byte alignment mask AND dest_stride a6
|
||||
bnez a15, _src_unalign_dest_unalign // Branch if a15 not equals to zero
|
||||
|
||||
// Check dest_w_bytes alignment
|
||||
and a15, a10, a11 // 16-byte alignment mask AND dest_w_bytes a11
|
||||
bnez a15, _src_unalign_dest_unalign // Branch if a15 not equals to zero
|
||||
|
||||
// We don't check src_stride alignment for this implementation, as it can be either align, or unalign
|
||||
|
||||
//**********************************************************************************************************************
|
||||
|
||||
// Less ideal case - Only destination array is aligned, src array is unaligned
|
||||
// Source stride is either aligned or unaligned, destination stride must be aligned, dest_w_bytes must be aligned
|
||||
|
||||
// dest_buff (a3) - 16-byte aligned
|
||||
// src_buff (a7) - unaligned
|
||||
// dest_stride (a6) - 16-byte multiple
|
||||
// src_stride (a8) - does not matter if 16-byte multiple
|
||||
// dest_w (a4) - 16-byte multiple
|
||||
|
||||
// Convert strides to matrix paddings
|
||||
sub a6, a6, a11 // dest_matrix_padding (a6) = dest_stride (a6) - dest_w_bytes (a11)
|
||||
sub a8, a8, a11 // src_matrix_padding (a8) = src_stride (a8) - dest_w_bytes (a11)
|
||||
|
||||
// Calculate modulo for non-aligned data
|
||||
movi a15, 48 // a15 = 48 (main loop copies 48 bytes)
|
||||
quou a9, a11, a15 // a9 = dest_w_bytes (a11) DIV 48 (15)
|
||||
remu a12, a11, a15 // a12 = dest_w_bytes (a11) remainder after DIV 48 (15)
|
||||
|
||||
.outer_loop_src_unalign_dest_align:
|
||||
|
||||
ee.ld.128.usar.ip q2, a7, 16 // Preload 16 bytes from src_buff a7 to q2, get value of the SAR_BYTE, increase src_buf pointer a7 by 16
|
||||
ee.ld.128.usar.ip q3, a7, 16 // Preload 16 bytes from src_buff a7 to q3, get value of the SAR_BYTE, increase src_buf pointer a7 by 16
|
||||
|
||||
// Run main loop which copies 48 bytes (24 RGB565 pixels) in one loop run
|
||||
loopnez a9, ._main_loop_src_unalign_dest_align // 48 bytes (24 RGB565 pixels) in one loop
|
||||
ee.src.q.ld.ip q4, a7, 16, q2, q3 // Load 16 bytes from src_buff a7 to q4, concatenate q2 and q3 and shift to q2 by the SAR_BYTE amount, increase src_buf pointer a7 by 16
|
||||
ee.vst.128.ip q2, a3, 16 // Store 16 bytes from q2 to aligned dest_buff a3, increase dest_buff pointer a3 by 16
|
||||
ee.src.q.ld.ip q2, a7, 16, q3, q4 // Load 16 bytes from src_buff a7 to q2, concatenate q3 and q4 and shift to q3 by the SAR_BYTE amount, increase src_buf pointer a7 by 16
|
||||
ee.vst.128.ip q3, a3, 16 // Store 16 bytes from q3 to aligned dest_buff a3, increase dest_buff pointer a3 by 16
|
||||
ee.src.q.ld.ip q3, a7, 16, q4, q2 // Load 16 bytes from src_buff a7 to q3, concatenate q4 and q2 and shift to q4 by the SAR_BYTE amount, increase src_buf pointer a7 by 16
|
||||
ee.vst.128.ip q4, a3, 16 // Store 16 bytes from q4 to aligned dest_buff a3, increase dest_buff pointer a3 by 16
|
||||
._main_loop_src_unalign_dest_align:
|
||||
|
||||
// Finish the main loop outside of the loop from Q registers preloads
|
||||
|
||||
// Check modulo 32 of the loop_len_remainder, if - then copy 32 bytes (16 RGB565 pixels)
|
||||
bbci a12, 5, _unalign_mod_32_check // Branch if 5-th bit of loop_len_remainder a12 is clear
|
||||
ee.src.q.ld.ip q4, a7, 0, q2, q3 // Load 16 bytes from src_buff a7 to q4, concatenate q2 and q3 and shift to q2 by the SAR_BYTE amount, don't increase src_buf pointer a7
|
||||
ee.vst.128.ip q2, a3, 16 // Store 16 bytes from q2 to aligned dest_buff a3, increase dest_buff pointer a3 by 16
|
||||
ee.src.q q3, q3, q4 // Concatenate q3 and q4 and shift to q3 by the SAR_BYTE amount
|
||||
ee.vst.128.ip q3, a3, 16 // Store 16 bytes from q3 to aligned dest_buff a3, increase dest_buff pointer a3 by 16
|
||||
j _end_of_row_src_unalign_dest_align
|
||||
_unalign_mod_32_check:
|
||||
|
||||
// Check modulo 16 of the loop_len_remainder, if - then copy 16 bytes (8 RGB565 pixels)
|
||||
bbci a12, 4, _unalign_mod_16_check // Branch if 4-th bit of loop_len_remainder a12 is clear
|
||||
ee.src.q q2, q2, q3 // Concatenate q2 and q3 and shift to q2 by the SAR_BYTE amount
|
||||
ee.vst.128.ip q2, a3, 16 // Store 16 bytes from q2 to aligned dest_buff a3, increase dest_buff pointer a3 by 16
|
||||
addi a7, a7, -16 // Correct the src_buff pointer a7, caused by q reg preload
|
||||
j _end_of_row_src_unalign_dest_align
|
||||
_unalign_mod_16_check:
|
||||
|
||||
// Nothing to copy outside of the main loop
|
||||
addi a7, a7, -32 // Correct the src_buff pointer a7, caused by q reg preload
|
||||
|
||||
_end_of_row_src_unalign_dest_align:
|
||||
|
||||
add a3, a3, a6 // dest_buff (a3) = dest_buff (a3) + dest_matrix_padding (a6)
|
||||
add a7, a7, a8 // src_buff (a7) = src_buff (a7) + src_matrix_padding (a8)
|
||||
addi.n a5, a5, -1 // Decrease the outer loop
|
||||
bnez a5, .outer_loop_src_unalign_dest_align
|
||||
|
||||
movi.n a2, 1 // Return LV_RESULT_OK = 1
|
||||
retw.n // Return
|
||||
|
||||
|
||||
_src_unalign_dest_unalign:
|
||||
|
||||
//**********************************************************************************************************************
|
||||
|
||||
// The most general case, can handle all the possible combinations
|
||||
|
||||
// dest_buff (a3) - unaligned
|
||||
// src_buff (a7) - unaligned
|
||||
// dest_stride (a6) - not 16-byte multiple
|
||||
// src_stride (a8) - not 16-byte multiple
|
||||
// dest_w (a4) - not 16-byte multiple
|
||||
|
||||
// Convert strides to matrix paddings
|
||||
sub a6, a6, a11 // dest_matrix_padding (a6) = dest_stride (a6) - dest_w_bytes (a11)
|
||||
sub a8, a8, a11 // src_matrix_padding (a8) = src_stride (a8) - dest_w_bytes (a11)
|
||||
|
||||
.outer_loop_all_unalign:
|
||||
|
||||
// dest_buff alignment check
|
||||
and a13, a10, a3 // Alignment mask 0xf (a10) AND dest_buff pointer
|
||||
beqz a13, _dest_buff_aligned // Branch if a13 = 0 (if dest_buff is aligned)
|
||||
|
||||
movi.n a14, 16 // a14 = 16
|
||||
sub a13, a14, a13 // a13 = 16 - unalignment
|
||||
|
||||
// Check modulo 8 of the unalignment a13, if - then copy 8 bytes (4 RGB565 pixels)
|
||||
// src_buff a7, dest_buff a3, unalignment a13, copy registers a14, a15
|
||||
macro_memcpy_mod_8 a7, a3, a13, a15, a14, __LINE__
|
||||
|
||||
// Check modulo 4 of the unalignment, if - then copy 4 bytes (2 RGB565 pixels)
|
||||
// src_buff a7, dest_buff a3, unalignment a13, copy register a15
|
||||
macro_memcpy_mod_4 a7, a3, a13, a15, __LINE__
|
||||
|
||||
// Check modulo 2 of the unalignment, if - then copy 2 bytes (1 RGB565 pixel)
|
||||
// src_buff a7, dest_buff a3, unalignment a13, copy register a15
|
||||
macro_memcpy_mod_2 a7, a3, a13, a15, __LINE__
|
||||
|
||||
// Check modulo 1 of the unalignment, if - then copy 1 byte (1/2 of RGB565 pixel)
|
||||
// src_buff a7, dest_buff a3, unalignment a13, copy register a15
|
||||
macro_memcpy_mod_1 a7, a3, a13, a15, __LINE__
|
||||
|
||||
_dest_buff_aligned:
|
||||
|
||||
// Calculate modulo for non-aligned data
|
||||
sub a11, a11, a13 // a11 = local_dest_w_bytes (a11) = dest_w_bytes (a11) - (16 - unalignment)
|
||||
movi a15, 48 // a15 = 48
|
||||
quou a9, a11, a15 // a9 = local_dest_w_bytes (a11) DIV 48 (a15)
|
||||
remu a12, a11, a15 // a12 = local_dest_w_bytes (a11) remainder after div 48 (a15)
|
||||
|
||||
ee.ld.128.usar.ip q2, a7, 16 // Preload 16 bytes from src_buff a7 to q2, get value of the SAR_BYTE, increase src_buf pointer a7 by 16
|
||||
ee.ld.128.usar.ip q3, a7, 16 // Preload 16 bytes from src_buff a7 to q3, get value of the SAR_BYTE, increase src_buf pointer a7 by 16
|
||||
|
||||
// Run main loop which copies 48 bytes (24 RGB565 pixels) in one loop run
|
||||
loopnez a9, ._main_loop_all_unalign // 48 bytes (24 RGB565 pixels) in one loop
|
||||
ee.src.q.ld.ip q4, a7, 16, q2, q3 // Load 16 bytes from src_buff a7 to q4, concatenate q2 and q3 and shift to q2 by the SAR_BYTE amount, increase src_buf pointer a7 by 16
|
||||
ee.vst.128.ip q2, a3, 16 // Store 16 bytes from q2 to aligned dest_buff a3, increase dest_buff pointer a3 by 16
|
||||
ee.src.q.ld.ip q2, a7, 16, q3, q4 // Load 16 bytes from src_buff a7 to q2, concatenate q3 and q4 and shift to q3 by the SAR_BYTE amount, increase src_buf pointer a7 by 16
|
||||
ee.vst.128.ip q3, a3, 16 // Store 16 bytes from q3 to aligned dest_buff a3, increase dest_buff pointer a3 by 16
|
||||
ee.src.q.ld.ip q3, a7, 16, q4, q2 // Load 16 bytes from src_buff a7 to q3, concatenate q4 and q2 and shift to q4 by the SAR_BYTE amount, increase src_buf pointer a7 by 16
|
||||
ee.vst.128.ip q4, a3, 16 // Store 16 bytes from q4 to aligned dest_buff a3, increase dest_buff pointer a3 by 16
|
||||
._main_loop_all_unalign:
|
||||
|
||||
// Finish the main loop outside of the loop from Q registers preloads
|
||||
|
||||
// Check modulo 32 and modulo 8 of the loop_len_remainder a12
|
||||
bbci a12, 5, _all_unalign_mod_32_check // Branch if 5-th bit of loop_len_remainder a12 is clear
|
||||
bbsi a12, 3, _all_unalign_mod_32_mod_8_check // Branch if 3-rd bif of loop_len_remainder a12 is set
|
||||
|
||||
// Copy 32 bytes (16 RGB565 pixels) (47 - 40)
|
||||
ee.src.q.ld.ip q4, a7, 0, q2, q3 // Load 16 bytes from src_buff a7 to q4, concatenate q2 and q3 and shift to q2 by the SAR_BYTE amount, don't increase src_buf pointer a7
|
||||
ee.vst.128.ip q2, a3, 16 // Store 16 bytes from q2 to aligned dest_buff a3, increase dest_buff pointer a3 by 16
|
||||
ee.src.q q3, q3, q4 // Concatenate q3 and q4 and shift to q3 by the SAR_BYTE amount
|
||||
ee.vst.128.ip q3, a3, 16 // Store 16 bytes from q3 to aligned dest_buff a3, increase dest_buff pointer a3 by 16
|
||||
j _skip_mod16
|
||||
|
||||
_all_unalign_mod_32_mod_8_check:
|
||||
// Copy 40 bytes (20 RGB565 pixels)
|
||||
ee.src.q.ld.ip q4, a7, 16, q2, q3 // Load 16 bytes from src_buff a7 to q4, concatenate q2 and q3 and shift to q2 by the SAR_BYTE amount, increase src_buf pointer a7 by 16
|
||||
ee.vst.128.ip q2, a3, 16 // Store 16 bytes from q2 to aligned dest_buff a3, increase dest_buff pointer a3 by 16
|
||||
ee.src.q.ld.ip q2, a7, 0, q3, q4 // Load 16 bytes from src_buff a7 to q2, concatenate q3 and q4 and shift to q3 by the SAR_BYTE amount, don't increase src_buf pointer a7
|
||||
ee.vst.128.ip q3, a3, 16 // Store 16 bytes from q3 to aligned dest_buff a3, increase dest_buff pointer a3 by 16
|
||||
ee.src.q q4, q4, q2 // Concatenate q4 and q2 and shift to q4 by the SAR_BYTE amount
|
||||
ee.vst.l.64.ip q4, a3, 8 // Store lower 8 bytes from q4 to aligned dest_buff a3, increase dest_buff pointer a3 by 8
|
||||
addi a7, a7, -8 // Correct the src_buff pointer a7, caused by q reg preload
|
||||
j _skip_mod16
|
||||
|
||||
_all_unalign_mod_32_check:
|
||||
|
||||
// Check modulo 16 and modulo 8 of the loop_len_remainder a12
|
||||
bbci a12, 4, _all_unalign_mod_16_check // branch if 4-th bit of loop_len_remainder a12 is clear
|
||||
bbsi a12, 3, _all_unalign_mod_16_mod_8_check // branch if 3-rd bit of loop_len_remainder a12 is set
|
||||
|
||||
// Copy 16 bytes (8 RGB565 pixels)
|
||||
ee.src.q q2, q2, q3 // Concatenate q2 and q3 and shift to q2 by the SAR_BYTE amount
|
||||
ee.vst.128.ip q2, a3, 16 // Store 16 bytes from q2 to aligned dest_buff a3, increase dest_buff pointer a3 by 16
|
||||
addi a7, a7, -16 // Correct the src_buff pointer a7, caused by q reg preload
|
||||
j _skip_mod16
|
||||
|
||||
_all_unalign_mod_16_mod_8_check:
|
||||
// Copy 24 bytes (12 RGB565 pixels)
|
||||
ee.src.q.ld.ip q4, a7, 0, q2, q3 // Load 16 bytes from src_buff a7 to q4, concatenate q2 and q3 and shift to q2 by the SAR_BYTE amount, don't increase src_buf pointer a7
|
||||
ee.vst.128.ip q2, a3, 16 // Store 16 bytes from q2 to aligned dest_buff a3, increase dest_buff pointer a3 by 16
|
||||
ee.src.q q3, q3, q4 // Concatenate q3 and q4 and shift to q3 by the SAR_BYTE amount
|
||||
ee.vst.l.64.ip q3, a3, 8 // Store lower 8 bytes from q3 to aligned dest_buff a3, increase dest_buff pointer a3 by 8
|
||||
addi a7, a7, -8 // Correct the src_buff pointer a7, caused by q reg preload
|
||||
j _skip_mod16
|
||||
_all_unalign_mod_16_check:
|
||||
|
||||
bbci a12, 3, _all_unalign_mod_8_check // Branch if 3-rd bit of loop_len_remainder a12 is clear
|
||||
// Copy 8 bytes (4 RGB565 pixels)
|
||||
ee.src.q q2, q2, q3 // Concatenate q2 and q3 and shift to q2 by the SAR_BYTE amount
|
||||
ee.vst.l.64.ip q2, a3, 8 // Store lower 8 bytes from q2 to aligned dest_buff a3, increase dest_buff pointer a3 by 8
|
||||
addi a7, a7, -24 // Correct the src_buff pointer a7, caused by q reg preload
|
||||
j _skip_mod16
|
||||
_all_unalign_mod_8_check:
|
||||
|
||||
addi a7, a7, -32 // Correct the src_buff pointer a7, caused by q reg preload
|
||||
|
||||
_skip_mod16:
|
||||
|
||||
// Check modulo 4 of the loop_len_remainder, if - then copy 4 bytes (2 RGB565 pixels)
|
||||
// src_buff a7, dest_buff a3, loop_len_remainder a12, copy register a15
|
||||
macro_memcpy_mod_4 a7, a3, a12, a15, __LINE__
|
||||
|
||||
// Check modulo 2 of the loop_len_remainder, if - then copy 2 bytes (1 RGB565 pixel)
|
||||
// src_buff a7, dest_buff a3, loop_len_remainder a12, copy register a15
|
||||
macro_memcpy_mod_2 a7, a3, a12, a15, __LINE__
|
||||
|
||||
// Check modulo 1 of the loop_len_remainder, if - then copy 1 byte (1/2 RGB565 pixel)
|
||||
// src_buff a7, dest_buff a3, loop_len_remainder a12, copy register a15
|
||||
macro_memcpy_mod_1 a7, a3, a12, a15, __LINE_
|
||||
|
||||
slli a11, a4, 1 // Refresh dest_w_bytes
|
||||
add a3, a3, a6 // dest_buff (a3) = dest_buff (a3) + dest_matrix_padding (a6)
|
||||
add a7, a7, a8 // src_buff (a7) = src_buff (a7) + src_matrix_padding (a8)
|
||||
addi.n a5, a5, -1 // Decrease the outer loop
|
||||
bnez a5, .outer_loop_all_unalign
|
||||
|
||||
movi.n a2, 1 // Return LV_RESULT_OK = 1
|
||||
retw.n // Return
|
||||
|
||||
//**********************************************************************************************************************
|
||||
|
||||
// Small matrix width, keep it simple for lengths less than 8 pixels
|
||||
_matrix_width_check: // Matrix width is greater or equal 8 pixels
|
||||
|
||||
// Convert strides to matrix paddings
|
||||
sub a6, a6, a11 // dest_matrix_padding (a6) = dest_stride (a6) - dest_w_bytes (a11)
|
||||
sub a8, a8, a11 // src_matrix_padding (a8) = src_stride (a8) - dest_w_bytes (a11)
|
||||
|
||||
.outer_loop_short_matrix_length:
|
||||
|
||||
// Run main loop which copies 2 bytes (one RGB565 pixel) in one loop run
|
||||
loopnez a4, ._main_loop_short_matrix_length
|
||||
l8ui a15, a7, 0 // Load 8 bits from src_buff a7 to a15, offset 0
|
||||
l8ui a14, a7, 1 // Load 8 bits from src_buff a7 to a14, offset 1
|
||||
s8i a15, a3, 0 // Save 8 bits from a15 to dest_buff a3, offset 0
|
||||
s8i a14, a3, 1 // Save 8 bits from a14 to dest_buff a3, offset 1
|
||||
addi.n a7, a7, 2 // Increment src_buff pointer a7 by 1
|
||||
addi.n a3, a3, 2 // Increment dest_buff pointer a3 by 2
|
||||
._main_loop_short_matrix_length:
|
||||
|
||||
// Finish remaining byte out of the main loop
|
||||
|
||||
// Check modulo 1 of the dest_w_bytes (a11), if - then copy 1 byte (1/2 RGB565 pixel)
|
||||
// src_buff a7, dest_buff a3, dest_w_bytes a11, copy register a15
|
||||
macro_memcpy_mod_1 a7, a3, a11, a15, __LINE__
|
||||
|
||||
add a3, a3, a6 // dest_buff (a3) = dest_buff (a3) + dest_matrix_padding (a6)
|
||||
add a7, a7, a8 // src_buff (a7) = src_buff (a7) + src_matrix_padding (a8)
|
||||
addi.n a5, a5, -1 // Decrease the outer loop
|
||||
bnez a5, .outer_loop_short_matrix_length
|
||||
|
||||
movi.n a2, 1 // Return LV_RESULT_OK = 1
|
||||
retw.n // Return
|
||||
@@ -0,0 +1,261 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "lv_macro_memcpy.S" // Memcpy macros
|
||||
|
||||
// This is LVGL RGB888 image blend to RGB888 for ESP32 processor
|
||||
|
||||
.section .text
|
||||
.align 4
|
||||
.global lv_rgb888_blend_normal_to_rgb888_esp
|
||||
.type lv_rgb888_blend_normal_to_rgb888_esp,@function
|
||||
// The function implements the following C code:
|
||||
// void rgb888_image_blend(_lv_draw_sw_blend_image_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_rgb888_blend_normal_to_rgb888_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
|
||||
l32i.n a8, a2, 24 // a8 - src_stride in bytes
|
||||
slli a11, a4, 1 // a11 = (a4 << 1) + a4
|
||||
add a11, a11, a4 // a11 - dest_w_bytes = sizeof(uint24_t) * dest_w
|
||||
|
||||
// No need to convert any colors here, we are copying from rgb888 to rgb888
|
||||
|
||||
// Check dest_w length
|
||||
bltui a4, 5, _matrix_width_check // Branch if dest_w (a4) is lower than 5 (dest_w_bytes 15)
|
||||
|
||||
// Check memory alignment and input parameters lengths and decide which implementation to use
|
||||
movi.n a10, 0x3 // a10 = 0x3 alignment mask (4-byte alignment)
|
||||
or a15, a7, a3 // a15 = src_buff (a7) OR dest_buff (a3)
|
||||
or a15, a15, a6 // a15 = a15 OR dest_stride (a6)
|
||||
or a15, a15, a8 // a15 = a15 OR src_stride (a8)
|
||||
or a15, a15, a11 // a15 = a15 OR dest_w_bytes (a11)
|
||||
and a15, a15, a10 // a15 = a15 AND alignment mask (a10)
|
||||
bnez a15, _alignment_check // Branch if a15 not equals to zero
|
||||
|
||||
//**********************************************************************************************************************
|
||||
|
||||
// The most ideal case - both arrays aligned, both strides and dest_w are multiples of 4
|
||||
|
||||
// dest_buff (a3) - 4-byte aligned
|
||||
// src_buff (a7) - 4-byte aligned
|
||||
// dest_stride (a6) - 4-byte multiple
|
||||
// src_stride (a8) - 4-byte multiple
|
||||
// dest_w (a4) - 4-byte multiple
|
||||
|
||||
srli a9, a11, 4 // a9 - loop_len = dest_w_bytes / 16
|
||||
// Convert strides to matrix paddings
|
||||
sub a6, a6, a11 // dest_matrix_padding (a6) = dest_stride (a6) - dest_w_bytes (a11)
|
||||
sub a8, a8, a11 // src_matrix_padding (a8) = src_stride (a8) - dest_w_bytes (a11)
|
||||
|
||||
.outer_loop_align:
|
||||
|
||||
// Run main loop which copies 16 bytes (5 and 1/3 of RGB888 pixels) in one loop run
|
||||
loopnez a9, ._main_loop_aligned
|
||||
l32i.n a15, a7, 0 // Load 32 bits from src_buff a7 to a15, offset 0
|
||||
l32i.n a14, a7, 4 // Load 32 bits from src_buff a7 to a14, offset 4
|
||||
l32i.n a13, a7, 8 // Load 32 bits from src_buff a7 to a13, offset 8
|
||||
l32i.n a12, a7, 12 // Load 32 bits from src_buff a7 to a12, offset 12
|
||||
s32i.n a15, a3, 0 // Save 32 bits from a15 to dest_buff a3, offset 0
|
||||
s32i.n a14, a3, 4 // Save 32 bits from a15 to dest_buff a3, offset 4
|
||||
s32i.n a13, a3, 8 // Save 32 bits from a15 to dest_buff a3, offset 8
|
||||
s32i.n a12, a3, 12 // Save 32 bits from a15 to dest_buff a3, offset 12
|
||||
addi.n a7, a7, 16 // Increment src_buff pointer a7 by 16
|
||||
addi.n a3, a3, 16 // Increment dest_buff pointer a3 by 16
|
||||
._main_loop_aligned:
|
||||
|
||||
// Finish the remaining bytes out of the main loop
|
||||
|
||||
// Check modulo 8 of the dest_w_bytes (a11), if - then copy 8 bytes (2 and 2/3 of RGB888 pixels)
|
||||
// src_buff a7, dest_buff a3, dest_w_bytes a11, copy registers a14 a15
|
||||
macro_memcpy_mod_8 a7, a3, a11, a14, a15 __LINE__
|
||||
|
||||
// Check modulo 4 of the dest_w_bytes (a11), if - then copy 4 bytes (1 and 1/3 of RGB888 pixels)
|
||||
// src_buff a7, dest_buff a3, dest_w_bytes a11, copy register a15
|
||||
macro_memcpy_mod_4 a7, a3, a11, a15 __LINE__
|
||||
|
||||
// Check modulo 2 of the dest_w_bytes (a11), if - then copy 2 bytes (2/3 of RGB888 pixel)
|
||||
// src_buff a7, dest_buff a3, dest_w_bytes a11, copy register a15
|
||||
macro_memcpy_mod_2 a7, a3, a11, a15 __LINE__
|
||||
|
||||
// Check modulo 1 of the dest_w_bytes (a11), if - then copy 1 byte (1/3 of RGB888 pixel)
|
||||
// src_buff a7, dest_buff a3, dest_w_bytes a11, copy register a15
|
||||
macro_memcpy_mod_1 a7, a3, a11, a15 __LINE__
|
||||
|
||||
add a3, a3, a6 // dest_buff (a3) = dest_buff (a3) + dest_matrix_padding (a6)
|
||||
add a7, a7, a8 // src_buff (a7) = src_buff (a7) + src_matrix_padding (a8)
|
||||
addi.n a5, a5, -1 // Decrease the outer loop
|
||||
bnez a5, .outer_loop_align
|
||||
|
||||
movi.n a2, 1 // Return LV_RESULT_OK = 1
|
||||
retw.n // Return
|
||||
|
||||
|
||||
//**********************************************************************************************************************
|
||||
|
||||
// The most general case - at leas one array is not aligned, or one parameter is not multiple of 4
|
||||
_alignment_check:
|
||||
|
||||
// dest_buff (a3) - 4-byte aligned, or not
|
||||
// src_buff (a7) - 4-byte aligned, or not
|
||||
// dest_stride (a6) - 4-byte multiple, or not
|
||||
// src_stride (a8) - 4-byte multiple, or not
|
||||
// dest_w (a4) - 4-byte multiple, or not
|
||||
|
||||
// Convert strides to matrix paddings
|
||||
sub a6, a6, a11 // dest_matrix_padding (a6) = dest_stride (a6) - dest_w_bytes (a11)
|
||||
sub a8, a8, a11 // src_matrix_padding (a8) = src_stride (a8) - dest_w_bytes (a11)
|
||||
|
||||
.outer_loop_unalign:
|
||||
|
||||
extui a13, a3, 0, 2 // Get last two bits of the dest_buff address a3, to a13
|
||||
movi.n a15, 4 // Move 4 to a15, for calculation of the destination alignment loop
|
||||
sub a14, a15, a13 // Calculate destination alignment loop length (a14 = 4 - a13)
|
||||
|
||||
// In case of the dest_buff a3 being already aligned (for example by matrix padding), correct a14 value,
|
||||
// to prevent the destination aligning loop to run 4 times (to prevent aligning already aligned memory)
|
||||
moveqz a14, a13, a13 // If a13 is zero, move a13 to a14, move 0 to a14
|
||||
|
||||
sub a10, a11, a14 // Get the dest_w_bytes after the aligning loop
|
||||
srli a9, a10, 4 // Calculate main loop len (a9 = dest_w_bytes_local / 16)
|
||||
|
||||
// Run dest_buff aligning loop byte by byte
|
||||
loopnez a14, ._dest_aligning_loop
|
||||
l8ui a15, a7, 0 // Load 8 bits from src_buff a7 to a15, offset 0
|
||||
addi.n a7, a7, 1 // Increment src_buff pointer a7 by 1
|
||||
s8i a15, a3, 0 // Save 8 bits from a15 to dest_buff a3, offset 0
|
||||
addi.n a3, a3, 1 // Increment dest_buff pointer a3 by 1
|
||||
._dest_aligning_loop:
|
||||
|
||||
// Destination is aligned, source is unaligned
|
||||
|
||||
// For more information about this implementation, see chapter 3.3.2 Shifts and the Shift Amount Register (SAR)
|
||||
// in Xtensa Instruction Set Architecture (ISA) Reference Manual
|
||||
|
||||
ssa8l a7 // Set SAR_BYTE from src_buff a7 unalignment
|
||||
extui a4, a7, 0, 2 // Get last 2 bits of the src_buff, a4 = src_buff_unalignment
|
||||
sub a7, a7, a4 // "align" the src_buff a7, to 4-byte boundary by decreasing it's pointer to the nearest aligned boundary
|
||||
|
||||
// First preload for the loopnez cycle
|
||||
l32i.n a15, a7, 0 // Load 32 bits from 4-byte aligned src_buff a7 to a15, offset 0
|
||||
|
||||
// Run main loop which copies 16 bytes (5 and 1/3 of RGB888 pixels) in one loop run
|
||||
loopnez a9, ._main_loop_unalign
|
||||
l32i.n a14, a7, 4 // Load 32 bits from 4-byte aligned src_buff a7 to a14, offset 4
|
||||
l32i.n a13, a7, 8 // Load 32 bits from 4-byte aligned src_buff a7 to a13, offset 8
|
||||
src a15, a14, a15 // Concatenate a14 and a15 and shift by SAR_BYTE amount to a15
|
||||
s32i.n a15, a3, 0 // Save 32 bits from shift-corrected a15 to dest_buff a3, offset 0
|
||||
l32i.n a12, a7, 12 // Load 32 bits from 4-byte aligned src_buff a7 to a12, offset 12
|
||||
src a14, a13, a14 // Concatenate a13 and a14 and shift by SAR_BYTE amount to a14
|
||||
s32i.n a14, a3, 4 // Save 32 bits from shift-corrected a14 to dest_buff a3, offset 4
|
||||
l32i.n a15, a7, 16 // Load 32 bits from 4-byte aligned src_buff a7 to a15, offset 16
|
||||
src a13, a12, a13 // Concatenate a12 and a13 and shift by SAR_BYTE amount to a13
|
||||
s32i.n a13, a3, 8 // Save 32 bits from shift-corrected a13 to dest_buff a3, offset 8
|
||||
addi.n a7, a7, 16 // Increment src_buff pointer a7 by 16
|
||||
src a12, a15, a12 // Concatenate a15 and a12 and shift by SAR_BYTE amount to a12
|
||||
s32i.n a12, a3, 12 // Save 32 bits from shift-corrected a12 to dest_buff a3, offset 12
|
||||
addi.n a3, a3, 16 // Increment dest_buff pointer a3 by 16
|
||||
._main_loop_unalign:
|
||||
|
||||
// Finish the remaining bytes out of the loop
|
||||
// Check modulo 8 of the dest_w_bytes_local (a10), if - then copy 8 bytes
|
||||
bbci a10, 3, _mod_8_check // Branch if 3-rd bit of dest_w_bytes_local is clear
|
||||
l32i.n a14, a7, 4 // Load 32 bits from 4-byte aligned src_buff a7 to a14, offset 4
|
||||
l32i.n a13, a7, 8 // Load 32 bits from 4-byte aligned src_buff a7 to a13, offset 8
|
||||
src a15, a14, a15 // Concatenate a14 and a15 and shift by SAR_BYTE amount to a15 (value in a15 is already prepared from previous steps)
|
||||
s32i.n a15, a3, 0 // Save 32 bits from shift-corrected a15 to dest_buff a3, offset 0
|
||||
addi.n a7, a7, 8 // Increment src_buff pointer a7 by 8
|
||||
src a14, a13, a14 // Concatenate a13 and a14 and shift by SAR_BYTE amount to a14
|
||||
s32i.n a14, a3, 4 // Save 32 bits from shift-corrected a14 to dest_buff a3, offset 4
|
||||
addi.n a3, a3, 8 // Increment dest_buff pointer a3 by 8
|
||||
mov a15, a13 // Prepare a15 for the next steps (copy a13 to a15)
|
||||
_mod_8_check:
|
||||
|
||||
// Check modulo 4 of the dest_w_bytes_local (a10), if - then copy 4 bytes
|
||||
bbci a10, 2, _mod_4_check // Branch if 2-nd bit of dest_w_bytes_local is clear
|
||||
l32i.n a14, a7, 4 // Load 32 bits from 4-byte aligned src_buff a7 to a14, offset 4
|
||||
addi.n a7, a7, 4 // Increment src_buff pointer a7 by 4
|
||||
src a15, a14, a15 // Concatenate a14 and a15 and shift by SAR_BYTE amount to a15 (value in a15 is already prepared from previous steps)
|
||||
s32i.n a15, a3, 0 // Save 32 bits from shift-corrected a15 to dest_buff a3, offset 0
|
||||
addi.n a3, a3, 4 // Increment dest_buff pointer a3 by 4
|
||||
mov a15, a14 // Prepare a15 for the next steps (copy a14 to a15)
|
||||
_mod_4_check:
|
||||
|
||||
extui a13, a10, 0, 2 // Get the last 2 bytes of the dest_w_bytes_local (a10), a13 = a10[1:0], to find out how many bytes are needs copied and to increase src and dest pointer accordingly
|
||||
beqz a13, _mod_1_2_check // Branch if a13 equal to zero, E.G. if there are no bytes to be copied
|
||||
l32i.n a14, a7, 4 // Load 32 bits from 4-byte aligned src_buff a7 to a14, offset 4
|
||||
l32i.n a12, a3, 0 // Get dest_buff value: Load 32 bits from 4-byte aligned dest_buff a3 to a12, offset 0
|
||||
src a15, a14, a15 // Concatenate a14 and a15 and shift by SAR_BYTE amount to a15 (value in a15 is already prepared from previous steps)
|
||||
ssa8l a10 // Set SAR_BYTE from dest_w_bytes_local a10 length
|
||||
sll a15, a15 // Shift the dest word a15 by SAR_BYTE amount
|
||||
srl a12, a12 // Shift the src word a12 by SAR_BYTE amount
|
||||
ssa8b a10 // Set SAR_BYTE from dest_w_bytes_local a10 length
|
||||
src a12, a12, a15 // Concatenate a12 and a15 and shift by SAR_BYTE amount to a12
|
||||
s32i.n a12, a3, 0 // Save 32 bits from shift-corrected a12 to dest_buff a3, offset 0
|
||||
add a7, a7, a13 // Increment src_buff pointer a7, by amount of copied bytes (a13)
|
||||
add a3, a3, a13 // Increment dest_buff pointer a3, by amount of copied bytes (a13)
|
||||
_mod_1_2_check:
|
||||
|
||||
add a7, a7, a4 // Correct the src_buff back by src_buff_unalignment (a4), after we have force-aligned it to 4-byte boundary before the main loop
|
||||
add a3, a3, a6 // dest_buff + dest_stride
|
||||
add a7, a7, a8 // src_buff + src_stride
|
||||
addi.n a5, a5, -1 // Decrease the outer loop
|
||||
bnez a5, .outer_loop_unalign
|
||||
|
||||
movi.n a2, 1 // Return LV_RESULT_OK = 1
|
||||
retw.n // Return
|
||||
|
||||
//**********************************************************************************************************************
|
||||
|
||||
// Small matrix width, keep it simple for lengths less than 8 pixels
|
||||
_matrix_width_check: // Matrix width is greater or equal 8 pixels
|
||||
|
||||
// Convert strides to matrix paddings
|
||||
sub a6, a6, a11 // dest_matrix_padding (a6) = dest_stride (a6) - dest_w_bytes (a11)
|
||||
sub a8, a8, a11 // src_matrix_padding (a8) = src_stride (a8) - dest_w_bytes (a11)
|
||||
|
||||
.outer_loop_short_matrix_length:
|
||||
|
||||
// Run main loop which copies 3 bytes (one RGB888 pixel) in one loop run
|
||||
loopnez a4, ._main_loop_short_matrix_length
|
||||
l8ui a15, a7, 0 // Load 8 bits from src_buff a7 to a15, offset 0
|
||||
l8ui a14, a7, 1 // Load 8 bits from src_buff a7 to a14, offset 1
|
||||
l8ui a13, a7, 2 // Load 8 bits from src_buff a7 to a13, offset 2
|
||||
s8i a15, a3, 0 // Save 8 bits from a15 to dest_buff a3, offset 0
|
||||
s8i a14, a3, 1 // Save 8 bits from a14 to dest_buff a3, offset 1
|
||||
s8i a13, a3, 2 // Save 8 bits from a13 to dest_buff a3, offset 2
|
||||
addi.n a7, a7, 3 // Increment src_buff pointer a7 by 3
|
||||
addi.n a3, a3, 3 // Increment dest_buff pointer a3 by 3
|
||||
._main_loop_short_matrix_length:
|
||||
|
||||
add a3, a3, a6 // dest_buff (a3) = dest_buff (a3) + dest_matrix_padding (a6)
|
||||
add a7, a7, a8 // src_buff (a7) = src_buff (a7) + src_matrix_padding (a8)
|
||||
addi.n a5, a5, -1 // Decrease the outer loop
|
||||
bnez a5, .outer_loop_short_matrix_length
|
||||
|
||||
movi.n a2, 1 // Return LV_RESULT_OK = 1
|
||||
retw.n // Return
|
||||
@@ -0,0 +1,221 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "lv_macro_memcpy.S" // Memcpy macros
|
||||
|
||||
// This is LVGL RGB888 image blend to RGB888 for ESP32S3 processor
|
||||
|
||||
.section .text
|
||||
.align 4
|
||||
.global lv_rgb888_blend_normal_to_rgb888_esp
|
||||
.type lv_rgb888_blend_normal_to_rgb888_esp,@function
|
||||
// The function implements the following C code:
|
||||
// void lv_color_blend_to_rgb888(_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_rgb888_blend_normal_to_rgb888_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
|
||||
l32i.n a8, a2, 24 // a8 - src_stride in bytes
|
||||
movi.n a10, 0xf // 0xf alignment mask (16-byte alignment)
|
||||
slli a11, a4, 1 // a11 = (a4 << 1) + a4
|
||||
add a11, a11, a4 // a11 - dest_w_bytes = sizeof(uint24_t) * dest_w
|
||||
|
||||
// No need to convert any colors here, we are copying from rgb888 to rgb888
|
||||
|
||||
// Check dest_w length
|
||||
bltui a4, 8, _matrix_width_check // Branch if dest_w (a4) is lower than 8
|
||||
|
||||
//**********************************************************************************************************************
|
||||
|
||||
// The most general case, can handle all the possible combinations
|
||||
|
||||
// dest_buff (a3) - any alignment
|
||||
// src_buff (a7) - any alignment
|
||||
// dest_stride (a6) - any length
|
||||
// src_stride (a8) - any length
|
||||
// dest_w (a4) - any length
|
||||
|
||||
// Convert strides to matrix paddings
|
||||
sub a6, a6, a11 // dest_matrix_padding (a6) = dest_stride (a6) - dest_w_bytes (a11)
|
||||
sub a8, a8, a11 // src_matrix_padding (a8) = src_stride (a8) - dest_w_bytes (a11)
|
||||
|
||||
.outer_loop_all_unalign:
|
||||
|
||||
// dest_buff alignment check
|
||||
and a13, a10, a3 // Alignment mask 0xf (a10) AND dest_buff pointer
|
||||
beqz a13, _dest_buff_aligned // Branch if a13 = 0 (if dest_buff is aligned)
|
||||
|
||||
movi.n a14, 16 // a14 = 16
|
||||
sub a13, a14, a13 // a13 = 16 - unalignment
|
||||
|
||||
// Check modulo 8 of the unalignment a13, if - then copy 8 bytes (2 and 2/3 of RGB888 pixels)
|
||||
// src_buff a7, dest_buff a3, unalignment a13, copy registers a14, a15
|
||||
macro_memcpy_mod_8 a7, a3, a13, a15, a14, __LINE__
|
||||
|
||||
// Check modulo 4 of the unalignment, if - then copy 4 bytes (1 and 1/3 of RGB888 pixels)
|
||||
// src_buff a7, dest_buff a3, unalignment a13, copy register a15
|
||||
macro_memcpy_mod_4 a7, a3, a13, a15, __LINE__
|
||||
|
||||
// Check modulo 2 of the unalignment, if - then copy 2 bytes (2/3 of RGB888 pixel)
|
||||
// src_buff a7, dest_buff a3, unalignment a13, copy register a15
|
||||
macro_memcpy_mod_2 a7, a3, a13, a15, __LINE__
|
||||
|
||||
// Check modulo 1 of the unalignment, if - then copy 1 byte (1/3 of RGB888 pixel)
|
||||
// src_buff a7, dest_buff a3, unalignment a13, copy register a15
|
||||
macro_memcpy_mod_1 a7, a3, a13, a15, __LINE__
|
||||
|
||||
_dest_buff_aligned:
|
||||
|
||||
// Calculate modulo for non-aligned data
|
||||
sub a11, a11, a13 // a11 = local_dest_w_bytes (a11) = dest_w_bytes (a11) - (16 - unalignment)
|
||||
movi a15, 48 // a15 = 48
|
||||
quou a9, a11, a15 // a9 = local_dest_w_bytes (a11) DIV 48 (a15)
|
||||
remu a12, a11, a15 // a12 = local_dest_w_bytes (a11) remainder after div 48 (a15)
|
||||
|
||||
ee.ld.128.usar.ip q2, a7, 16 // Preload 16 bytes from src_buff a7 to q2, get value of the SAR_BYTE, increase src_buf pointer a7 by 16
|
||||
ee.ld.128.usar.ip q3, a7, 16 // Preload 16 bytes from src_buff a7 to q3, get value of the SAR_BYTE, increase src_buf pointer a7 by 16
|
||||
|
||||
// Run main loop which copies 48 bytes (16 RGB888 pixels) in one loop run
|
||||
loopnez a9, ._main_loop_all_unalign // 48 bytes (16 RGB888 pixels) in one loop
|
||||
ee.src.q.ld.ip q4, a7, 16, q2, q3 // Load 16 bytes from src_buff a7 to q4, concatenate q2 and q3 and shift to q2 by the SAR_BYTE amount, increase src_buf pointer a7 by 16
|
||||
ee.vst.128.ip q2, a3, 16 // Store 16 bytes from q2 to aligned dest_buff a3, increase dest_buff pointer a3 by 16
|
||||
ee.src.q.ld.ip q2, a7, 16, q3, q4 // Load 16 bytes from src_buff a7 to q2, concatenate q3 and q4 and shift to q3 by the SAR_BYTE amount, increase src_buf pointer a7 by 16
|
||||
ee.vst.128.ip q3, a3, 16 // Store 16 bytes from q3 to aligned dest_buff a3, increase dest_buff pointer a3 by 16
|
||||
ee.src.q.ld.ip q3, a7, 16, q4, q2 // Load 16 bytes from src_buff a7 to q3, concatenate q4 and q2 and shift to q4 by the SAR_BYTE amount, increase src_buf pointer a7 by 16
|
||||
ee.vst.128.ip q4, a3, 16 // Store 16 bytes from q4 to aligned dest_buff a3, increase dest_buff pointer a3 by 16
|
||||
._main_loop_all_unalign:
|
||||
|
||||
// Finish the main loop outside of the loop from Q registers preloads
|
||||
|
||||
// Check modulo 32 and modulo 8 of the loop_len_remainder a12
|
||||
bbci a12, 5, _all_unalign_mod_32_check // Branch if 5-th bit of loop_len_remainder a12 is clear
|
||||
bbsi a12, 3, _all_unalign_mod_32_mod_8_check // Branch if 3-rd bif of loop_len_remainder a12 is set
|
||||
|
||||
// Copy 32 bytes (10 and 2/3 of RGB888 pixels)
|
||||
ee.src.q.ld.ip q4, a7, 0, q2, q3 // Load 16 bytes from src_buff a7 to q4, concatenate q2 and q3 and shift to q2 by the SAR_BYTE amount, don't increase src_buf pointer a7
|
||||
ee.vst.128.ip q2, a3, 16 // Store 16 bytes from q2 to aligned dest_buff a3, increase dest_buff pointer a3 by 16
|
||||
ee.src.q q3, q3, q4 // Concatenate q3 and q4 and shift to q3 by the SAR_BYTE amount
|
||||
ee.vst.128.ip q3, a3, 16 // Store 16 bytes from q3 to aligned dest_buff a3, increase dest_buff pointer a3 by 16
|
||||
j _skip_mod16
|
||||
|
||||
_all_unalign_mod_32_mod_8_check:
|
||||
// Copy 40 bytes (13 and 1/3 of RGB888 pixels)
|
||||
ee.src.q.ld.ip q4, a7, 16, q2, q3 // Load 16 bytes from src_buff a7 to q4, concatenate q2 and q3 and shift to q2 by the SAR_BYTE amount, increase src_buf pointer a7 by 16
|
||||
ee.vst.128.ip q2, a3, 16 // Store 16 bytes from q2 to aligned dest_buff a3, increase dest_buff pointer a3 by 16
|
||||
ee.src.q.ld.ip q2, a7, 0, q3, q4 // Load 16 bytes from src_buff a7 to q2, concatenate q3 and q4 and shift to q3 by the SAR_BYTE amount, don't increase src_buf pointer a7
|
||||
ee.vst.128.ip q3, a3, 16 // Store 16 bytes from q3 to aligned dest_buff a3, increase dest_buff pointer a3 by 16
|
||||
ee.src.q q4, q4, q2 // Concatenate q4 and q2 and shift to q4 by the SAR_BYTE amount
|
||||
ee.vst.l.64.ip q4, a3, 8 // Store lower 8 bytes from q4 to aligned dest_buff a3, increase dest_buff pointer a3 by 8
|
||||
addi a7, a7, -8 // Correct the src_buff pointer a7, caused by q reg preload
|
||||
j _skip_mod16
|
||||
|
||||
_all_unalign_mod_32_check:
|
||||
|
||||
// Check modulo 16 and modulo 8 of the loop_len_remainder a12
|
||||
bbci a12, 4, _all_unalign_mod_16_check // branch if 4-th bit of loop_len_remainder a12 is clear
|
||||
bbsi a12, 3, _all_unalign_mod_16_mod_8_check // branch if 3-rd bit of loop_len_remainder a12 is set
|
||||
|
||||
// Copy 16 bytes (5 and 1/3 of RGB888 pixels)
|
||||
ee.src.q q2, q2, q3 // Concatenate q2 and q3 and shift to q2 by the SAR_BYTE amount
|
||||
ee.vst.128.ip q2, a3, 16 // Store 16 bytes from q2 to aligned dest_buff a3, increase dest_buff pointer a3 by 16
|
||||
addi a7, a7, -16 // Correct the src_buff pointer a7, caused by q reg preload
|
||||
j _skip_mod16
|
||||
|
||||
_all_unalign_mod_16_mod_8_check:
|
||||
// Copy 24 bytes (8 RGB888 pixels)
|
||||
ee.src.q.ld.ip q4, a7, 0, q2, q3 // Load 16 bytes from src_buff a7 to q4, concatenate q2 and q3 and shift to q2 by the SAR_BYTE amount, don't increase src_buf pointer a7
|
||||
ee.vst.128.ip q2, a3, 16 // Store 16 bytes from q2 to aligned dest_buff a3, increase dest_buff pointer a3 by 16
|
||||
ee.src.q q3, q3, q4 // Concatenate q3 and q4 and shift to q3 by the SAR_BYTE amount
|
||||
ee.vst.l.64.ip q3, a3, 8 // Store lower 8 bytes from q3 to aligned dest_buff a3, increase dest_buff pointer a3 by 8
|
||||
addi a7, a7, -8 // Correct the src_buff pointer a7, caused by q reg preload
|
||||
j _skip_mod16
|
||||
_all_unalign_mod_16_check:
|
||||
|
||||
bbci a12, 3, _all_unalign_mod_8_check // Branch if 3-rd bit of loop_len_remainder a12 is clear
|
||||
// Copy 8 bytes (2 and 2/3 of RGB888 pixels)
|
||||
ee.src.q q2, q2, q3 // Concatenate q2 and q3 and shift to q2 by the SAR_BYTE amount
|
||||
ee.vst.l.64.ip q2, a3, 8 // Store lower 8 bytes from q2 to aligned dest_buff a3, increase dest_buff pointer a3 by 8
|
||||
addi a7, a7, -24 // Correct the src_buff pointer a7, caused by q reg preload
|
||||
j _skip_mod16
|
||||
_all_unalign_mod_8_check:
|
||||
|
||||
addi a7, a7, -32 // Correct the src_buff pointer a7, caused by q reg preload
|
||||
|
||||
_skip_mod16:
|
||||
|
||||
// Check modulo 4 of the loop_len_remainder, if - then copy 4 bytes (1 and 1/3 of RGB888 pixels)
|
||||
// src_buff a7, dest_buff a3, loop_len_remainder a12, copy register a15
|
||||
macro_memcpy_mod_4 a7, a3, a12, a15, __LINE__
|
||||
|
||||
// Check modulo 2 of the loop_len_remainder, if - then copy 2 bytes (2/3 of RGB888 pixel)
|
||||
// src_buff a7, dest_buff a3, loop_len_remainder a12, copy register a15
|
||||
macro_memcpy_mod_2 a7, a3, a12, a15, __LINE__
|
||||
|
||||
// Check modulo 1 of the loop_len_remainder, if - then copy 1 byte (1/3 of RGB888 pixel)
|
||||
// src_buff a7, dest_buff a3, loop_len_remainder a12, copy register a15
|
||||
macro_memcpy_mod_1 a7, a3, a12, a15, __LINE_
|
||||
|
||||
slli a11, a4, 1 // Refresh dest_w_bytes
|
||||
add a11, a11, a4
|
||||
add a3, a3, a6 // dest_buff (a3) = dest_buff (a3) + dest_matrix_padding (a6)
|
||||
add a7, a7, a8 // src_buff (a7) = src_buff (a7) + src_matrix_padding (a8)
|
||||
addi.n a5, a5, -1 // Decrease the outer loop
|
||||
bnez a5, .outer_loop_all_unalign
|
||||
|
||||
movi.n a2, 1 // Return LV_RESULT_OK = 1
|
||||
retw.n // Return
|
||||
|
||||
//**********************************************************************************************************************
|
||||
|
||||
// Small matrix width, keep it simple for lengths less than 8 pixels
|
||||
_matrix_width_check: // Matrix width is greater or equal 8 pixels
|
||||
|
||||
// Convert strides to matrix paddings
|
||||
sub a6, a6, a11 // dest_matrix_padding (a6) = dest_stride (a6) - dest_w_bytes (a11)
|
||||
sub a8, a8, a11 // src_matrix_padding (a8) = src_stride (a8) - dest_w_bytes (a11)
|
||||
|
||||
.outer_loop_short_matrix_length:
|
||||
|
||||
// Run main loop which copies 3 bytes (one RGB888 pixel) in one loop run
|
||||
loopnez a4, ._main_loop_short_matrix_length
|
||||
l8ui a15, a7, 0 // Load 8 bits from src_buff a7 to a15, offset 0
|
||||
l8ui a14, a7, 1 // Load 8 bits from src_buff a7 to a14, offset 1
|
||||
l8ui a13, a7, 2 // Load 8 bits from src_buff a7 to a13, offset 2
|
||||
s8i a15, a3, 0 // Save 8 bits from a15 to dest_buff a3, offset 0
|
||||
s8i a14, a3, 1 // Save 8 bits from a14 to dest_buff a3, offset 1
|
||||
s8i a13, a3, 2 // Save 8 bits from a13 to dest_buff a3, offset 2
|
||||
addi.n a7, a7, 3 // Increment src_buff pointer a7 by 3
|
||||
addi.n a3, a3, 3 // Increment dest_buff pointer a3 by 3
|
||||
._main_loop_short_matrix_length:
|
||||
|
||||
add a3, a3, a6 // dest_buff (a3) = dest_buff (a3) + dest_matrix_padding (a6)
|
||||
add a7, a7, a8 // src_buff (a7) = src_buff (a7) + src_matrix_padding (a8)
|
||||
addi.n a5, a5, -1 // Decrease the outer loop
|
||||
bnez a5, .outer_loop_short_matrix_length
|
||||
|
||||
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: "../../../"
|
||||
@@ -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
|
||||
@@ -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)
|
||||
@@ -0,0 +1,135 @@
|
||||
# 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 for LV Fill functions (memset)
|
||||
|
||||
| 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 |
|
||||
| RGB888 | 128x128 | 16 byte | 0.608 | 4.062 |
|
||||
| | 127x127 | 1 byte | 0.818 | 3.969 |
|
||||
* 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
|
||||
|
||||
## Benchmark results for LV Image functions (memcpy)
|
||||
|
||||
| Color format | Matrix size | Memory alignment | ASM version | ANSI C version |
|
||||
| :----------- | :---------- | :--------------- | :------------- | :------------- |
|
||||
| RGB565 | 128x128 | 16 byte | 0.352 | 3.437 |
|
||||
| | 127x128 | 1 byte | 0.866 | 5.978 |
|
||||
| RGB888 | 128x128 | 16 byte | 0.744 | 4.002 |
|
||||
| | 127x128 | 1 byte | 1.002 | 7.998 |
|
||||
* this data was obtained by running [benchmark tests](#benchmark-test) on 128x128 16 byte aligned matrix (ideal case) and 127x128 1 byte aligned matrix (worst case)
|
||||
* the values represent cycles per sample to perform memory copy between two matrices 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]
|
||||
(5) "LV Image functionality RGB565 blend to RGB565" [image][functionality][RGB565]
|
||||
(6) "LV Image benchmark RGB565 blend to RGB565" [image][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 |
|
||||
@@ -0,0 +1,31 @@
|
||||
# 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()
|
||||
|
||||
file(GLOB_RECURSE ASM_MACROS ${PORT_PATH}/simd/lv_macro_*.S) # Explicitly add all assembler macro files
|
||||
|
||||
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" # memset tests
|
||||
"test_lv_fill_benchmark.c"
|
||||
"test_lv_image_functionality.c" # memcpy tests
|
||||
"test_lv_image_benchmark.c"
|
||||
${BLEND_SRCS} # Hard copy of LVGL's blend API, to simplify testing
|
||||
${ASM_SOURCES} # Assembly src files
|
||||
${ASM_MACROS} # Assembly macro files
|
||||
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,75 @@
|
||||
/*
|
||||
* 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;
|
||||
bool use_asm;
|
||||
} _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,53 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 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_rgb888.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_DRAW_SW_BLEND_RGB888_H
|
||||
#define LV_DRAW_SW_BLEND_RGB888_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_rgb888(_lv_draw_sw_blend_fill_dsc_t *dsc,
|
||||
uint32_t dest_px_size);
|
||||
|
||||
void /* LV_ATTRIBUTE_FAST_MEM */ lv_draw_sw_blend_image_to_rgb888(_lv_draw_sw_blend_image_dsc_t *dsc,
|
||||
uint32_t dest_px_size);
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_DRAW_SW_BLEND_RGB888_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,79 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 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_string.h
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef LV_STRING_H
|
||||
#define LV_STRING_H
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
//#include "../lv_conf_internal.h"
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include "lv_types.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* GLOBAL PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**
|
||||
* @brief Copies a block of memory from a source address to a destination address.
|
||||
* @param dst Pointer to the destination array where the content is to be copied.
|
||||
* @param src Pointer to the source of data to be copied.
|
||||
* @param len Number of bytes to copy.
|
||||
* @return Pointer to the destination array.
|
||||
* @note The function does not check for any overlapping of the source and destination memory blocks.
|
||||
*/
|
||||
void *lv_memcpy(void *dst, const void *src, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Fills a block of memory with a specified value.
|
||||
* @param dst Pointer to the destination array to fill with the specified value.
|
||||
* @param v Value to be set. The value is passed as an int, but the function fills
|
||||
* the block of memory using the unsigned char conversion of this value.
|
||||
* @param len Number of bytes to be set to the value.
|
||||
*/
|
||||
void lv_memset(void *dst, uint8_t v, size_t len);
|
||||
|
||||
/**
|
||||
* @brief Move a block of memory from source to destination
|
||||
* @param dst Pointer to the destination array where the content is to be copied.
|
||||
* @param src Pointer to the source of data to be copied.
|
||||
* @param len Number of bytes to copy
|
||||
* @return Pointer to the destination array.
|
||||
*/
|
||||
void *lv_memmove(void *dst, const void *src, size_t len);
|
||||
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
|
||||
#endif /*LV_STRING_H*/
|
||||
@@ -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,53 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
/**********************
|
||||
* 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 "lv_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++) {
|
||||
lv_memcpy(dest_buf_c32, src_buf, line_in_bytes);
|
||||
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)
|
||||
{
|
||||
lv_memset(&cache->fg_saved, 0x00, sizeof(lv_color32_t)); //lv_memzero
|
||||
lv_memset(&cache->bg_saved, 0x00, sizeof(lv_color32_t)); //lv_memzero
|
||||
lv_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,962 @@
|
||||
/*
|
||||
* 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 "lv_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 (dsc->use_asm) {
|
||||
LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_RGB565(dsc);
|
||||
} else {
|
||||
uint32_t line_in_bytes = w * 2;
|
||||
for (y = 0; y < h; y++) {
|
||||
lv_memcpy(dest_buf_u16, src_buf_u16, line_in_bytes);
|
||||
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);
|
||||
}
|
||||
@@ -0,0 +1,954 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 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_rgb888.c
|
||||
*
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
#include "lv_draw_sw_blend_to_rgb888.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 "lv_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, uint32_t dest_px_size);
|
||||
|
||||
static void /* LV_ATTRIBUTE_FAST_MEM */ i1_image_blend(_lv_draw_sw_blend_image_dsc_t *dsc, uint32_t dest_px_size);
|
||||
|
||||
static inline uint8_t /* LV_ATTRIBUTE_FAST_MEM */ get_bit(const uint8_t *buf, int32_t bit_idx);
|
||||
|
||||
static void /* LV_ATTRIBUTE_FAST_MEM */ l8_image_blend(_lv_draw_sw_blend_image_dsc_t *dsc, uint32_t dest_px_size);
|
||||
|
||||
static void /* LV_ATTRIBUTE_FAST_MEM */ rgb565_image_blend(_lv_draw_sw_blend_image_dsc_t *dsc, uint32_t dest_px_size);
|
||||
|
||||
static void /* LV_ATTRIBUTE_FAST_MEM */ rgb888_image_blend(_lv_draw_sw_blend_image_dsc_t *dsc,
|
||||
const uint8_t dest_px_size,
|
||||
uint32_t src_px_size);
|
||||
|
||||
static void /* LV_ATTRIBUTE_FAST_MEM */ argb8888_image_blend(_lv_draw_sw_blend_image_dsc_t *dsc,
|
||||
uint32_t dest_px_size);
|
||||
|
||||
static inline void /* LV_ATTRIBUTE_FAST_MEM */ lv_color_8_24_mix(const uint8_t src, uint8_t *dest, uint8_t mix);
|
||||
|
||||
static inline void /* LV_ATTRIBUTE_FAST_MEM */ lv_color_24_24_mix(const uint8_t *src, uint8_t *dest, uint8_t mix);
|
||||
|
||||
static inline void /* LV_ATTRIBUTE_FAST_MEM */ blend_non_normal_pixel(uint8_t *dest, lv_color32_t src,
|
||||
lv_blend_mode_t mode);
|
||||
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_RGB888
|
||||
#define LV_DRAW_SW_COLOR_BLEND_TO_RGB888(...) LV_RESULT_INVALID
|
||||
#endif
|
||||
|
||||
#ifndef LV_DRAW_SW_COLOR_BLEND_TO_RGB888_WITH_OPA
|
||||
#define LV_DRAW_SW_COLOR_BLEND_TO_RGB888_WITH_OPA(...) LV_RESULT_INVALID
|
||||
#endif
|
||||
|
||||
#ifndef LV_DRAW_SW_COLOR_BLEND_TO_RGB888_WITH_MASK
|
||||
#define LV_DRAW_SW_COLOR_BLEND_TO_RGB888_WITH_MASK(...) LV_RESULT_INVALID
|
||||
#endif
|
||||
|
||||
#ifndef LV_DRAW_SW_COLOR_BLEND_TO_RGB888_MIX_MASK_OPA
|
||||
#define LV_DRAW_SW_COLOR_BLEND_TO_RGB888_MIX_MASK_OPA(...) LV_RESULT_INVALID
|
||||
#endif
|
||||
|
||||
#ifndef LV_DRAW_SW_L8_BLEND_NORMAL_TO_RGB888
|
||||
#define LV_DRAW_SW_L8_BLEND_NORMAL_TO_RGB888(...) LV_RESULT_INVALID
|
||||
#endif
|
||||
|
||||
#ifndef LV_DRAW_SW_L8_BLEND_NORMAL_TO_RGB888_WITH_OPA
|
||||
#define LV_DRAW_SW_L8_BLEND_NORMAL_TO_RGB888_WITH_OPA(...) LV_RESULT_INVALID
|
||||
#endif
|
||||
|
||||
#ifndef LV_DRAW_SW_L8_BLEND_NORMAL_TO_RGB888_WITH_MASK
|
||||
#define LV_DRAW_SW_L8_BLEND_NORMAL_TO_RGB888_WITH_MASK(...) LV_RESULT_INVALID
|
||||
#endif
|
||||
|
||||
#ifndef LV_DRAW_SW_L8_BLEND_NORMAL_TO_RGB888_MIX_MASK_OPA
|
||||
#define LV_DRAW_SW_L8_BLEND_NORMAL_TO_RGB888_MIX_MASK_OPA(...) LV_RESULT_INVALID
|
||||
#endif
|
||||
|
||||
#ifndef LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_RGB888
|
||||
#define LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_RGB888(...) LV_RESULT_INVALID
|
||||
#endif
|
||||
|
||||
#ifndef LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_RGB888_WITH_OPA
|
||||
#define LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_RGB888_WITH_OPA(...) LV_RESULT_INVALID
|
||||
#endif
|
||||
|
||||
#ifndef LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_RGB888_WITH_MASK
|
||||
#define LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_RGB888_WITH_MASK(...) LV_RESULT_INVALID
|
||||
#endif
|
||||
|
||||
#ifndef LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_RGB888_MIX_MASK_OPA
|
||||
#define LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_RGB888_MIX_MASK_OPA(...) LV_RESULT_INVALID
|
||||
#endif
|
||||
|
||||
#ifndef LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_RGB888
|
||||
#define LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_RGB888(...) LV_RESULT_INVALID
|
||||
#endif
|
||||
|
||||
#ifndef LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_RGB888_WITH_OPA
|
||||
#define LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_RGB888_WITH_OPA(...) LV_RESULT_INVALID
|
||||
#endif
|
||||
|
||||
#ifndef LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_RGB888_WITH_MASK
|
||||
#define LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_RGB888_WITH_MASK(...) LV_RESULT_INVALID
|
||||
#endif
|
||||
|
||||
#ifndef LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_RGB888_MIX_MASK_OPA
|
||||
#define LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_RGB888_MIX_MASK_OPA(...) LV_RESULT_INVALID
|
||||
#endif
|
||||
|
||||
#ifndef LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_RGB888
|
||||
#define LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_RGB888(...) LV_RESULT_INVALID
|
||||
#endif
|
||||
|
||||
#ifndef LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_RGB888_WITH_OPA
|
||||
#define LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_RGB888_WITH_OPA(...) LV_RESULT_INVALID
|
||||
#endif
|
||||
|
||||
#ifndef LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_RGB888_WITH_MASK
|
||||
#define LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_RGB888_WITH_MASK(...) LV_RESULT_INVALID
|
||||
#endif
|
||||
|
||||
#ifndef LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_RGB888_MIX_MASK_OPA
|
||||
#define LV_DRAW_SW_ARGB8888_BLEND_NORMAL_TO_RGB888_MIX_MASK_OPA(...) LV_RESULT_INVALID
|
||||
#endif
|
||||
|
||||
#ifndef LV_DRAW_SW_I1_BLEND_NORMAL_TO_888
|
||||
#define LV_DRAW_SW_I1_BLEND_NORMAL_TO_888(...) LV_RESULT_INVALID
|
||||
#endif
|
||||
|
||||
#ifndef LV_DRAW_SW_I1_BLEND_NORMAL_TO_888_WITH_OPA
|
||||
#define LV_DRAW_SW_I1_BLEND_NORMAL_TO_888_WITH_OPA(...) LV_RESULT_INVALID
|
||||
#endif
|
||||
|
||||
#ifndef LV_DRAW_SW_I1_BLEND_NORMAL_TO_888_WITH_MASK
|
||||
#define LV_DRAW_SW_I1_BLEND_NORMAL_TO_888_WITH_MASK(...) LV_RESULT_INVALID
|
||||
#endif
|
||||
|
||||
#ifndef LV_DRAW_SW_I1_BLEND_NORMAL_TO_888_MIX_MASK_OPA
|
||||
#define LV_DRAW_SW_I1_BLEND_NORMAL_TO_888_MIX_MASK_OPA(...) LV_RESULT_INVALID
|
||||
#endif
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void LV_ATTRIBUTE_FAST_MEM lv_draw_sw_blend_color_to_rgb888(_lv_draw_sw_blend_fill_dsc_t *dsc, uint32_t dest_px_size)
|
||||
{
|
||||
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;
|
||||
|
||||
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 && dest_px_size == 3) {
|
||||
LV_DRAW_SW_COLOR_BLEND_TO_RGB888(dsc, dest_px_size);
|
||||
} else {
|
||||
if (dest_px_size == 3) {
|
||||
uint8_t *dest_buf_u8 = dsc->dest_buf;
|
||||
uint8_t *dest_buf_ori = dsc->dest_buf;
|
||||
w *= dest_px_size;
|
||||
|
||||
for (x = 0; x < w; x += 3) {
|
||||
dest_buf_u8[x + 0] = dsc->color.blue;
|
||||
dest_buf_u8[x + 1] = dsc->color.green;
|
||||
dest_buf_u8[x + 2] = dsc->color.red;
|
||||
}
|
||||
|
||||
dest_buf_u8 += dest_stride;
|
||||
|
||||
for (y = 1; y < h; y++) {
|
||||
lv_memcpy(dest_buf_u8, dest_buf_ori, w);
|
||||
dest_buf_u8 += dest_stride;
|
||||
}
|
||||
}
|
||||
if (dest_px_size == 4) {
|
||||
uint32_t color32 = lv_color_to_u32(dsc->color);
|
||||
uint32_t *dest_buf_u32 = dsc->dest_buf;
|
||||
for (y = 0; y < h; y++) {
|
||||
for (x = 0; x <= w - 16; x += 16) {
|
||||
dest_buf_u32[x + 0] = color32;
|
||||
dest_buf_u32[x + 1] = color32;
|
||||
dest_buf_u32[x + 2] = color32;
|
||||
dest_buf_u32[x + 3] = color32;
|
||||
|
||||
dest_buf_u32[x + 4] = color32;
|
||||
dest_buf_u32[x + 5] = color32;
|
||||
dest_buf_u32[x + 6] = color32;
|
||||
dest_buf_u32[x + 7] = color32;
|
||||
|
||||
dest_buf_u32[x + 8] = color32;
|
||||
dest_buf_u32[x + 9] = color32;
|
||||
dest_buf_u32[x + 10] = color32;
|
||||
dest_buf_u32[x + 11] = color32;
|
||||
|
||||
dest_buf_u32[x + 12] = color32;
|
||||
dest_buf_u32[x + 13] = color32;
|
||||
dest_buf_u32[x + 14] = color32;
|
||||
dest_buf_u32[x + 15] = color32;
|
||||
}
|
||||
for (; x < w; x ++) {
|
||||
dest_buf_u32[x] = color32;
|
||||
}
|
||||
|
||||
dest_buf_u32 = drawbuf_next_row(dest_buf_u32, dest_stride);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/*Opacity only*/
|
||||
else if (mask == NULL && opa < LV_OPA_MAX) {
|
||||
if (LV_RESULT_INVALID == LV_DRAW_SW_COLOR_BLEND_TO_RGB888_WITH_OPA(dsc, dest_px_size)) {
|
||||
uint32_t color32 = lv_color_to_u32(dsc->color);
|
||||
uint8_t *dest_buf = dsc->dest_buf;
|
||||
w *= dest_px_size;
|
||||
for (y = 0; y < h; y++) {
|
||||
for (x = 0; x < w; x += dest_px_size) {
|
||||
lv_color_24_24_mix((const uint8_t *)&color32, &dest_buf[x], opa);
|
||||
}
|
||||
|
||||
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_RGB888_WITH_MASK(dsc, dest_px_size)) {
|
||||
uint32_t color32 = lv_color_to_u32(dsc->color);
|
||||
uint8_t *dest_buf = dsc->dest_buf;
|
||||
w *= dest_px_size;
|
||||
|
||||
for (y = 0; y < h; y++) {
|
||||
uint32_t mask_x;
|
||||
for (x = 0, mask_x = 0; x < w; x += dest_px_size, mask_x++) {
|
||||
lv_color_24_24_mix((const uint8_t *)&color32, &dest_buf[x], mask[mask_x]);
|
||||
}
|
||||
dest_buf += dest_stride;
|
||||
mask += mask_stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
/*Masked with opacity*/
|
||||
else {
|
||||
if (LV_RESULT_INVALID == LV_DRAW_SW_COLOR_BLEND_TO_RGB888_MIX_MASK_OPA(dsc, dest_px_size)) {
|
||||
uint32_t color32 = lv_color_to_u32(dsc->color);
|
||||
uint8_t *dest_buf = dsc->dest_buf;
|
||||
w *= dest_px_size;
|
||||
|
||||
for (y = 0; y < h; y++) {
|
||||
uint32_t mask_x;
|
||||
for (x = 0, mask_x = 0; x < w; x += dest_px_size, mask_x++) {
|
||||
lv_color_24_24_mix((const uint8_t *) &color32, &dest_buf[x], LV_OPA_MIX2(opa, mask[mask_x]));
|
||||
}
|
||||
dest_buf += dest_stride;
|
||||
mask += mask_stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void LV_ATTRIBUTE_FAST_MEM lv_draw_sw_blend_image_to_rgb888(_lv_draw_sw_blend_image_dsc_t *dsc, uint32_t dest_px_size)
|
||||
{
|
||||
|
||||
switch (dsc->src_color_format) {
|
||||
case LV_COLOR_FORMAT_RGB565:
|
||||
rgb565_image_blend(dsc, dest_px_size);
|
||||
break;
|
||||
case LV_COLOR_FORMAT_RGB888:
|
||||
rgb888_image_blend(dsc, dest_px_size, 3);
|
||||
break;
|
||||
case LV_COLOR_FORMAT_XRGB8888:
|
||||
rgb888_image_blend(dsc, dest_px_size, 4);
|
||||
break;
|
||||
case LV_COLOR_FORMAT_ARGB8888:
|
||||
argb8888_image_blend(dsc, dest_px_size);
|
||||
break;
|
||||
case LV_COLOR_FORMAT_L8:
|
||||
l8_image_blend(dsc, dest_px_size);
|
||||
break;
|
||||
case LV_COLOR_FORMAT_AL88:
|
||||
al88_image_blend(dsc, dest_px_size);
|
||||
break;
|
||||
case LV_COLOR_FORMAT_I1:
|
||||
i1_image_blend(dsc, dest_px_size);
|
||||
break;
|
||||
default:
|
||||
LV_LOG_WARN("Not supported source color format");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
static void LV_ATTRIBUTE_FAST_MEM i1_image_blend(_lv_draw_sw_blend_image_dsc_t *dsc, uint32_t dest_px_size)
|
||||
{
|
||||
int32_t w = dsc->dest_w;
|
||||
int32_t h = dsc->dest_h;
|
||||
lv_opa_t opa = dsc->opa;
|
||||
uint8_t *dest_buf_u8 = dsc->dest_buf;
|
||||
int32_t dest_stride = dsc->dest_stride;
|
||||
const uint8_t *src_buf_i1 = 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_I1_BLEND_NORMAL_TO_888(dsc)) {
|
||||
for (y = 0; y < h; y++) {
|
||||
for (dest_x = 0, src_x = 0; src_x < w; dest_x += dest_px_size, src_x++) {
|
||||
uint8_t chan_val = get_bit(src_buf_i1, src_x) * 255;
|
||||
dest_buf_u8[dest_x + 2] = chan_val;
|
||||
dest_buf_u8[dest_x + 1] = chan_val;
|
||||
dest_buf_u8[dest_x + 0] = chan_val;
|
||||
}
|
||||
dest_buf_u8 = drawbuf_next_row(dest_buf_u8, dest_stride);
|
||||
src_buf_i1 = drawbuf_next_row(src_buf_i1, src_stride);
|
||||
}
|
||||
}
|
||||
} else if (mask_buf == NULL && opa < LV_OPA_MAX) {
|
||||
if (LV_RESULT_INVALID == LV_DRAW_SW_I1_BLEND_NORMAL_TO_888_WITH_OPA(dsc)) {
|
||||
for (y = 0; y < h; y++) {
|
||||
for (dest_x = 0, src_x = 0; src_x < w; dest_x += dest_px_size, src_x++) {
|
||||
uint8_t chan_val = get_bit(src_buf_i1, src_x) * 255;
|
||||
lv_color_8_24_mix(chan_val, &dest_buf_u8[dest_x], opa);
|
||||
}
|
||||
dest_buf_u8 = drawbuf_next_row(dest_buf_u8, dest_stride);
|
||||
src_buf_i1 = drawbuf_next_row(src_buf_i1, src_stride);
|
||||
}
|
||||
}
|
||||
} else if (mask_buf && opa >= LV_OPA_MAX) {
|
||||
if (LV_RESULT_INVALID == LV_DRAW_SW_I1_BLEND_NORMAL_TO_888_WITH_MASK(dsc)) {
|
||||
for (y = 0; y < h; y++) {
|
||||
for (dest_x = 0, src_x = 0; src_x < w; dest_x += dest_px_size, src_x++) {
|
||||
uint8_t chan_val = get_bit(src_buf_i1, src_x) * 255;
|
||||
lv_color_8_24_mix(chan_val, &dest_buf_u8[dest_x], mask_buf[src_x]);
|
||||
}
|
||||
dest_buf_u8 = drawbuf_next_row(dest_buf_u8, dest_stride);
|
||||
src_buf_i1 = drawbuf_next_row(src_buf_i1, src_stride);
|
||||
mask_buf += mask_stride;
|
||||
}
|
||||
}
|
||||
} else if (mask_buf && opa < LV_OPA_MAX) {
|
||||
if (LV_RESULT_INVALID == LV_DRAW_SW_I1_BLEND_NORMAL_TO_888_MIX_MASK_OPA(dsc)) {
|
||||
for (y = 0; y < h; y++) {
|
||||
for (dest_x = 0, src_x = 0; src_x < w; dest_x += dest_px_size, src_x++) {
|
||||
uint8_t chan_val = get_bit(src_buf_i1, src_x) * 255;
|
||||
lv_color_8_24_mix(chan_val, &dest_buf_u8[dest_x], LV_OPA_MIX2(opa, mask_buf[src_x]));
|
||||
}
|
||||
dest_buf_u8 = drawbuf_next_row(dest_buf_u8, dest_stride);
|
||||
src_buf_i1 = drawbuf_next_row(src_buf_i1, src_stride);
|
||||
mask_buf += mask_stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (y = 0; y < h; y++) {
|
||||
for (dest_x = 0, src_x = 0; src_x < w; dest_x += dest_px_size, src_x++) {
|
||||
lv_color32_t src_argb;
|
||||
src_argb.red = get_bit(src_buf_i1, src_x) * 255;
|
||||
src_argb.green = src_argb.red;
|
||||
src_argb.blue = src_argb.red;
|
||||
if (mask_buf == NULL) {
|
||||
src_argb.alpha = opa;
|
||||
} else {
|
||||
src_argb.alpha = LV_OPA_MIX2(mask_buf[src_x], opa);
|
||||
}
|
||||
blend_non_normal_pixel(&dest_buf_u8[dest_x], src_argb, dsc->blend_mode);
|
||||
}
|
||||
if (mask_buf) {
|
||||
mask_buf += mask_stride;
|
||||
}
|
||||
dest_buf_u8 = drawbuf_next_row(dest_buf_u8, dest_stride);
|
||||
src_buf_i1 = drawbuf_next_row(src_buf_i1, src_stride);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void LV_ATTRIBUTE_FAST_MEM al88_image_blend(_lv_draw_sw_blend_image_dsc_t *dsc, uint32_t dest_px_size)
|
||||
{
|
||||
int32_t w = dsc->dest_w;
|
||||
int32_t h = dsc->dest_h;
|
||||
lv_opa_t opa = dsc->opa;
|
||||
uint8_t *dest_buf_u8 = 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_L8_BLEND_NORMAL_TO_RGB888(dsc, dest_px_size)) {
|
||||
for (y = 0; y < h; y++) {
|
||||
for (dest_x = 0, src_x = 0; src_x < w; dest_x += dest_px_size, src_x++) {
|
||||
lv_color_8_24_mix(src_buf_al88[src_x].lumi, &dest_buf_u8[dest_x], src_buf_al88[src_x].alpha);
|
||||
}
|
||||
dest_buf_u8 += 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_L8_BLEND_NORMAL_TO_RGB888_WITH_OPA(dsc, dest_px_size)) {
|
||||
for (y = 0; y < h; y++) {
|
||||
for (dest_x = 0, src_x = 0; src_x < w; dest_x += dest_px_size, src_x++) {
|
||||
lv_color_8_24_mix(src_buf_al88[src_x].lumi, &dest_buf_u8[dest_x], LV_OPA_MIX2(src_buf_al88[src_x].alpha, opa));
|
||||
}
|
||||
dest_buf_u8 += 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_L8_BLEND_NORMAL_TO_RGB888_WITH_MASK(dsc, dest_px_size)) {
|
||||
for (y = 0; y < h; y++) {
|
||||
for (dest_x = 0, src_x = 0; src_x < w; dest_x += dest_px_size, src_x++) {
|
||||
lv_color_8_24_mix(src_buf_al88[src_x].lumi, &dest_buf_u8[dest_x], LV_OPA_MIX2(src_buf_al88[src_x].alpha,
|
||||
mask_buf[src_x]));
|
||||
}
|
||||
dest_buf_u8 += 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_L8_BLEND_NORMAL_TO_RGB888_MIX_MASK_OPA(dsc, dest_px_size)) {
|
||||
for (y = 0; y < h; y++) {
|
||||
for (dest_x = 0, src_x = 0; src_x < w; dest_x += dest_px_size, src_x++) {
|
||||
lv_color_8_24_mix(src_buf_al88[src_x].lumi, &dest_buf_u8[dest_x], LV_OPA_MIX3(src_buf_al88[src_x].alpha,
|
||||
mask_buf[src_x], opa));
|
||||
}
|
||||
dest_buf_u8 += dest_stride;
|
||||
src_buf_al88 = drawbuf_next_row(src_buf_al88, src_stride);
|
||||
mask_buf += mask_stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (y = 0; y < h; y++) {
|
||||
for (dest_x = 0, src_x = 0; src_x < w; dest_x += dest_px_size, src_x++) {
|
||||
lv_color32_t src_argb;
|
||||
src_argb.red = src_argb.green = 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_u8[dest_x], src_argb, dsc->blend_mode);
|
||||
}
|
||||
if (mask_buf) {
|
||||
mask_buf += mask_stride;
|
||||
}
|
||||
dest_buf_u8 += 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, uint32_t dest_px_size)
|
||||
{
|
||||
int32_t w = dsc->dest_w;
|
||||
int32_t h = dsc->dest_h;
|
||||
lv_opa_t opa = dsc->opa;
|
||||
uint8_t *dest_buf_u8 = 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_RGB888(dsc, dest_px_size)) {
|
||||
for (y = 0; y < h; y++) {
|
||||
for (dest_x = 0, src_x = 0; src_x < w; dest_x += dest_px_size, src_x++) {
|
||||
dest_buf_u8[dest_x + 2] = src_buf_l8[src_x];
|
||||
dest_buf_u8[dest_x + 1] = src_buf_l8[src_x];
|
||||
dest_buf_u8[dest_x + 0] = src_buf_l8[src_x];
|
||||
}
|
||||
dest_buf_u8 += 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_RGB888_WITH_OPA(dsc, dest_px_size)) {
|
||||
for (y = 0; y < h; y++) {
|
||||
for (dest_x = 0, src_x = 0; src_x < w; dest_x += dest_px_size, src_x++) {
|
||||
lv_color_8_24_mix(src_buf_l8[src_x], &dest_buf_u8[dest_x], opa);
|
||||
}
|
||||
dest_buf_u8 += 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_RGB888_WITH_MASK(dsc, dest_px_size)) {
|
||||
for (y = 0; y < h; y++) {
|
||||
for (dest_x = 0, src_x = 0; src_x < w; dest_x += dest_px_size, src_x++) {
|
||||
lv_color_8_24_mix(src_buf_l8[src_x], &dest_buf_u8[dest_x], mask_buf[src_x]);
|
||||
}
|
||||
dest_buf_u8 += 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_RGB888_MIX_MASK_OPA(dsc, dest_px_size)) {
|
||||
for (y = 0; y < h; y++) {
|
||||
for (dest_x = 0, src_x = 0; src_x < w; dest_x += dest_px_size, src_x++) {
|
||||
lv_color_8_24_mix(src_buf_l8[src_x], &dest_buf_u8[dest_x], LV_OPA_MIX2(opa, mask_buf[src_x]));
|
||||
}
|
||||
dest_buf_u8 += dest_stride;
|
||||
src_buf_l8 = drawbuf_next_row(src_buf_l8, 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; src_x < w; dest_x += dest_px_size, 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_u8[dest_x], src_argb, dsc->blend_mode);
|
||||
}
|
||||
if (mask_buf) {
|
||||
mask_buf += mask_stride;
|
||||
}
|
||||
dest_buf_u8 += 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, uint32_t dest_px_size)
|
||||
{
|
||||
int32_t w = dsc->dest_w;
|
||||
int32_t h = dsc->dest_h;
|
||||
lv_opa_t opa = dsc->opa;
|
||||
uint8_t *dest_buf_u8 = 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;
|
||||
|
||||
int32_t src_x;
|
||||
int32_t dest_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_RGB888(dsc, dest_px_size)) {
|
||||
for (y = 0; y < h; y++) {
|
||||
for (src_x = 0, dest_x = 0; src_x < w; dest_x += dest_px_size, src_x++) {
|
||||
dest_buf_u8[dest_x + 2] = (src_buf_c16[src_x].red * 2106) >> 8; /*To make it rounded*/
|
||||
dest_buf_u8[dest_x + 1] = (src_buf_c16[src_x].green * 1037) >> 8;
|
||||
dest_buf_u8[dest_x + 0] = (src_buf_c16[src_x].blue * 2106) >> 8;
|
||||
}
|
||||
dest_buf_u8 += dest_stride;
|
||||
src_buf_c16 = drawbuf_next_row(src_buf_c16, src_stride);
|
||||
}
|
||||
}
|
||||
} else if (mask_buf == NULL && opa < LV_OPA_MAX) {
|
||||
if (LV_RESULT_INVALID == LV_DRAW_SW_RGB565_BLEND_NORMAL_TO_RGB888_WITH_OPA(dsc, dest_px_size)) {
|
||||
uint8_t res[3];
|
||||
for (y = 0; y < h; y++) {
|
||||
for (src_x = 0, dest_x = 0; src_x < w; dest_x += dest_px_size, src_x++) {
|
||||
res[2] = (src_buf_c16[src_x].red * 2106) >> 8; /*To make it rounded*/
|
||||
res[1] = (src_buf_c16[src_x].green * 1037) >> 8;
|
||||
res[0] = (src_buf_c16[src_x].blue * 2106) >> 8;
|
||||
lv_color_24_24_mix(res, &dest_buf_u8[dest_x], opa);
|
||||
}
|
||||
dest_buf_u8 += 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_RGB888_WITH_MASK(dsc, dest_px_size)) {
|
||||
uint8_t res[3];
|
||||
for (y = 0; y < h; y++) {
|
||||
for (src_x = 0, dest_x = 0; src_x < w; dest_x += dest_px_size, src_x++) {
|
||||
res[2] = (src_buf_c16[src_x].red * 2106) >> 8; /*To make it rounded*/
|
||||
res[1] = (src_buf_c16[src_x].green * 1037) >> 8;
|
||||
res[0] = (src_buf_c16[src_x].blue * 2106) >> 8;
|
||||
lv_color_24_24_mix(res, &dest_buf_u8[dest_x], mask_buf[src_x]);
|
||||
}
|
||||
dest_buf_u8 += 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_RGB888_MIX_MASK_OPA(dsc, dest_px_size)) {
|
||||
uint8_t res[3];
|
||||
for (y = 0; y < h; y++) {
|
||||
for (src_x = 0, dest_x = 0; src_x < w; dest_x += dest_px_size, src_x++) {
|
||||
res[2] = (src_buf_c16[src_x].red * 2106) >> 8; /*To make it rounded*/
|
||||
res[1] = (src_buf_c16[src_x].green * 1037) >> 8;
|
||||
res[0] = (src_buf_c16[src_x].blue * 2106) >> 8;
|
||||
lv_color_24_24_mix(res, &dest_buf_u8[dest_x], LV_OPA_MIX2(opa, mask_buf[src_x]));
|
||||
}
|
||||
dest_buf_u8 += 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 (src_x = 0, dest_x = 0; src_x < w; src_x++, dest_x += dest_px_size) {
|
||||
src_argb.red = (src_buf_c16[src_x].red * 2106) >> 8;
|
||||
src_argb.green = (src_buf_c16[src_x].green * 1037) >> 8;
|
||||
src_argb.blue = (src_buf_c16[src_x].blue * 2106) >> 8;
|
||||
if (mask_buf == NULL) {
|
||||
src_argb.alpha = opa;
|
||||
} else {
|
||||
src_argb.alpha = LV_OPA_MIX2(mask_buf[src_x], opa);
|
||||
}
|
||||
blend_non_normal_pixel(&dest_buf_u8[dest_x], src_argb, dsc->blend_mode);
|
||||
}
|
||||
if (mask_buf) {
|
||||
mask_buf += mask_stride;
|
||||
}
|
||||
dest_buf_u8 += 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 dest_px_size,
|
||||
uint32_t src_px_size)
|
||||
{
|
||||
int32_t w = dsc->dest_w * dest_px_size;
|
||||
int32_t h = dsc->dest_h;
|
||||
lv_opa_t opa = dsc->opa;
|
||||
uint8_t *dest_buf = 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;
|
||||
|
||||
int32_t dest_x;
|
||||
int32_t src_x;
|
||||
int32_t y;
|
||||
|
||||
if (dsc->blend_mode == LV_BLEND_MODE_NORMAL) {
|
||||
/*Special case*/
|
||||
if (mask_buf == NULL && opa >= LV_OPA_MAX) {
|
||||
if (dsc->use_asm && dest_px_size == 3 && src_px_size == 3) {
|
||||
LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_RGB888(dsc, src_px_size, dest_px_size);
|
||||
} else {
|
||||
if (src_px_size == dest_px_size) {
|
||||
for (y = 0; y < h; y++) {
|
||||
lv_memcpy(dest_buf, src_buf, w);
|
||||
dest_buf += dest_stride;
|
||||
src_buf += src_stride;
|
||||
}
|
||||
} else {
|
||||
for (y = 0; y < h; y++) {
|
||||
for (dest_x = 0, src_x = 0; dest_x < w; dest_x += dest_px_size, src_x += src_px_size) {
|
||||
dest_buf[dest_x + 0] = src_buf[src_x + 0];
|
||||
dest_buf[dest_x + 1] = src_buf[src_x + 1];
|
||||
dest_buf[dest_x + 2] = src_buf[src_x + 2];
|
||||
}
|
||||
dest_buf += dest_stride;
|
||||
src_buf += src_stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mask_buf == NULL && opa < LV_OPA_MAX) {
|
||||
if (LV_RESULT_INVALID == LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_RGB888_WITH_OPA(dsc, dest_px_size, src_px_size)) {
|
||||
for (y = 0; y < h; y++) {
|
||||
for (dest_x = 0, src_x = 0; dest_x < w; dest_x += dest_px_size, src_x += src_px_size) {
|
||||
lv_color_24_24_mix(&src_buf[src_x], &dest_buf[dest_x], opa);
|
||||
}
|
||||
dest_buf += dest_stride;
|
||||
src_buf += src_stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mask_buf && opa >= LV_OPA_MAX) {
|
||||
if (LV_RESULT_INVALID == LV_DRAW_SW_RGB888_BLEND_NORMAL_TO_RGB888_WITH_MASK(dsc, dest_px_size, src_px_size)) {
|
||||
uint32_t mask_x;
|
||||
for (y = 0; y < h; y++) {
|
||||
for (mask_x = 0, dest_x = 0, src_x = 0; dest_x < w; mask_x++, dest_x += dest_px_size, src_x += src_px_size) {
|
||||
lv_color_24_24_mix(&src_buf[src_x], &dest_buf[dest_x], mask_buf[mask_x]);
|
||||
}
|
||||
dest_buf += dest_stride;
|
||||
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_RGB888_MIX_MASK_OPA(dsc, dest_px_size, src_px_size)) {
|
||||
uint32_t mask_x;
|
||||
for (y = 0; y < h; y++) {
|
||||
for (mask_x = 0, dest_x = 0, src_x = 0; dest_x < w; mask_x++, dest_x += dest_px_size, src_x += src_px_size) {
|
||||
lv_color_24_24_mix(&src_buf[src_x], &dest_buf[dest_x], LV_OPA_MIX2(opa, mask_buf[mask_x]));
|
||||
}
|
||||
dest_buf += dest_stride;
|
||||
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 += dest_px_size, 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[dest_x], src_argb, dsc->blend_mode);
|
||||
}
|
||||
if (mask_buf) {
|
||||
mask_buf += mask_stride;
|
||||
}
|
||||
dest_buf += dest_stride;
|
||||
src_buf += src_stride;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void LV_ATTRIBUTE_FAST_MEM argb8888_image_blend(_lv_draw_sw_blend_image_dsc_t *dsc, uint32_t dest_px_size)
|
||||
{
|
||||
int32_t w = dsc->dest_w;
|
||||
int32_t h = dsc->dest_h;
|
||||
lv_opa_t opa = dsc->opa;
|
||||
uint8_t *dest_buf = 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;
|
||||
|
||||
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_RGB888(dsc, dest_px_size)) {
|
||||
for (y = 0; y < h; y++) {
|
||||
for (dest_x = 0, src_x = 0; src_x < w; dest_x += dest_px_size, src_x++) {
|
||||
lv_color_24_24_mix((const uint8_t *)&src_buf_c32[src_x], &dest_buf[dest_x], src_buf_c32[src_x].alpha);
|
||||
}
|
||||
dest_buf += 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_RGB888_WITH_OPA(dsc, dest_px_size)) {
|
||||
for (y = 0; y < h; y++) {
|
||||
for (dest_x = 0, src_x = 0; src_x < w; dest_x += dest_px_size, src_x++) {
|
||||
lv_color_24_24_mix((const uint8_t *)&src_buf_c32[src_x], &dest_buf[dest_x], LV_OPA_MIX2(src_buf_c32[src_x].alpha, opa));
|
||||
}
|
||||
dest_buf += 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_RGB888_WITH_MASK(dsc, dest_px_size)) {
|
||||
for (y = 0; y < h; y++) {
|
||||
for (dest_x = 0, src_x = 0; src_x < w; dest_x += dest_px_size, src_x++) {
|
||||
lv_color_24_24_mix((const uint8_t *)&src_buf_c32[src_x], &dest_buf[dest_x],
|
||||
LV_OPA_MIX2(src_buf_c32[src_x].alpha, mask_buf[src_x]));
|
||||
}
|
||||
dest_buf += 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_RGB888_MIX_MASK_OPA(dsc, dest_px_size)) {
|
||||
for (y = 0; y < h; y++) {
|
||||
for (dest_x = 0, src_x = 0; src_x < w; dest_x += dest_px_size, src_x++) {
|
||||
lv_color_24_24_mix((const uint8_t *)&src_buf_c32[src_x], &dest_buf[dest_x],
|
||||
LV_OPA_MIX3(src_buf_c32[src_x].alpha, mask_buf[src_x], opa));
|
||||
}
|
||||
dest_buf += dest_stride;
|
||||
src_buf_c32 = drawbuf_next_row(src_buf_c32, 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; src_x < w; dest_x += dest_px_size, src_x ++) {
|
||||
src_argb = src_buf_c32[src_x];
|
||||
if (mask_buf == NULL) {
|
||||
src_argb.alpha = LV_OPA_MIX2(src_argb.alpha, opa);
|
||||
} else {
|
||||
src_argb.alpha = LV_OPA_MIX3(src_argb.alpha, mask_buf[dest_x], opa);
|
||||
}
|
||||
|
||||
blend_non_normal_pixel(&dest_buf[dest_x], src_argb, dsc->blend_mode);
|
||||
}
|
||||
if (mask_buf) {
|
||||
mask_buf += mask_stride;
|
||||
}
|
||||
dest_buf += dest_stride;
|
||||
src_buf_c32 = drawbuf_next_row(src_buf_c32, src_stride);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void LV_ATTRIBUTE_FAST_MEM blend_non_normal_pixel(uint8_t *dest, lv_color32_t src, lv_blend_mode_t mode)
|
||||
{
|
||||
uint8_t res[3] = {0, 0, 0};
|
||||
switch (mode) {
|
||||
case LV_BLEND_MODE_ADDITIVE:
|
||||
res[0] = LV_MIN(dest[0] + src.blue, 255);
|
||||
res[1] = LV_MIN(dest[1] + src.green, 255);
|
||||
res[2] = LV_MIN(dest[2] + src.red, 255);
|
||||
break;
|
||||
case LV_BLEND_MODE_SUBTRACTIVE:
|
||||
res[0] = LV_MAX(dest[0] - src.blue, 0);
|
||||
res[1] = LV_MAX(dest[1] - src.green, 0);
|
||||
res[2] = LV_MAX(dest[2] - src.red, 0);
|
||||
break;
|
||||
case LV_BLEND_MODE_MULTIPLY:
|
||||
res[0] = (dest[0] * src.blue) >> 8;
|
||||
res[1] = (dest[1] * src.green) >> 8;
|
||||
res[2] = (dest[2] * src.red) >> 8;
|
||||
break;
|
||||
default:
|
||||
LV_LOG_WARN("Not supported blend mode: %d", mode);
|
||||
return;
|
||||
}
|
||||
lv_color_24_24_mix(res, dest, src.alpha);
|
||||
}
|
||||
|
||||
static inline void LV_ATTRIBUTE_FAST_MEM lv_color_8_24_mix(const uint8_t src, uint8_t *dest, uint8_t mix)
|
||||
{
|
||||
|
||||
if (mix == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mix >= LV_OPA_MAX) {
|
||||
dest[0] = src;
|
||||
dest[1] = src;
|
||||
dest[2] = src;
|
||||
} else {
|
||||
lv_opa_t mix_inv = 255 - mix;
|
||||
dest[0] = (uint32_t)((uint32_t)src * mix + dest[0] * mix_inv) >> 8;
|
||||
dest[1] = (uint32_t)((uint32_t)src * mix + dest[1] * mix_inv) >> 8;
|
||||
dest[2] = (uint32_t)((uint32_t)src * mix + dest[2] * mix_inv) >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void LV_ATTRIBUTE_FAST_MEM lv_color_24_24_mix(const uint8_t *src, uint8_t *dest, uint8_t mix)
|
||||
{
|
||||
|
||||
if (mix == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mix >= LV_OPA_MAX) {
|
||||
dest[0] = src[0];
|
||||
dest[1] = src[1];
|
||||
dest[2] = src[2];
|
||||
} else {
|
||||
lv_opa_t mix_inv = 255 - mix;
|
||||
dest[0] = (uint32_t)((uint32_t)src[0] * mix + dest[0] * mix_inv) >> 8;
|
||||
dest[1] = (uint32_t)((uint32_t)src[1] * mix + dest[1] * mix_inv) >> 8;
|
||||
dest[2] = (uint32_t)((uint32_t)src[2] * mix + dest[2] * mix_inv) >> 8;
|
||||
}
|
||||
}
|
||||
|
||||
static inline uint8_t LV_ATTRIBUTE_FAST_MEM get_bit(const uint8_t *buf, int32_t bit_idx)
|
||||
{
|
||||
return (buf[bit_idx / 8] >> (7 - (bit_idx % 8))) & 1;
|
||||
}
|
||||
|
||||
static inline void *LV_ATTRIBUTE_FAST_MEM drawbuf_next_row(const void *buf, uint32_t stride)
|
||||
{
|
||||
return (void *)((uint8_t *)buf + stride);
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user