add some code
This commit is contained in:
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* 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.c
|
||||
*/
|
||||
|
||||
/*********************
|
||||
* INCLUDES
|
||||
*********************/
|
||||
//#include "../../lv_conf_internal.h"
|
||||
#if LV_USE_STDLIB_STRING == LV_STDLIB_BUILTIN
|
||||
#include "lv_assert.h"
|
||||
#include "lv_log.h"
|
||||
#include "lv_math.h"
|
||||
#include "lv_string.h"
|
||||
|
||||
/*********************
|
||||
* DEFINES
|
||||
*********************/
|
||||
#ifdef LV_ARCH_64
|
||||
#define MEM_UNIT uint64_t
|
||||
#define ALIGN_MASK 0x7
|
||||
#else
|
||||
#define MEM_UNIT uint32_t
|
||||
#define ALIGN_MASK 0x3
|
||||
#endif
|
||||
|
||||
#define LV_ATTRIBUTE_FAST_MEM
|
||||
|
||||
/**********************
|
||||
* TYPEDEFS
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC PROTOTYPES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* STATIC VARIABLES
|
||||
**********************/
|
||||
|
||||
/**********************
|
||||
* MACROS
|
||||
**********************/
|
||||
#if LV_USE_LOG && LV_LOG_TRACE_MEM
|
||||
#define LV_TRACE_MEM(...) LV_LOG_TRACE(__VA_ARGS__)
|
||||
#else
|
||||
#define LV_TRACE_MEM(...)
|
||||
#endif
|
||||
|
||||
#define _COPY(d, s) *d = *s; d++; s++;
|
||||
#define _SET(d, v) *d = v; d++;
|
||||
#define _REPEAT8(expr) expr expr expr expr expr expr expr expr
|
||||
|
||||
/**********************
|
||||
* GLOBAL FUNCTIONS
|
||||
**********************/
|
||||
|
||||
void *LV_ATTRIBUTE_FAST_MEM lv_memcpy(void *dst, const void *src, size_t len)
|
||||
{
|
||||
uint8_t *d8 = dst;
|
||||
const uint8_t *s8 = src;
|
||||
|
||||
/*Simplify for small memories*/
|
||||
if (len < 16) {
|
||||
while (len) {
|
||||
*d8 = *s8;
|
||||
d8++;
|
||||
s8++;
|
||||
len--;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
lv_uintptr_t d_align = (lv_uintptr_t)d8 & ALIGN_MASK;
|
||||
lv_uintptr_t s_align = (lv_uintptr_t)s8 & ALIGN_MASK;
|
||||
|
||||
/*Byte copy for unaligned memories*/
|
||||
if (s_align != d_align) {
|
||||
while (len > 32) {
|
||||
_REPEAT8(_COPY(d8, s8));
|
||||
_REPEAT8(_COPY(d8, s8));
|
||||
_REPEAT8(_COPY(d8, s8));
|
||||
_REPEAT8(_COPY(d8, s8));
|
||||
len -= 32;
|
||||
}
|
||||
while (len) {
|
||||
_COPY(d8, s8)
|
||||
len--;
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
/*Make the memories aligned*/
|
||||
if (d_align) {
|
||||
d_align = ALIGN_MASK + 1 - d_align;
|
||||
while (d_align && len) {
|
||||
_COPY(d8, s8);
|
||||
d_align--;
|
||||
len--;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t *d32 = (uint32_t *)d8;
|
||||
const uint32_t *s32 = (uint32_t *)s8;
|
||||
while (len > 32) {
|
||||
_REPEAT8(_COPY(d32, s32))
|
||||
len -= 32;
|
||||
}
|
||||
|
||||
d8 = (uint8_t *)d32;
|
||||
s8 = (const uint8_t *)s32;
|
||||
while (len) {
|
||||
_COPY(d8, s8)
|
||||
len--;
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
void LV_ATTRIBUTE_FAST_MEM lv_memset(void *dst, uint8_t v, size_t len)
|
||||
{
|
||||
uint8_t *d8 = (uint8_t *)dst;
|
||||
uintptr_t d_align = (lv_uintptr_t) d8 & ALIGN_MASK;
|
||||
|
||||
/*Make the address aligned*/
|
||||
if (d_align) {
|
||||
d_align = ALIGN_MASK + 1 - d_align;
|
||||
while (d_align && len) {
|
||||
_SET(d8, v);
|
||||
len--;
|
||||
d_align--;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t v32 = (uint32_t)v + ((uint32_t)v << 8) + ((uint32_t)v << 16) + ((uint32_t)v << 24);
|
||||
uint32_t *d32 = (uint32_t *)d8;
|
||||
|
||||
while (len > 32) {
|
||||
_REPEAT8(_SET(d32, v32));
|
||||
len -= 32;
|
||||
}
|
||||
|
||||
d8 = (uint8_t *)d32;
|
||||
while (len) {
|
||||
_SET(d8, v);
|
||||
len--;
|
||||
}
|
||||
}
|
||||
|
||||
void *LV_ATTRIBUTE_FAST_MEM lv_memmove(void *dst, const void *src, size_t len)
|
||||
{
|
||||
if (dst < src || (char *)dst > ((char *)src + len)) {
|
||||
return lv_memcpy(dst, src, len);
|
||||
}
|
||||
|
||||
if (dst > src) {
|
||||
char *tmp = (char *)dst + len - 1;
|
||||
char *s = (char *)src + len - 1;
|
||||
|
||||
while (len--) {
|
||||
*tmp-- = *s--;
|
||||
}
|
||||
} else {
|
||||
char *tmp = (char *)dst;
|
||||
char *s = (char *)src;
|
||||
|
||||
while (len--) {
|
||||
*tmp++ = *s++;
|
||||
}
|
||||
}
|
||||
|
||||
return dst;
|
||||
}
|
||||
|
||||
/**********************
|
||||
* STATIC FUNCTIONS
|
||||
**********************/
|
||||
|
||||
#endif /*LV_STDLIB_BUILTIN*/
|
||||
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include <stdint.h>
|
||||
#include "lv_color.h"
|
||||
#include "lv_draw_sw_blend.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// ------------------------------------------------- Macros and Types --------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Functionality test combinations
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned int min_w; // Minimum width of the test array
|
||||
unsigned int min_h; // Minimum height of the test array
|
||||
unsigned int max_w; // Maximum width of the test array
|
||||
unsigned int max_h; // Maximum height of the test array
|
||||
unsigned int min_unalign_byte; // Minimum amount of unaligned bytes of the test array
|
||||
unsigned int max_unalign_byte; // Maximum amount of unaligned bytes of the test array
|
||||
unsigned int unalign_step; // Increment step in bytes unalignment of the test array
|
||||
unsigned int dest_stride_step; // Increment step in destination stride of the test array
|
||||
unsigned int test_combinations_count; // Count of fest combinations
|
||||
} test_matrix_params_t;
|
||||
|
||||
/**
|
||||
* @brief Functionality test case parameters
|
||||
*/
|
||||
typedef struct {
|
||||
struct {
|
||||
void *p_asm; // pointer to the working ASM test buf
|
||||
void *p_ansi; // pointer to the working ANSI test buf
|
||||
void *p_asm_alloc; // pointer to the beginning of the memory allocated for ASM test buf, used in free()
|
||||
void *p_ansi_alloc; // pointer to the beginning of the memory allocated for ANSI test buf, used in free()
|
||||
} buf;
|
||||
void (*blend_api_func)(_lv_draw_sw_blend_fill_dsc_t *); // pointer to LVGL API function
|
||||
void (*blend_api_px_func)(_lv_draw_sw_blend_fill_dsc_t *, uint32_t); // pointer to LVGL API function with dest_px_size argument
|
||||
lv_color_format_t color_format; // LV color format
|
||||
size_t data_type_size; // Used data type size, eg sizeof()
|
||||
size_t active_buf_len; // Length of buffer, where the actual data are stored (not including Canary bytes)
|
||||
size_t total_buf_len; // Total length of buffer (including Canary bytes)
|
||||
unsigned int dest_w; // Destination buffer width
|
||||
unsigned int dest_h; // Destination buffer height
|
||||
unsigned int dest_stride; // Destination buffer stride
|
||||
unsigned int unalign_byte; // Destination buffer memory unalignment
|
||||
} func_test_case_params_t;
|
||||
|
||||
/**
|
||||
* @brief Benchmark test case parameters
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned int height; // Test array height
|
||||
unsigned int width; // Test array width
|
||||
unsigned int stride; // Test array stride
|
||||
unsigned int cc_height; // Corner case test array height
|
||||
unsigned int cc_width; // Corner case test array width
|
||||
unsigned int benchmark_cycles; // Count of benchmark cycles
|
||||
void *array_align16; // test array with 16 byte alignment - testing most ideal case
|
||||
void *array_align1; // test array with 1 byte alignment - testing worst case
|
||||
void (*blend_api_func)(_lv_draw_sw_blend_fill_dsc_t *); // pointer to LVGL API function
|
||||
void (*blend_api_px_func)(_lv_draw_sw_blend_fill_dsc_t *, uint32_t); // pointer to LVGL API function with dest_px_size argument
|
||||
} bench_test_case_params_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include <stdint.h>
|
||||
#include "lv_color.h"
|
||||
#include "lv_draw_sw_blend.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// ------------------------------------------------- Macros and Types --------------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Type of blend DUT function
|
||||
*/
|
||||
typedef enum {
|
||||
OPERATION_FILL,
|
||||
OPERATION_FILL_WITH_OPA,
|
||||
} blend_operation_t;
|
||||
|
||||
/**
|
||||
* @brief Canary pixels amount depending on data type
|
||||
* @note
|
||||
* - We should use at least 16 bytes of memory for canary pixels because of esp32s3 TIE 16-bytes wide Q registers
|
||||
* - Canary pixels are multiplied by sizeof(used_data_type) to get the memory length occupied by the canary pixels
|
||||
* - The memory occupied by canary pixels should be in 16-byte multiples, to achieve 16-byte memory alignment in functionality test
|
||||
* - For example, ideally, for RGB565 we would need 8 canary pixels -> 8 * sizeof(uint16_t) = 16
|
||||
*/
|
||||
typedef enum {
|
||||
CANARY_PIXELS_ARGB8888 = 4, /*!< Canary pixels: 4 * sizeof(uint32_t) = 16 */
|
||||
CANARY_PIXELS_RGB565 = 8, /*!< Canary pixels: 8 * sizeof(uint16_t) = 16 */
|
||||
CANARY_PIXELS_RGB888 = 6, /*!< Canary pixels: 6 * sizeof(uint24_t) = 18 */
|
||||
} canary_pixels_t;
|
||||
|
||||
/**
|
||||
* @brief Functionality test combinations for LV Image
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned int min_w; /*!< Minimum width of the test array */
|
||||
unsigned int min_h; /*!< Minimum height of the test array */
|
||||
unsigned int max_w; /*!< Maximum width of the test array */
|
||||
unsigned int max_h; /*!< Maximum height of the test array */
|
||||
unsigned int src_min_unalign_byte; /*!< Minimum amount of unaligned bytes of the source test array */
|
||||
unsigned int dest_min_unalign_byte; /*!< Minimum amount of unaligned bytes of the destination test array */
|
||||
unsigned int src_max_unalign_byte; /*!< Maximum amount of unaligned bytes of the source test array */
|
||||
unsigned int dest_max_unalign_byte; /*!< Maximum amount of unaligned bytes of the destination test array */
|
||||
unsigned int src_unalign_step; /*!< Increment step in bytes unalignment of the source test array */
|
||||
unsigned int dest_unalign_step; /*!< Increment step in bytes unalignment of the destination test array */
|
||||
unsigned int src_stride_step; /*!< Increment step in destination stride of the source test array */
|
||||
unsigned int dest_stride_step; /*!< Increment step in destination stride of the destination test array */
|
||||
unsigned int test_combinations_count; /*!< Count of fest combinations */
|
||||
} test_matrix_lv_image_params_t;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Functionality test case parameters for LV Image
|
||||
*/
|
||||
typedef struct {
|
||||
struct {
|
||||
void *p_src; /*!< pointer to the source test buff (common src buffer for both the ANSI and ASM) */
|
||||
void *p_src_alloc; /*!< pointer to the beginning of the memory allocated for the source ASM test buf, used in free() */
|
||||
void *p_dest_asm; /*!< pointer to the destination ASM test buf */
|
||||
void *p_dest_ansi; /*!< pointer to the destination ANSI test buf */
|
||||
void *p_dest_asm_alloc; /*!< pointer to the beginning of the memory allocated for the destination ASM test buf, used in free() */
|
||||
void *p_dest_ansi_alloc; /*!< pointer to the beginning of the memory allocated for the destination ANSI test buf, used in free() */
|
||||
} buf;
|
||||
void (*blend_api_func)(_lv_draw_sw_blend_image_dsc_t *); /*!< pointer to LVGL API function */
|
||||
void (*blend_api_func_px_size)(_lv_draw_sw_blend_image_dsc_t *, uint32_t); /*!< pointer to LVGL API function, with additional parameter: pixel size */
|
||||
lv_color_format_t color_format; /*!< LV color format */
|
||||
size_t src_data_type_size; /*!< Used data type size in the source buffer, eg sizeof(src_buff[0]) */
|
||||
size_t dest_data_type_size; /*!< Used data type size in the destination buffer, eg sizeof(dest_buff[0]) */
|
||||
size_t src_buf_len; /*!< Length of the source buffer, including matrix padding (no Canary pixels are used for source buffer) */
|
||||
size_t active_dest_buf_len; /*!< Length of the destination buffer, where the actual data are stored, including matrix padding, not including Canary pixels */
|
||||
size_t total_dest_buf_len; /*!< Total length of the destination buffer (including Canary pixels and matrix padding) */
|
||||
size_t canary_pixels; /*!< Canary pixels must be adjusted according to the used color type, to achieve aligned memory effect */
|
||||
size_t memory_alignment_offset; /*!< Memory offset, to correct canary pixels memory shift for RGB888 */
|
||||
unsigned int dest_w; /*!< Destination buffer width */
|
||||
unsigned int dest_h; /*!< Destination buffer height */
|
||||
unsigned int src_stride; /*!< Source buffer stride */
|
||||
unsigned int dest_stride; /*!< Destination buffer stride */
|
||||
unsigned int src_unalign_byte; /*!< Source buffer memory unalignment */
|
||||
unsigned int dest_unalign_byte; /*!< Destination buffer memory unalignment */
|
||||
blend_operation_t operation_type; /*!< Type of fundamental blend operation */
|
||||
} func_test_case_lv_image_params_t;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Benchmark test case parameters for LV Image
|
||||
*/
|
||||
typedef struct {
|
||||
unsigned int height; /*!< Test array height */
|
||||
unsigned int width; /*!< Test array width */
|
||||
unsigned int dest_stride; /*!< Destination test array stride */
|
||||
unsigned int src_stride; /*!< Source test array stride */
|
||||
unsigned int cc_height; /*!< Corner case test array height */
|
||||
unsigned int cc_width; /*!< Corner case test array width */
|
||||
unsigned int benchmark_cycles; /*!< Count of benchmark cycles */
|
||||
void *src_array_align16; /*!< Source test array with 16 byte alignment - testing most ideal case */
|
||||
void *src_array_align1; /*!< Source test array with 1 byte alignment - testing worst case */
|
||||
void *dest_array_align16; /*!< Destination test array with 16 byte alignment - testing most ideal case */
|
||||
void *dest_array_align1; /*!< Destination test array with 1 byte alignment - testing worst case */
|
||||
void (*blend_api_func)(_lv_draw_sw_blend_image_dsc_t *); /*!< pointer to LVGL API function */
|
||||
void (*blend_api_func_px_size)(_lv_draw_sw_blend_image_dsc_t *, uint32_t); /*!< pointer to LVGL API function, with additional parameter: pixel size */
|
||||
lv_color_format_t color_format; /*!< LV color format */
|
||||
} bench_test_case_lv_image_params_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /*extern "C"*/
|
||||
#endif
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "unity.h"
|
||||
#include "unity_test_utils.h"
|
||||
#include "lv_fill_common.h"
|
||||
|
||||
#define TEST_MEMORY_LEAK_THRESHOLD (300)
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
|
||||
// ______ _____ ______ _ _
|
||||
// | _ \/ ___|| ___ \ | | | |
|
||||
// | | | |\ `--. | |_/ / | |_ ___ ___ | |_
|
||||
// | | | | `--. \| __/ | __| / _ \/ __|| __|
|
||||
// | |/ / /\__/ /| | | |_ | __/\__ \| |_
|
||||
// |___/ \____/ \_| \__| \___||___/ \__|
|
||||
|
||||
printf("______ _____ ______ _ _ \r\n");
|
||||
printf("| _ \\/ ___|| ___ \\ | | | | \r\n");
|
||||
printf("| | | |\\ `--. | |_/ / | |_ ___ ___ | |_ \r\n");
|
||||
printf("| | | | `--. \\| __/ | __| / _ \\/ __|| __|\r\n");
|
||||
printf("| |/ / /\\__/ /| | | |_ | __/\\__ \\| |_ \r\n");
|
||||
printf("|___/ \\____/ \\_| \\__| \\___||___/ \\__|\r\n");
|
||||
|
||||
|
||||
UNITY_BEGIN();
|
||||
unity_run_menu();
|
||||
UNITY_END();
|
||||
}
|
||||
|
||||
/* setUp runs before every test */
|
||||
void setUp(void)
|
||||
{
|
||||
// Check for memory leaks
|
||||
unity_utils_set_leak_level(TEST_MEMORY_LEAK_THRESHOLD);
|
||||
unity_utils_record_free_mem();
|
||||
}
|
||||
|
||||
/* tearDown runs after every test */
|
||||
void tearDown(void)
|
||||
{
|
||||
// Evaluate memory leaks
|
||||
unity_utils_evaluate_leaks();
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include <sdkconfig.h>
|
||||
|
||||
#include "unity.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h" // for xthal_get_ccount()
|
||||
#include "lv_fill_common.h"
|
||||
#include "lv_draw_sw_blend.h"
|
||||
#include "lv_draw_sw_blend_to_argb8888.h"
|
||||
#include "lv_draw_sw_blend_to_rgb565.h"
|
||||
#include "lv_draw_sw_blend_to_rgb888.h"
|
||||
|
||||
#define WIDTH 128
|
||||
#define HEIGHT 128
|
||||
#define STRIDE WIDTH
|
||||
#define UNALIGN_BYTES 1
|
||||
#define BENCHMARK_CYCLES 1000
|
||||
|
||||
// ------------------------------------------------- Macros and Types --------------------------------------------------
|
||||
|
||||
static const char *TAG_LV_FILL_BENCH = "LV Fill Benchmark";
|
||||
static const char *asm_ansi_func[] = {"ASM", "ANSI"};
|
||||
static lv_color_t test_color = {
|
||||
.blue = 0x56,
|
||||
.green = 0x34,
|
||||
.red = 0x12,
|
||||
};
|
||||
|
||||
// ------------------------------------------------ Static function headers --------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Initialize the benchmark test
|
||||
*/
|
||||
static void lv_fill_benchmark_init(bench_test_case_params_t *test_params);
|
||||
|
||||
/**
|
||||
* @brief Run the benchmark test
|
||||
*/
|
||||
static float lv_fill_benchmark_run(bench_test_case_params_t *test_params, _lv_draw_sw_blend_fill_dsc_t *dsc);
|
||||
|
||||
// ------------------------------------------------ Test cases ---------------------------------------------------------
|
||||
|
||||
/*
|
||||
Benchmark tests
|
||||
|
||||
Requires:
|
||||
- To pass functionality tests first
|
||||
|
||||
Purpose:
|
||||
- Test that an acceleration is achieved by an assembly implementation of LVGL blending API
|
||||
|
||||
Procedure:
|
||||
- Initialize input parameters (test array length, width, allocate array...) of the benchmark test
|
||||
- Run assembly version of LVGL blending API multiple times (1000-times or so)
|
||||
- Firstly use an input test parameters for the most ideal case (16-byte aligned array, array width and height divisible by 4 for ARGB8888 color format)
|
||||
- Then use worst-case input test parameters (1-byte aligned array, array width and height NOT divisible by 4 for ARGB8888 color format)
|
||||
- Count how many CPU cycles does it take to run a function from the LVGL blending API for each case (ideal and worst case)
|
||||
- Run ansi version of LVGL blending API multiple times (1000-times or so) and repeat the 2 above steps for the ansi version
|
||||
- Free test arrays and structures needed for LVGL blending API
|
||||
*/
|
||||
// ------------------------------------------------ Test cases stages --------------------------------------------------
|
||||
|
||||
TEST_CASE("LV Fill benchmark ARGB8888", "[fill][benchmark][ARGB8888]")
|
||||
{
|
||||
uint32_t *dest_array_align16 = (uint32_t *)memalign(16, STRIDE * HEIGHT * sizeof(uint32_t) + UNALIGN_BYTES);
|
||||
TEST_ASSERT_NOT_EQUAL(NULL, dest_array_align16);
|
||||
|
||||
// Apply byte unalignment for the worst-case test scenario
|
||||
uint32_t *dest_array_align1 = dest_array_align16 + UNALIGN_BYTES;
|
||||
|
||||
bench_test_case_params_t test_params = {
|
||||
.height = HEIGHT,
|
||||
.width = WIDTH,
|
||||
.stride = STRIDE * sizeof(uint32_t),
|
||||
.cc_height = HEIGHT - 1,
|
||||
.cc_width = WIDTH - 1,
|
||||
.benchmark_cycles = BENCHMARK_CYCLES,
|
||||
.array_align16 = (void *)dest_array_align16,
|
||||
.array_align1 = (void *)dest_array_align1,
|
||||
.blend_api_func = &lv_draw_sw_blend_color_to_argb8888,
|
||||
};
|
||||
|
||||
ESP_LOGI(TAG_LV_FILL_BENCH, "running test for ARGB8888 color format");
|
||||
lv_fill_benchmark_init(&test_params);
|
||||
free(dest_array_align16);
|
||||
}
|
||||
|
||||
TEST_CASE("LV Fill benchmark RGB565", "[fill][benchmark][RGB565]")
|
||||
{
|
||||
uint16_t *dest_array_align16 = (uint16_t *)memalign(16, STRIDE * HEIGHT * sizeof(uint16_t) + UNALIGN_BYTES);
|
||||
TEST_ASSERT_NOT_EQUAL(NULL, dest_array_align16);
|
||||
|
||||
// Apply byte unalignment for the worst-case test scenario
|
||||
uint16_t *dest_array_align1 = dest_array_align16 + UNALIGN_BYTES;
|
||||
|
||||
bench_test_case_params_t test_params = {
|
||||
.height = HEIGHT,
|
||||
.width = WIDTH,
|
||||
.stride = STRIDE * sizeof(uint16_t),
|
||||
.cc_height = HEIGHT - 1,
|
||||
.cc_width = WIDTH - 1,
|
||||
.benchmark_cycles = BENCHMARK_CYCLES,
|
||||
.array_align16 = (void *)dest_array_align16,
|
||||
.array_align1 = (void *)dest_array_align1,
|
||||
.blend_api_func = &lv_draw_sw_blend_color_to_rgb565,
|
||||
};
|
||||
|
||||
ESP_LOGI(TAG_LV_FILL_BENCH, "running test for RGB565 color format");
|
||||
lv_fill_benchmark_init(&test_params);
|
||||
free(dest_array_align16);
|
||||
}
|
||||
|
||||
TEST_CASE("LV Fill benchmark RGB888", "[fill][benchmark][RGB888]")
|
||||
{
|
||||
uint8_t *dest_array_align16 = (uint8_t *)memalign(16, STRIDE * HEIGHT * sizeof(uint8_t) * 3 + UNALIGN_BYTES);
|
||||
TEST_ASSERT_NOT_EQUAL(NULL, dest_array_align16);
|
||||
|
||||
// Apply byte unalignment for the worst-case test scenario
|
||||
uint8_t *dest_array_align1 = dest_array_align16 + UNALIGN_BYTES;
|
||||
|
||||
bench_test_case_params_t test_params = {
|
||||
.height = HEIGHT,
|
||||
.width = WIDTH,
|
||||
.stride = STRIDE * 3,
|
||||
.cc_height = HEIGHT - 1,
|
||||
.cc_width = WIDTH - 1,
|
||||
.benchmark_cycles = BENCHMARK_CYCLES,
|
||||
.array_align16 = (void *)dest_array_align16,
|
||||
.array_align1 = (void *)dest_array_align1,
|
||||
.blend_api_px_func = &lv_draw_sw_blend_color_to_rgb888,
|
||||
};
|
||||
|
||||
ESP_LOGI(TAG_LV_FILL_BENCH, "running test for RGB888 color format");
|
||||
lv_fill_benchmark_init(&test_params);
|
||||
free(dest_array_align16);
|
||||
}
|
||||
// ------------------------------------------------ Static test functions ----------------------------------------------
|
||||
|
||||
static void lv_fill_benchmark_init(bench_test_case_params_t *test_params)
|
||||
{
|
||||
// Init structure for LVGL blend API, to call the Assembly API
|
||||
_lv_draw_sw_blend_fill_dsc_t dsc = {
|
||||
.dest_buf = test_params->array_align16,
|
||||
.dest_w = test_params->width,
|
||||
.dest_h = test_params->height,
|
||||
.dest_stride = test_params->stride, // stride * sizeof()
|
||||
.mask_buf = NULL,
|
||||
.color = test_color,
|
||||
.opa = LV_OPA_MAX,
|
||||
.use_asm = true,
|
||||
};
|
||||
|
||||
// Init structure for LVGL blend API, to call the ANSI API
|
||||
_lv_draw_sw_blend_fill_dsc_t dsc_cc = dsc;
|
||||
dsc_cc.dest_buf = test_params->array_align1;
|
||||
dsc_cc.dest_w = test_params->cc_width;
|
||||
dsc_cc.dest_h = test_params->cc_height;
|
||||
|
||||
// Run benchmark 2 times:
|
||||
// First run using assembly, second run using ANSI
|
||||
for (int i = 0; i < 2; i++) {
|
||||
|
||||
// Run benchmark with the most ideal input parameters
|
||||
// Dest array is 16 byte aligned, dest_w and dest_h are dividable by 4
|
||||
float cycles = lv_fill_benchmark_run(test_params, &dsc); // Call Benchmark cycle
|
||||
float per_sample = cycles / ((float)(dsc.dest_w * dsc.dest_h));
|
||||
ESP_LOGI(TAG_LV_FILL_BENCH, " %s ideal case: %.3f cycles for %"PRIi32"x%"PRIi32" matrix, %.3f cycles per sample", asm_ansi_func[i], cycles, dsc.dest_w, dsc.dest_h, per_sample);
|
||||
|
||||
// Run benchmark with the corner case input parameters
|
||||
// Dest array is 1 byte aligned, dest_w and dest_h are not dividable by 4
|
||||
cycles = lv_fill_benchmark_run(test_params, &dsc_cc); // Call Benchmark cycle
|
||||
per_sample = cycles / ((float)(dsc_cc.dest_w * dsc_cc.dest_h));
|
||||
ESP_LOGI(TAG_LV_FILL_BENCH, " %s corner case: %.3f cycles for %"PRIi32"x%"PRIi32" matrix, %.3f cycles per sample\n", asm_ansi_func[i], cycles, dsc_cc.dest_w, dsc_cc.dest_h, per_sample);
|
||||
|
||||
// change to ANSI
|
||||
dsc.use_asm = false;
|
||||
dsc_cc.use_asm = false;
|
||||
}
|
||||
}
|
||||
|
||||
static float lv_fill_benchmark_run(bench_test_case_params_t *test_params, _lv_draw_sw_blend_fill_dsc_t *dsc)
|
||||
{
|
||||
// Call the DUT function for the first time to init the benchmark test
|
||||
if (test_params->blend_api_func != NULL) {
|
||||
test_params->blend_api_func(dsc);
|
||||
} else if (test_params->blend_api_px_func != NULL) {
|
||||
test_params->blend_api_px_func(dsc, 3);
|
||||
}
|
||||
|
||||
const unsigned int start_b = xthal_get_ccount();
|
||||
if (test_params->blend_api_func != NULL) {
|
||||
for (int i = 0; i < test_params->benchmark_cycles; i++) {
|
||||
test_params->blend_api_func(dsc);
|
||||
}
|
||||
} else if (test_params->blend_api_px_func != NULL) {
|
||||
for (int i = 0; i < test_params->benchmark_cycles; i++) {
|
||||
test_params->blend_api_px_func(dsc, 3);
|
||||
}
|
||||
}
|
||||
const unsigned int end_b = xthal_get_ccount();
|
||||
|
||||
const float total_b = end_b - start_b;
|
||||
const float cycles = total_b / (test_params->benchmark_cycles);
|
||||
return cycles;
|
||||
}
|
||||
@@ -0,0 +1,383 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include <inttypes.h>
|
||||
#include "unity.h"
|
||||
#include "esp_log.h"
|
||||
#include "lv_fill_common.h"
|
||||
#include "lv_draw_sw_blend.h"
|
||||
#include "lv_draw_sw_blend_to_argb8888.h"
|
||||
#include "lv_draw_sw_blend_to_rgb565.h"
|
||||
#include "lv_draw_sw_blend_to_rgb888.h"
|
||||
|
||||
// ------------------------------------------------- Defines -----------------------------------------------------------
|
||||
|
||||
#define DBG_PRINT_OUTPUT false
|
||||
#define CANARY_BYTES 4
|
||||
|
||||
// ------------------------------------------------- Macros and Types --------------------------------------------------
|
||||
|
||||
#define UPDATE_TEST_CASE(test_case_ptr, dest_w, dest_h, dest_stride, unalign_byte) ({ \
|
||||
(test_case_ptr)->active_buf_len = (size_t)(dest_h * dest_stride); \
|
||||
(test_case_ptr)->total_buf_len = (size_t)((dest_h * dest_stride) + (CANARY_BYTES * 2)); \
|
||||
(test_case_ptr)->dest_w = (dest_w); \
|
||||
(test_case_ptr)->dest_h = (dest_h); \
|
||||
(test_case_ptr)->dest_stride = (dest_stride); \
|
||||
(test_case_ptr)->unalign_byte = (unalign_byte); \
|
||||
})
|
||||
|
||||
static const char *TAG_LV_FILL_FUNC = "LV Fill Functionality";
|
||||
static char test_msg_buf[128];
|
||||
|
||||
static lv_color_t test_color = {
|
||||
.blue = 0x56,
|
||||
.green = 0x34,
|
||||
.red = 0x12,
|
||||
};
|
||||
|
||||
// ------------------------------------------------ Static function headers --------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Generate all the functionality test combinations
|
||||
*
|
||||
* - generate functionality test combinations, based on the provided test_matrix struct
|
||||
*
|
||||
* @param[in] test_matrix Pointer to structure defining test matrix - all the test combinations
|
||||
* @param[in] test_case Pointer to structure defining functionality test case
|
||||
*/
|
||||
static void functionality_test_matrix(test_matrix_params_t *test_matrix, func_test_case_params_t *test_case);
|
||||
|
||||
/**
|
||||
* @brief Fill test buffers for functionality test
|
||||
*
|
||||
* @param[in] test_case Pointer to structure defining functionality test case
|
||||
*/
|
||||
static void fill_test_bufs(func_test_case_params_t *test_case);
|
||||
|
||||
/**
|
||||
* @brief The actual functionality test
|
||||
*
|
||||
* - function prepares structures for functionality testing and runs the LVGL API
|
||||
*
|
||||
* @param[in] test_case Pointer to structure defining functionality test case
|
||||
*/
|
||||
static void lv_fill_functionality(func_test_case_params_t *test_case);
|
||||
|
||||
/**
|
||||
* @brief Evaluate results for 32bit data length
|
||||
*
|
||||
* @param[in] test_case Pointer to structure defining functionality test case
|
||||
*/
|
||||
static void test_eval_32bit_data(func_test_case_params_t *test_case);
|
||||
|
||||
/**
|
||||
* @brief Evaluate results for 16bit data length
|
||||
*
|
||||
* @param[in] test_case Pointer to structure defining functionality test case
|
||||
*/
|
||||
static void test_eval_16bit_data(func_test_case_params_t *test_case);
|
||||
|
||||
/**
|
||||
* @brief Evaluate results for 24bit data length
|
||||
*
|
||||
* @param[in] test_case Pointer to structure defining functionality test case
|
||||
*/
|
||||
static void test_eval_24bit_data(func_test_case_params_t *test_case);
|
||||
|
||||
// ------------------------------------------------ Test cases ---------------------------------------------------------
|
||||
|
||||
/*
|
||||
Functionality tests
|
||||
|
||||
Purpose:
|
||||
- Test that an assembly version of LVGL blending API achieves the same results as the ANSI version
|
||||
|
||||
Procedure:
|
||||
- Prepare testing matrix, to cover all the possible combinations of destination array widths, lengths, memory alignment...
|
||||
- Run assembly version of the LVGL blending API
|
||||
- Run ANSI C version of the LVGL blending API
|
||||
- Compare the results
|
||||
- Repeat above 3 steps for each test matrix setup
|
||||
*/
|
||||
|
||||
// ------------------------------------------------ Test cases stages --------------------------------------------------
|
||||
|
||||
TEST_CASE("Test fill functionality ARGB8888", "[fill][functionality][ARGB8888]")
|
||||
{
|
||||
test_matrix_params_t test_matrix = {
|
||||
.min_w = 8, // 8 is the lower limit for the esp32s3 asm implementation, otherwise esp32 is executed
|
||||
.min_h = 1,
|
||||
.max_w = 16,
|
||||
.max_h = 16,
|
||||
.min_unalign_byte = 0,
|
||||
.max_unalign_byte = 16,
|
||||
.unalign_step = 1,
|
||||
.dest_stride_step = 1,
|
||||
.test_combinations_count = 0,
|
||||
};
|
||||
|
||||
func_test_case_params_t test_case = {
|
||||
.blend_api_func = &lv_draw_sw_blend_color_to_argb8888,
|
||||
.color_format = LV_COLOR_FORMAT_ARGB8888,
|
||||
.data_type_size = sizeof(uint32_t),
|
||||
};
|
||||
|
||||
ESP_LOGI(TAG_LV_FILL_FUNC, "running test for ARGB8888 color format");
|
||||
functionality_test_matrix(&test_matrix, &test_case);
|
||||
}
|
||||
|
||||
TEST_CASE("Test fill functionality RGB565", "[fill][functionality][RGB565]")
|
||||
{
|
||||
test_matrix_params_t test_matrix = {
|
||||
.min_w = 16, // 16 is the lower limit for the esp32s3 asm implementation, otherwise esp32 is executed
|
||||
.min_h = 1,
|
||||
.max_w = 32,
|
||||
.max_h = 16,
|
||||
.min_unalign_byte = 0,
|
||||
.max_unalign_byte = 16,
|
||||
.unalign_step = 1,
|
||||
.dest_stride_step = 1,
|
||||
.test_combinations_count = 0,
|
||||
};
|
||||
|
||||
func_test_case_params_t test_case = {
|
||||
.blend_api_func = &lv_draw_sw_blend_color_to_rgb565,
|
||||
.color_format = LV_COLOR_FORMAT_RGB565,
|
||||
.data_type_size = sizeof(uint16_t),
|
||||
};
|
||||
|
||||
ESP_LOGI(TAG_LV_FILL_FUNC, "running test for RGB565 color format");
|
||||
functionality_test_matrix(&test_matrix, &test_case);
|
||||
}
|
||||
|
||||
TEST_CASE("Test fill functionality RGB888", "[fill][functionality][RGB888]")
|
||||
{
|
||||
test_matrix_params_t test_matrix = {
|
||||
.min_w = 12, // 12 is the lower limit for the esp32s3 asm implementation, otherwise esp32 is executed
|
||||
.min_h = 1,
|
||||
.max_w = 32,
|
||||
.max_h = 3,
|
||||
.min_unalign_byte = 0,
|
||||
.max_unalign_byte = 16,
|
||||
.unalign_step = 1,
|
||||
.dest_stride_step = 1,
|
||||
.test_combinations_count = 0,
|
||||
};
|
||||
|
||||
func_test_case_params_t test_case = {
|
||||
.blend_api_px_func = &lv_draw_sw_blend_color_to_rgb888,
|
||||
.color_format = LV_COLOR_FORMAT_RGB888,
|
||||
.data_type_size = sizeof(uint8_t) * 3, // 24-bit data length
|
||||
};
|
||||
|
||||
ESP_LOGI(TAG_LV_FILL_FUNC, "running test for RGB888 color format");
|
||||
functionality_test_matrix(&test_matrix, &test_case);
|
||||
}
|
||||
// ------------------------------------------------ Static test functions ----------------------------------------------
|
||||
|
||||
static void functionality_test_matrix(test_matrix_params_t *test_matrix, func_test_case_params_t *test_case)
|
||||
{
|
||||
// Step destination array width
|
||||
for (int dest_w = test_matrix->min_w; dest_w <= test_matrix->max_w; dest_w++) {
|
||||
|
||||
// Step destination array height
|
||||
for (int dest_h = test_matrix->min_h; dest_h <= test_matrix->max_h; dest_h++) {
|
||||
|
||||
// Step destination array stride
|
||||
for (int dest_stride = dest_w; dest_stride <= dest_w * 2; dest_stride += test_matrix->dest_stride_step) {
|
||||
|
||||
// Step destination array unalignment
|
||||
for (int unalign_byte = test_matrix->min_unalign_byte; unalign_byte <= test_matrix->max_unalign_byte; unalign_byte += test_matrix->unalign_step) {
|
||||
|
||||
// Call functionality test
|
||||
UPDATE_TEST_CASE(test_case, dest_w, dest_h, dest_stride, unalign_byte);
|
||||
lv_fill_functionality(test_case);
|
||||
test_matrix->test_combinations_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ESP_LOGI(TAG_LV_FILL_FUNC, "test combinations: %d\n", test_matrix->test_combinations_count);
|
||||
}
|
||||
|
||||
static void lv_fill_functionality(func_test_case_params_t *test_case)
|
||||
{
|
||||
fill_test_bufs(test_case);
|
||||
|
||||
// Init structure for LVGL blend API, to call the Assembly API
|
||||
_lv_draw_sw_blend_fill_dsc_t dsc_asm = {
|
||||
.dest_buf = test_case->buf.p_asm,
|
||||
.dest_w = test_case->dest_w,
|
||||
.dest_h = test_case->dest_h,
|
||||
.dest_stride = test_case->dest_stride * test_case->data_type_size, // stride * sizeof()
|
||||
.mask_buf = NULL,
|
||||
.color = test_color,
|
||||
.opa = LV_OPA_MAX,
|
||||
.use_asm = true,
|
||||
};
|
||||
|
||||
// Init structure for LVGL blend API, to call the ANSI API
|
||||
_lv_draw_sw_blend_fill_dsc_t dsc_ansi = dsc_asm;
|
||||
dsc_ansi.dest_buf = test_case->buf.p_ansi;
|
||||
dsc_ansi.use_asm = false;
|
||||
|
||||
if (test_case->blend_api_func != NULL) {
|
||||
test_case->blend_api_func(&dsc_asm); // Call the LVGL API with Assembly code
|
||||
test_case->blend_api_func(&dsc_ansi); // Call the LVGL API with ANSI code
|
||||
} else if (test_case->blend_api_px_func != NULL) {
|
||||
test_case->blend_api_px_func(&dsc_asm, 3); // Call the LVGL API with Assembly code with set pixel size
|
||||
test_case->blend_api_px_func(&dsc_ansi, 3); // Call the LVGL API with ANSI code with set pixel size
|
||||
}
|
||||
|
||||
// Shift array pointers by Canary Bytes amount back
|
||||
test_case->buf.p_asm -= CANARY_BYTES * test_case->data_type_size;
|
||||
test_case->buf.p_ansi -= CANARY_BYTES * test_case->data_type_size;
|
||||
|
||||
// Evaluate the results
|
||||
sprintf(test_msg_buf, "Test case: dest_w = %d, dest_h = %d, dest_stride = %d, unalign_byte = %d\n", test_case->dest_w, test_case->dest_h, test_case->dest_stride, test_case->unalign_byte);
|
||||
|
||||
switch (test_case->color_format) {
|
||||
case LV_COLOR_FORMAT_ARGB8888: {
|
||||
test_eval_32bit_data(test_case);
|
||||
break;
|
||||
}
|
||||
|
||||
case LV_COLOR_FORMAT_RGB565: {
|
||||
test_eval_16bit_data(test_case);
|
||||
break;
|
||||
}
|
||||
|
||||
case LV_COLOR_FORMAT_RGB888: {
|
||||
test_eval_24bit_data(test_case);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
TEST_ASSERT_MESSAGE(false, "LV Color format not found");
|
||||
}
|
||||
|
||||
free(test_case->buf.p_asm_alloc);
|
||||
free(test_case->buf.p_ansi_alloc);
|
||||
|
||||
}
|
||||
|
||||
static void fill_test_bufs(func_test_case_params_t *test_case)
|
||||
{
|
||||
const size_t data_type_size = test_case->data_type_size; // sizeof() of used data type
|
||||
const size_t total_buf_len = test_case->total_buf_len; // Total buffer length, data part of the buffer including the Canary bytes
|
||||
const size_t active_buf_len = test_case->active_buf_len; // Length of buffer
|
||||
const unsigned int unalign_byte = test_case->unalign_byte;
|
||||
|
||||
// Allocate destination arrays for Assembly and ANSI LVGL Blend API
|
||||
void *mem_asm = memalign(16, (total_buf_len * data_type_size) + unalign_byte);
|
||||
void *mem_ansi = memalign(16, (total_buf_len * data_type_size) + unalign_byte);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(mem_asm, "Lack of memory");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(mem_ansi, "Lack of memory");
|
||||
|
||||
// Save a pointer to the beginning of the allocated memory which will be used to free()
|
||||
test_case->buf.p_asm_alloc = mem_asm;
|
||||
test_case->buf.p_ansi_alloc = mem_ansi;
|
||||
|
||||
// Apply destination array unalignment
|
||||
uint8_t *dest_buf_asm = (uint8_t *)mem_asm + unalign_byte;
|
||||
uint8_t *dest_buf_ansi = (uint8_t *)mem_ansi + unalign_byte;
|
||||
|
||||
// Set the whole buffer to 0, including the Canary bytes part
|
||||
memset(dest_buf_asm, 0, total_buf_len * data_type_size);
|
||||
memset(dest_buf_ansi, 0, total_buf_len * data_type_size);
|
||||
|
||||
// Fill the actual part of the destination buffers with known values,
|
||||
// Values must be same, because of the stride
|
||||
for (int i = CANARY_BYTES; i < active_buf_len + CANARY_BYTES; i++) {
|
||||
dest_buf_asm[i * data_type_size] = (uint8_t)(i % 255);
|
||||
dest_buf_ansi[i * data_type_size] = (uint8_t)(i % 255);
|
||||
}
|
||||
|
||||
// Shift array pointers by Canary Bytes amount
|
||||
dest_buf_asm += CANARY_BYTES * data_type_size;
|
||||
dest_buf_ansi += CANARY_BYTES * data_type_size;
|
||||
|
||||
// Save a pointer to the working part of the memory, where the test data are stored
|
||||
test_case->buf.p_asm = (void *)dest_buf_asm;
|
||||
test_case->buf.p_ansi = (void *)dest_buf_ansi;
|
||||
}
|
||||
|
||||
static void test_eval_32bit_data(func_test_case_params_t *test_case)
|
||||
{
|
||||
// Print results 32bit data
|
||||
#if DBG_PRINT_OUTPUT
|
||||
for (uint32_t i = 0; i < test_case->total_buf_len; i++) {
|
||||
printf("dest_buf[%"PRIi32"] %s ansi = %8"PRIx32" \t asm = %8"PRIx32" \n", i, ((i < 10) ? (" ") : ("")), ((uint32_t *)test_case->buf.p_ansi)[i], ((uint32_t *)test_case->buf.p_asm)[i]);
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
|
||||
// Canary bytes area must stay 0
|
||||
TEST_ASSERT_EACH_EQUAL_UINT32_MESSAGE(0, (uint32_t *)test_case->buf.p_ansi, CANARY_BYTES, test_msg_buf);
|
||||
TEST_ASSERT_EACH_EQUAL_UINT32_MESSAGE(0, (uint32_t *)test_case->buf.p_asm, CANARY_BYTES, test_msg_buf);
|
||||
|
||||
// dest_buf_asm and dest_buf_ansi must be equal
|
||||
TEST_ASSERT_EQUAL_UINT32_ARRAY_MESSAGE((uint32_t *)test_case->buf.p_asm + CANARY_BYTES, (uint32_t *)test_case->buf.p_ansi + CANARY_BYTES, test_case->active_buf_len, test_msg_buf);
|
||||
|
||||
// Canary bytes area must stay 0
|
||||
TEST_ASSERT_EACH_EQUAL_UINT32_MESSAGE(0, (uint32_t *)test_case->buf.p_ansi + (test_case->total_buf_len - CANARY_BYTES), CANARY_BYTES, test_msg_buf);
|
||||
TEST_ASSERT_EACH_EQUAL_UINT32_MESSAGE(0, (uint32_t *)test_case->buf.p_asm + (test_case->total_buf_len - CANARY_BYTES), CANARY_BYTES, test_msg_buf);
|
||||
}
|
||||
|
||||
static void test_eval_16bit_data(func_test_case_params_t *test_case)
|
||||
{
|
||||
// Print results, 16bit data
|
||||
#if DBG_PRINT_OUTPUT
|
||||
for (uint32_t i = 0; i < test_case->total_buf_len; i++) {
|
||||
printf("dest_buf[%"PRIi32"] %s ansi = %8"PRIx16" \t asm = %8"PRIx16" \n", i, ((i < 10) ? (" ") : ("")), ((uint16_t *)test_case->buf.p_ansi)[i], ((uint16_t *)test_case->buf.p_asm)[i]);
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
|
||||
// Canary bytes area must stay 0
|
||||
TEST_ASSERT_EACH_EQUAL_UINT16_MESSAGE(0, (uint16_t *)test_case->buf.p_ansi, CANARY_BYTES, test_msg_buf);
|
||||
TEST_ASSERT_EACH_EQUAL_UINT16_MESSAGE(0, (uint16_t *)test_case->buf.p_asm, CANARY_BYTES, test_msg_buf);
|
||||
|
||||
// dest_buf_asm and dest_buf_ansi must be equal
|
||||
TEST_ASSERT_EQUAL_UINT16_ARRAY_MESSAGE((uint16_t *)test_case->buf.p_asm + CANARY_BYTES, (uint16_t *)test_case->buf.p_ansi + CANARY_BYTES, test_case->active_buf_len, test_msg_buf);
|
||||
|
||||
// Canary bytes area must stay 0
|
||||
TEST_ASSERT_EACH_EQUAL_UINT16_MESSAGE(0, (uint16_t *)test_case->buf.p_ansi + (test_case->total_buf_len - CANARY_BYTES), CANARY_BYTES, test_msg_buf);
|
||||
TEST_ASSERT_EACH_EQUAL_UINT16_MESSAGE(0, (uint16_t *)test_case->buf.p_asm + (test_case->total_buf_len - CANARY_BYTES), CANARY_BYTES, test_msg_buf);
|
||||
}
|
||||
|
||||
static void test_eval_24bit_data(func_test_case_params_t *test_case)
|
||||
{
|
||||
// Print results, 24bit data
|
||||
#if DBG_PRINT_OUTPUT
|
||||
size_t data_type_size = test_case->data_type_size;
|
||||
for (uint32_t i = 0; i < test_case->total_buf_len; i++) {
|
||||
uint32_t ansi_value = ((uint8_t *)test_case->buf.p_ansi)[i * data_type_size]
|
||||
| (((uint8_t *)test_case->buf.p_ansi)[i * data_type_size + 1] << 8)
|
||||
| (((uint8_t *)test_case->buf.p_ansi)[i * data_type_size + 2] << 16);
|
||||
uint32_t asm_value = ((uint8_t *)test_case->buf.p_asm)[i * data_type_size]
|
||||
| (((uint8_t *)test_case->buf.p_asm)[i * data_type_size + 1] << 8)
|
||||
| (((uint8_t *)test_case->buf.p_asm)[i * data_type_size + 2] << 16);
|
||||
printf("dest_buf[%"PRIi32"] %s ansi = %8"PRIx32" \t asm = %8"PRIx32" \n", i, ((i < 10) ? (" ") : ("")), ansi_value, asm_value);
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
|
||||
const int canary_bytes_area = CANARY_BYTES * test_case->data_type_size;
|
||||
|
||||
// Canary bytes area must stay 0
|
||||
TEST_ASSERT_EACH_EQUAL_UINT8_MESSAGE(0, (uint8_t *)test_case->buf.p_ansi, canary_bytes_area, test_msg_buf);
|
||||
TEST_ASSERT_EACH_EQUAL_UINT8_MESSAGE(0, (uint8_t *)test_case->buf.p_asm, canary_bytes_area, test_msg_buf);
|
||||
|
||||
// dest_buf_asm and dest_buf_ansi must be equal
|
||||
TEST_ASSERT_EQUAL_UINT8_ARRAY_MESSAGE((uint8_t *)test_case->buf.p_asm + canary_bytes_area, (uint8_t *)test_case->buf.p_ansi + canary_bytes_area, test_case->active_buf_len * test_case->data_type_size, test_msg_buf);
|
||||
|
||||
// Canary bytes area must stay 0
|
||||
TEST_ASSERT_EACH_EQUAL_UINT8_MESSAGE(0, (uint8_t *)test_case->buf.p_ansi + (test_case->total_buf_len - CANARY_BYTES) * test_case->data_type_size, canary_bytes_area, test_msg_buf);
|
||||
TEST_ASSERT_EACH_EQUAL_UINT8_MESSAGE(0, (uint8_t *)test_case->buf.p_asm + (test_case->total_buf_len - CANARY_BYTES) * test_case->data_type_size, canary_bytes_area, test_msg_buf);
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include <sdkconfig.h>
|
||||
|
||||
#include "unity.h"
|
||||
#include "esp_log.h"
|
||||
#include "freertos/FreeRTOS.h" // for xthal_get_ccount()
|
||||
#include "lv_image_common.h"
|
||||
#include "lv_draw_sw_blend.h"
|
||||
#include "lv_draw_sw_blend_to_rgb565.h"
|
||||
#include "lv_draw_sw_blend_to_rgb888.h"
|
||||
|
||||
#define COMMON_DIM 128 // Common matrix dimension 128x128 pixels
|
||||
#define WIDTH COMMON_DIM
|
||||
#define HEIGHT COMMON_DIM
|
||||
#define STRIDE WIDTH
|
||||
#define UNALIGN_BYTES 3
|
||||
#define BENCHMARK_CYCLES 1000
|
||||
|
||||
// ------------------------------------------------ Static variables ---------------------------------------------------
|
||||
|
||||
static const char *TAG_LV_IMAGE_BENCH = "LV Image Benchmark";
|
||||
static const char *asm_ansi_func[] = {"ASM", "ANSI"};
|
||||
|
||||
// ------------------------------------------------ Static function headers --------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Initialize the benchmark test
|
||||
*/
|
||||
static void lv_image_benchmark_init(bench_test_case_lv_image_params_t *test_params);
|
||||
|
||||
/**
|
||||
* @brief Run the benchmark test
|
||||
*/
|
||||
static float lv_image_benchmark_run(bench_test_case_lv_image_params_t *test_params, _lv_draw_sw_blend_image_dsc_t *dsc);
|
||||
|
||||
// ------------------------------------------------ Test cases ---------------------------------------------------------
|
||||
|
||||
/*
|
||||
Benchmark tests
|
||||
|
||||
Requires:
|
||||
- To pass functionality tests first
|
||||
|
||||
Purpose:
|
||||
- Test that an acceleration is achieved by an assembly implementation of LVGL blending API
|
||||
|
||||
Procedure:
|
||||
- Initialize input parameters (test array length, width, allocate array...) of the benchmark test
|
||||
- Run assembly version of LVGL blending API multiple times (1000-times or so)
|
||||
- Firstly use an input test parameters for the most ideal case (16-byte aligned arrays, arrays widths divisible by 2 for RGB565 color format)
|
||||
- Then use worst-case input test parameters (1-byte aligned arrays, arrays width NOT divisible by 2 for RGB565 color format)
|
||||
- Count how many CPU cycles does it take to run a function from the LVGL blending API for each case (ideal and worst case)
|
||||
- Run ansi version of LVGL blending API multiple times (1000-times or so) and repeat the 2 above steps for the ansi version
|
||||
- Compare the results
|
||||
- Free test arrays and structures needed for LVGL blending API
|
||||
|
||||
Inducing Most ideal and worst case scenarios:
|
||||
- Most ideal:
|
||||
- Both, the source and the destination buffers should be aligned by 16-byte (Xtensa PIE), or 4-byte (Xtensa base) boundaries
|
||||
- Matrix width (in pixels) should be equal to the main loop length in the assembly src code
|
||||
typically multiples of 16 bytes (for RGB565 it's either 32 bytes - 16 pixels or 48 bytes - 24 pixels)
|
||||
- Matrix height does not have any effect on benchmark unit tests, unit the matrix is too large that cache limitations start to affect the performance
|
||||
- Matrix strides, should be equal to the matrix widths (0 matrix padding), or their multiples (matrix width = matrix padding)
|
||||
- Worst case:
|
||||
- Both, hte source and the destination buffers should NOT be aligned by 16-byte (Xtensa PIE), or 4-byte (Xtensa base) boundaries,
|
||||
Source buffer unalignment should be different from the destination unalignment, with one unalignment being even, the other being odd
|
||||
The unalignments shall be small numbers (preferably 1 or 2 bytes)
|
||||
- Matrix width should be one pixels smaller, than the matrix width for the most ideal case
|
||||
- Matrix height does not have any effect on benchmark unit tests, unit the matrix is too large that cache limitations start to affect the performance
|
||||
- Matrix strides, should NOT be equal to the matrix widths (non 0 matrix padding)
|
||||
*/
|
||||
// ------------------------------------------------ Test cases stages --------------------------------------------------
|
||||
|
||||
TEST_CASE("LV Image benchmark RGB565 blend to RGB565", "[image][benchmark][RGB565]")
|
||||
{
|
||||
uint16_t *dest_array_align16 = (uint16_t *)memalign(16, STRIDE * HEIGHT * sizeof(uint16_t) + UNALIGN_BYTES);
|
||||
uint16_t *src_array_align16 = (uint16_t *)memalign(16, STRIDE * HEIGHT * sizeof(uint16_t) + UNALIGN_BYTES);
|
||||
TEST_ASSERT_NOT_EQUAL_MESSAGE(NULL, dest_array_align16, "Lack of memory");
|
||||
TEST_ASSERT_NOT_EQUAL_MESSAGE(NULL, src_array_align16, "Lack of memory");
|
||||
|
||||
// Apply byte unalignment (different for each array) for the worst-case test scenario
|
||||
uint16_t *dest_array_align1 = (uint16_t *)((uint8_t *)dest_array_align16 + UNALIGN_BYTES - 1);
|
||||
uint16_t *src_array_align1 = (uint16_t *)((uint8_t *)src_array_align16 + UNALIGN_BYTES);
|
||||
|
||||
bench_test_case_lv_image_params_t test_params = {
|
||||
.height = HEIGHT,
|
||||
.width = WIDTH,
|
||||
.dest_stride = STRIDE * sizeof(uint16_t),
|
||||
.src_stride = STRIDE * sizeof(uint16_t),
|
||||
.cc_height = HEIGHT,
|
||||
.cc_width = WIDTH - 1,
|
||||
.benchmark_cycles = BENCHMARK_CYCLES,
|
||||
.src_array_align16 = (void *)src_array_align16,
|
||||
.src_array_align1 = (void *)src_array_align1,
|
||||
.dest_array_align16 = (void *)dest_array_align16,
|
||||
.dest_array_align1 = (void *)dest_array_align1,
|
||||
.blend_api_func = &lv_draw_sw_blend_image_to_rgb565,
|
||||
.color_format = LV_COLOR_FORMAT_RGB565,
|
||||
};
|
||||
|
||||
ESP_LOGI(TAG_LV_IMAGE_BENCH, "running test for RGB565 color format");
|
||||
lv_image_benchmark_init(&test_params);
|
||||
free(dest_array_align16);
|
||||
free(src_array_align16);
|
||||
}
|
||||
|
||||
TEST_CASE("LV Image benchmark RGB888 blend to RGB888", "[image][benchmark][RGB888]")
|
||||
{
|
||||
uint8_t *dest_array_align16 = (uint8_t *)memalign(16, (STRIDE * HEIGHT * sizeof(uint8_t) * 3) + UNALIGN_BYTES);
|
||||
uint8_t *src_array_align16 = (uint8_t *)memalign(16, (STRIDE * HEIGHT * sizeof(uint8_t) * 3) + UNALIGN_BYTES);
|
||||
TEST_ASSERT_NOT_EQUAL_MESSAGE(NULL, dest_array_align16, "Lack of memory");
|
||||
TEST_ASSERT_NOT_EQUAL_MESSAGE(NULL, src_array_align16, "Lack of memory");
|
||||
|
||||
// Apply byte unalignment (different for each array) for the worst-case test scenario
|
||||
uint8_t *dest_array_align1 = dest_array_align16 + UNALIGN_BYTES - 1;
|
||||
uint8_t *src_array_align1 = src_array_align16 + UNALIGN_BYTES;
|
||||
|
||||
bench_test_case_lv_image_params_t test_params = {
|
||||
.height = HEIGHT,
|
||||
.width = WIDTH,
|
||||
.dest_stride = STRIDE * sizeof(uint8_t) * 3,
|
||||
.src_stride = STRIDE * sizeof(uint8_t) * 3,
|
||||
.cc_height = HEIGHT,
|
||||
.cc_width = WIDTH - 1,
|
||||
.benchmark_cycles = BENCHMARK_CYCLES,
|
||||
.src_array_align16 = (void *)src_array_align16,
|
||||
.src_array_align1 = (void *)src_array_align1,
|
||||
.dest_array_align16 = (void *)dest_array_align16,
|
||||
.dest_array_align1 = (void *)dest_array_align1,
|
||||
.blend_api_func_px_size = &lv_draw_sw_blend_image_to_rgb888,
|
||||
.color_format = LV_COLOR_FORMAT_RGB888,
|
||||
};
|
||||
|
||||
ESP_LOGI(TAG_LV_IMAGE_BENCH, "running test for RGB888 color format");
|
||||
lv_image_benchmark_init(&test_params);
|
||||
free(dest_array_align16);
|
||||
free(src_array_align16);
|
||||
}
|
||||
// ------------------------------------------------ Static test functions ----------------------------------------------
|
||||
|
||||
static void lv_image_benchmark_init(bench_test_case_lv_image_params_t *test_params)
|
||||
{
|
||||
// Init structure for LVGL blend API, to call the Assembly API
|
||||
_lv_draw_sw_blend_image_dsc_t dsc = {
|
||||
.dest_buf = test_params->dest_array_align16,
|
||||
.dest_w = test_params->width,
|
||||
.dest_h = test_params->height,
|
||||
.dest_stride = test_params->dest_stride, // stride * sizeof()
|
||||
.mask_buf = NULL,
|
||||
.src_buf = test_params->src_array_align16,
|
||||
.src_stride = test_params->src_stride,
|
||||
.src_color_format = test_params->color_format,
|
||||
.opa = LV_OPA_MAX,
|
||||
.blend_mode = LV_BLEND_MODE_NORMAL,
|
||||
.use_asm = true,
|
||||
};
|
||||
|
||||
// Init structure for LVGL blend API, to call the ANSI API
|
||||
_lv_draw_sw_blend_image_dsc_t dsc_cc = dsc;
|
||||
dsc_cc.dest_buf = test_params->dest_array_align1;
|
||||
dsc_cc.dest_w = test_params->cc_width;
|
||||
dsc_cc.dest_h = test_params->cc_height;
|
||||
dsc_cc.src_buf = test_params->src_array_align1;
|
||||
|
||||
// Run benchmark 2 times:
|
||||
// First run using assembly, second run using ANSI
|
||||
for (int i = 0; i < 2; i++) {
|
||||
|
||||
// Run benchmark with the most ideal input parameters
|
||||
float cycles = lv_image_benchmark_run(test_params, &dsc); // Call Benchmark cycle
|
||||
float per_sample = cycles / ((float)(dsc.dest_w * dsc.dest_h));
|
||||
ESP_LOGI(TAG_LV_IMAGE_BENCH, " %s ideal case: %.3f cycles for %"PRIi32"x%"PRIi32" matrix, %.3f cycles per sample", asm_ansi_func[i], cycles, dsc.dest_w, dsc.dest_h, per_sample);
|
||||
|
||||
// Run benchmark with the corner case input parameters
|
||||
cycles = lv_image_benchmark_run(test_params, &dsc_cc); // Call Benchmark cycle
|
||||
per_sample = cycles / ((float)(dsc_cc.dest_w * dsc_cc.dest_h));
|
||||
ESP_LOGI(TAG_LV_IMAGE_BENCH, " %s corner case: %.3f cycles for %"PRIi32"x%"PRIi32" matrix, %.3f cycles per sample\n", asm_ansi_func[i], cycles, dsc_cc.dest_w, dsc_cc.dest_h, per_sample);
|
||||
|
||||
// change to ANSI
|
||||
dsc.use_asm = false;
|
||||
dsc_cc.use_asm = false;
|
||||
}
|
||||
}
|
||||
|
||||
static float lv_image_benchmark_run(bench_test_case_lv_image_params_t *test_params, _lv_draw_sw_blend_image_dsc_t *dsc)
|
||||
{
|
||||
// Call the DUT function for the first time to init the benchmark test
|
||||
if (test_params->blend_api_func != NULL) {
|
||||
test_params->blend_api_func(dsc); // Call the LVGL API
|
||||
} else if (test_params->blend_api_func_px_size != NULL) {
|
||||
test_params->blend_api_func_px_size(dsc, 3); // Call the LVGL API with set pixel size
|
||||
} else {
|
||||
TEST_ASSERT_MESSAGE(false, "Not supported: Both API pointers can't be NULL");
|
||||
}
|
||||
|
||||
|
||||
// Run the benchmark
|
||||
const unsigned int start_b = xthal_get_ccount();
|
||||
if (test_params->blend_api_func != NULL) {
|
||||
|
||||
for (int i = 0; i < test_params->benchmark_cycles; i++) {
|
||||
test_params->blend_api_func(dsc);
|
||||
}
|
||||
|
||||
} else if (test_params->blend_api_func_px_size != NULL) {
|
||||
|
||||
for (int i = 0; i < test_params->benchmark_cycles; i++) {
|
||||
test_params->blend_api_func_px_size(dsc, 3);
|
||||
}
|
||||
}
|
||||
const unsigned int end_b = xthal_get_ccount();
|
||||
|
||||
const float total_b = end_b - start_b;
|
||||
const float cycles = total_b / (test_params->benchmark_cycles);
|
||||
return cycles;
|
||||
}
|
||||
@@ -0,0 +1,459 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
#include <inttypes.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "unity.h"
|
||||
#include "esp_log.h"
|
||||
#include "lv_image_common.h"
|
||||
#include "lv_draw_sw_blend.h"
|
||||
#include "lv_draw_sw_blend_to_rgb565.h"
|
||||
#include "lv_draw_sw_blend_to_rgb888.h"
|
||||
|
||||
// ------------------------------------------------- Defines -----------------------------------------------------------
|
||||
|
||||
#define DBG_PRINT_OUTPUT false
|
||||
|
||||
// ------------------------------------------------- Macros and Types --------------------------------------------------
|
||||
|
||||
#define UPDATE_TEST_CASE(test_case_ptr, dest_w, dest_h, src_stride, dest_stride, src_unalign_byte, dest_unalign_byte) ({ \
|
||||
(test_case_ptr)->src_buf_len = (size_t)(dest_h * src_stride); \
|
||||
(test_case_ptr)->active_dest_buf_len = (size_t)(dest_h * dest_stride); \
|
||||
(test_case_ptr)->total_dest_buf_len = (size_t)((dest_h * dest_stride) + (test_case_ptr->canary_pixels * 2)); \
|
||||
(test_case_ptr)->dest_w = (dest_w); \
|
||||
(test_case_ptr)->dest_h = (dest_h); \
|
||||
(test_case_ptr)->src_stride = (src_stride); \
|
||||
(test_case_ptr)->dest_stride = (dest_stride); \
|
||||
(test_case_ptr)->src_unalign_byte = (src_unalign_byte); \
|
||||
(test_case_ptr)->dest_unalign_byte = (dest_unalign_byte); \
|
||||
})
|
||||
|
||||
// ------------------------------------------------ Static variables ---------------------------------------------------
|
||||
|
||||
static const char *TAG_LV_IMAGE_FUNC = "LV Image Functionality";
|
||||
static char test_msg_buf[200];
|
||||
|
||||
static const test_matrix_lv_image_params_t default_test_matrix_image_blend = {
|
||||
#if CONFIG_IDF_TARGET_ESP32S3
|
||||
.min_w = 8, // 8 is the lower limit for the esp32s3 asm implementation, otherwise esp32 is executed
|
||||
.min_h = 1,
|
||||
.max_w = 24,
|
||||
.max_h = 2,
|
||||
.src_max_unalign_byte = 16, // Use 16-byte boundary check for Xtensa PIE
|
||||
.dest_max_unalign_byte = 16,
|
||||
.dest_unalign_step = 1, // Step 1 as the destination array is being aligned in the assembly code all the time
|
||||
.src_unalign_step = 3, // Step 3 (more relaxed) as source array is used unaligned in the assembly code
|
||||
.src_stride_step = 3,
|
||||
.dest_stride_step = 3,
|
||||
#else
|
||||
.min_w = 1,
|
||||
.min_h = 1,
|
||||
.max_w = 16,
|
||||
.max_h = 2,
|
||||
.src_max_unalign_byte = 4, // Use 4-byte boundary check for Xtensa base
|
||||
.dest_max_unalign_byte = 4,
|
||||
.dest_unalign_step = 1,
|
||||
.src_unalign_step = 1,
|
||||
.src_stride_step = 1,
|
||||
.dest_stride_step = 1,
|
||||
#endif
|
||||
.src_min_unalign_byte = 0,
|
||||
.dest_min_unalign_byte = 0,
|
||||
.test_combinations_count = 0,
|
||||
};
|
||||
|
||||
// ------------------------------------------------ Static function headers --------------------------------------------
|
||||
|
||||
/**
|
||||
* @brief Generate all the functionality test combinations
|
||||
*
|
||||
* - generate functionality test combinations, based on the provided test_matrix struct
|
||||
*
|
||||
* @param[in] test_matrix Pointer to structure defining test matrix - all the test combinations
|
||||
* @param[in] test_case Pointer ot structure defining functionality test case
|
||||
*/
|
||||
static void functionality_test_matrix(test_matrix_lv_image_params_t *test_matrix, func_test_case_lv_image_params_t *test_case);
|
||||
|
||||
/**
|
||||
* @brief Fill test buffers for image functionality test
|
||||
*
|
||||
* @param[in] test_case Pointer ot structure defining functionality test case
|
||||
*/
|
||||
static void fill_test_bufs(func_test_case_lv_image_params_t *test_case);
|
||||
|
||||
/**
|
||||
* @brief The actual functionality test
|
||||
*
|
||||
* - function prepares structures for functionality testing and runs the LVGL API
|
||||
*
|
||||
* @param[in] test_case Pointer ot structure defining functionality test case
|
||||
*/
|
||||
static void lv_image_functionality(func_test_case_lv_image_params_t *test_case);
|
||||
|
||||
/**
|
||||
* @brief Evaluate results of LV Image functionality for 16bit data length
|
||||
*
|
||||
* @param[in] test_case Pointer ot structure defining functionality test case
|
||||
*/
|
||||
static void test_eval_image_16bit_data(func_test_case_lv_image_params_t *test_case);
|
||||
|
||||
/**
|
||||
* @brief Evaluate results of LV Image functionality for 24bit data length
|
||||
*
|
||||
* @param[in] test_case Pointer ot structure defining functionality test case
|
||||
*/
|
||||
static void test_eval_image_24bit_data(func_test_case_lv_image_params_t *test_case);
|
||||
|
||||
// ------------------------------------------------ Test cases ---------------------------------------------------------
|
||||
|
||||
/*
|
||||
Functionality tests
|
||||
|
||||
Purpose:
|
||||
- Test that an assembly version of LVGL blending API achieves the same results as the ANSI version
|
||||
|
||||
Procedure:
|
||||
- Prepare testing matrix, to cover all the possible combinations of destination and source arrays widths,
|
||||
lengths, strides and memory alignments
|
||||
- Run assembly version of the LVGL blending API
|
||||
- Run ANSI C version of the LVGL blending API
|
||||
- Compare the results
|
||||
- Repeat above 3 steps for each test matrix setup
|
||||
*/
|
||||
|
||||
// ------------------------------------------------ Test cases stages --------------------------------------------------
|
||||
|
||||
TEST_CASE("LV Image functionality RGB565 blend to RGB565", "[image][functionality][RGB565]")
|
||||
{
|
||||
test_matrix_lv_image_params_t test_matrix = default_test_matrix_image_blend;
|
||||
|
||||
func_test_case_lv_image_params_t test_case = {
|
||||
.blend_api_func = &lv_draw_sw_blend_image_to_rgb565,
|
||||
.color_format = LV_COLOR_FORMAT_RGB565,
|
||||
.canary_pixels = CANARY_PIXELS_RGB565,
|
||||
.memory_alignment_offset = 0,
|
||||
.src_data_type_size = sizeof(uint16_t),
|
||||
.dest_data_type_size = sizeof(uint16_t),
|
||||
.operation_type = OPERATION_FILL,
|
||||
};
|
||||
|
||||
ESP_LOGI(TAG_LV_IMAGE_FUNC, "running test for RGB565 color format");
|
||||
functionality_test_matrix(&test_matrix, &test_case);
|
||||
}
|
||||
|
||||
TEST_CASE("LV Image functionality RGB888 blend to RGB888", "[image][functionality][RGB888]")
|
||||
{
|
||||
test_matrix_lv_image_params_t test_matrix = default_test_matrix_image_blend;
|
||||
|
||||
func_test_case_lv_image_params_t test_case = {
|
||||
.blend_api_func_px_size = &lv_draw_sw_blend_image_to_rgb888, // The blending API function takes additional parameter, pixel size
|
||||
.color_format = LV_COLOR_FORMAT_RGB888,
|
||||
.canary_pixels = CANARY_PIXELS_RGB888,
|
||||
.memory_alignment_offset = 32 - (CANARY_PIXELS_RGB888 * 3), // Closes 16-byte boundary (32) - RGB888 canary pixels
|
||||
.src_data_type_size = sizeof(uint8_t) * 3,
|
||||
.dest_data_type_size = sizeof(uint8_t) * 3,
|
||||
.operation_type = OPERATION_FILL,
|
||||
};
|
||||
|
||||
ESP_LOGI(TAG_LV_IMAGE_FUNC, "running test for RGB888 color format");
|
||||
functionality_test_matrix(&test_matrix, &test_case);
|
||||
}
|
||||
|
||||
// ------------------------------------------------ Static test functions ----------------------------------------------
|
||||
|
||||
static void functionality_test_matrix(test_matrix_lv_image_params_t *test_matrix, func_test_case_lv_image_params_t *test_case)
|
||||
{
|
||||
// Step destination array width
|
||||
for (int dest_w = test_matrix->min_w; dest_w <= test_matrix->max_w; dest_w++) {
|
||||
|
||||
// Step destination array height
|
||||
for (int dest_h = test_matrix->min_h; dest_h <= test_matrix->max_h; dest_h++) {
|
||||
|
||||
// Step source array stride
|
||||
for (int src_stride = dest_w; src_stride <= dest_w * 2; src_stride += test_matrix->src_stride_step) {
|
||||
|
||||
// Step destination array stride
|
||||
for (int dest_stride = dest_w; dest_stride <= dest_w * 2; dest_stride += test_matrix->dest_stride_step) {
|
||||
|
||||
// Step source array unalignment
|
||||
for (int src_unalign_byte = test_matrix->src_min_unalign_byte; src_unalign_byte <= test_matrix->src_max_unalign_byte; src_unalign_byte += test_matrix->src_unalign_step) {
|
||||
|
||||
// Step destination array unalignment
|
||||
for (int dest_unalign_byte = test_matrix->dest_min_unalign_byte; dest_unalign_byte <= test_matrix->dest_max_unalign_byte; dest_unalign_byte += test_matrix->dest_unalign_step) {
|
||||
|
||||
// Call functionality test
|
||||
UPDATE_TEST_CASE(test_case, dest_w, dest_h, src_stride, dest_stride, src_unalign_byte, dest_unalign_byte);
|
||||
lv_image_functionality(test_case);
|
||||
test_matrix->test_combinations_count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ESP_LOGI(TAG_LV_IMAGE_FUNC, "test combinations: %d\n", test_matrix->test_combinations_count);
|
||||
}
|
||||
|
||||
static void lv_image_functionality(func_test_case_lv_image_params_t *test_case)
|
||||
{
|
||||
fill_test_bufs(test_case);
|
||||
|
||||
_lv_draw_sw_blend_image_dsc_t dsc_asm = {
|
||||
.dest_buf = test_case->buf.p_dest_asm,
|
||||
.dest_w = test_case->dest_w,
|
||||
.dest_h = test_case->dest_h,
|
||||
.dest_stride = test_case->dest_stride * test_case->dest_data_type_size, // dest_stride * sizeof(data_type)
|
||||
.mask_buf = NULL,
|
||||
.mask_stride = 0,
|
||||
.src_buf = test_case->buf.p_src,
|
||||
.src_stride = test_case->src_stride * test_case->src_data_type_size, // src_stride * sizeof(data_type)
|
||||
.src_color_format = test_case->color_format,
|
||||
.opa = LV_OPA_MAX,
|
||||
.blend_mode = LV_BLEND_MODE_NORMAL,
|
||||
.use_asm = true,
|
||||
};
|
||||
|
||||
// Init structure for LVGL blend API, to call the ANSI API
|
||||
_lv_draw_sw_blend_image_dsc_t dsc_ansi = dsc_asm;
|
||||
dsc_ansi.dest_buf = test_case->buf.p_dest_ansi;
|
||||
dsc_ansi.use_asm = false;
|
||||
|
||||
if (test_case->blend_api_func != NULL) {
|
||||
test_case->blend_api_func(&dsc_asm); // Call the LVGL API with Assembly code
|
||||
test_case->blend_api_func(&dsc_ansi); // Call the LVGL API with ANSI code
|
||||
} else if (test_case->blend_api_func_px_size != NULL) {
|
||||
test_case->blend_api_func_px_size(&dsc_asm, 3); // Call the LVGL API with Assembly code with set pixel size
|
||||
test_case->blend_api_func_px_size(&dsc_ansi, 3); // Call the LVGL API with ANSI code with set pixel size
|
||||
} else {
|
||||
TEST_ASSERT_MESSAGE(false, "Not supported: Both API pointers can't be NULL");
|
||||
}
|
||||
|
||||
// Shift array pointers by (Canary pixels amount * data type length) back
|
||||
test_case->buf.p_dest_asm -= test_case->canary_pixels * test_case->dest_data_type_size;
|
||||
test_case->buf.p_dest_ansi -= test_case->canary_pixels * test_case->dest_data_type_size;
|
||||
|
||||
// Evaluate the results
|
||||
sprintf(test_msg_buf, "Test case: dest_w = %d, dest_h = %d, dest_stride = %d, src_stride = %d, dest_unalign_byte = %d, src_unalign_byte = %d\n",
|
||||
test_case->dest_w, test_case->dest_h, test_case->dest_stride, test_case->src_stride, test_case->dest_unalign_byte, test_case->src_unalign_byte);
|
||||
#if DBG_PRINT_OUTPUT
|
||||
printf("%s\n", test_msg_buf);
|
||||
#endif
|
||||
switch (test_case->color_format) {
|
||||
case LV_COLOR_FORMAT_RGB565:
|
||||
test_eval_image_16bit_data(test_case);
|
||||
break;
|
||||
case LV_COLOR_FORMAT_RGB888:
|
||||
test_eval_image_24bit_data(test_case);
|
||||
break;
|
||||
default:
|
||||
TEST_ASSERT_MESSAGE(false, "LV Color format not found");
|
||||
break;
|
||||
}
|
||||
|
||||
// Free memory allocated for test buffers
|
||||
free(test_case->buf.p_dest_asm_alloc);
|
||||
free(test_case->buf.p_dest_ansi_alloc);
|
||||
free(test_case->buf.p_src_alloc);
|
||||
}
|
||||
|
||||
static void fill_test_bufs(func_test_case_lv_image_params_t *test_case)
|
||||
{
|
||||
const size_t src_data_type_size = test_case->src_data_type_size; // sizeof() of used data type in the source buffer
|
||||
const size_t dest_data_type_size = test_case->dest_data_type_size; // sizeof() of used data type in the destination buffer
|
||||
const size_t src_buf_len = test_case->src_buf_len; // Total source buffer length, data part of the source buffer including matrix padding (no Canary pixels are used for source buffer)
|
||||
const size_t total_dest_buf_len = test_case->total_dest_buf_len; // Total destination buffer length, data part of the destination buffer including the Canary pixels and matrix padding
|
||||
const size_t active_dest_buf_len = test_case->active_dest_buf_len; // Length of the data part of the destination buffer including matrix padding
|
||||
const size_t canary_pixels = test_case->canary_pixels; // Canary pixels, according to the data type
|
||||
const unsigned int src_unalign_byte = test_case->src_unalign_byte; // Unalignment bytes for source buffer
|
||||
const unsigned int dest_unalign_byte = test_case->dest_unalign_byte; // Unalignment bytes for destination buffer
|
||||
const unsigned int memory_offset = test_case->memory_alignment_offset; // Memory alignment offset for 16-byte boundary
|
||||
|
||||
// Allocate destination arrays and source array for Assembly and ANSI LVGL Blend API
|
||||
void *src_mem_common = memalign(16, (src_buf_len * src_data_type_size) + src_unalign_byte);
|
||||
void *dest_mem_asm = memalign(16, (total_dest_buf_len * dest_data_type_size) + dest_unalign_byte + memory_offset);
|
||||
void *dest_mem_ansi = memalign(16, (total_dest_buf_len * dest_data_type_size) + dest_unalign_byte + memory_offset);
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(src_mem_common, "Lack of memory");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(dest_mem_asm, "Lack of memory");
|
||||
TEST_ASSERT_NOT_NULL_MESSAGE(dest_mem_ansi, "Lack of memory");
|
||||
|
||||
// Save a pointer to the beginning of the allocated memory which will be used to free()
|
||||
test_case->buf.p_src_alloc = src_mem_common;
|
||||
test_case->buf.p_dest_asm_alloc = dest_mem_asm;
|
||||
test_case->buf.p_dest_ansi_alloc = dest_mem_ansi;
|
||||
|
||||
// Apply destination and source array unalignment
|
||||
uint8_t *src_buf_common = (uint8_t *)src_mem_common + src_unalign_byte;
|
||||
uint8_t *dest_buf_asm = (uint8_t *)dest_mem_asm + dest_unalign_byte + memory_offset;
|
||||
uint8_t *dest_buf_ansi = (uint8_t *)dest_mem_ansi + dest_unalign_byte + memory_offset;
|
||||
|
||||
// Set the whole buffer to 0, including the Canary pixels part
|
||||
memset(src_buf_common, 0, src_buf_len * src_data_type_size);
|
||||
memset(dest_buf_asm, 0, total_dest_buf_len * src_data_type_size);
|
||||
memset(dest_buf_ansi, 0, total_dest_buf_len * src_data_type_size);
|
||||
|
||||
switch (test_case->operation_type) {
|
||||
case OPERATION_FILL:
|
||||
// Fill the actual part of the destination buffers with known values,
|
||||
// Values must be same, because of the stride
|
||||
|
||||
if (test_case->color_format == LV_COLOR_FORMAT_RGB565) {
|
||||
uint16_t *dest_buf_asm_uint16 = (uint16_t *)dest_buf_asm;
|
||||
uint16_t *dest_buf_ansi_uint16 = (uint16_t *)dest_buf_ansi;
|
||||
uint16_t *src_buf_uint16 = (uint16_t *)src_buf_common;
|
||||
|
||||
// Fill destination buffers
|
||||
for (int i = 0; i < active_dest_buf_len; i++) {
|
||||
dest_buf_asm_uint16[canary_pixels + i] = i + ((i & 1) ? 0x6699 : 0x9966);
|
||||
dest_buf_ansi_uint16[canary_pixels + i] = dest_buf_asm_uint16[canary_pixels + i];
|
||||
}
|
||||
|
||||
// Fill source buffer
|
||||
for (int i = 0; i < src_buf_len; i++) {
|
||||
src_buf_uint16[i] = i + ((i & 1) ? 0x55AA : 0xAA55);
|
||||
}
|
||||
}
|
||||
|
||||
if (test_case->color_format == LV_COLOR_FORMAT_RGB888) {
|
||||
uint8_t *dest_buf_asm_uint8 = dest_buf_asm;
|
||||
uint8_t *dest_buf_ansi_uint8 = dest_buf_ansi;
|
||||
uint8_t *src_buf_uint8 = src_buf_common;
|
||||
|
||||
// Fill destination buffers
|
||||
for (int i = 0; i < active_dest_buf_len * 3; i++) {
|
||||
dest_buf_asm_uint8[(canary_pixels * 3) + i] = i + ((i & 1) ? 0x66 : 0x99);
|
||||
dest_buf_ansi_uint8[(canary_pixels * 3) + i] = dest_buf_asm_uint8[(canary_pixels * 3) + i];
|
||||
}
|
||||
|
||||
// Fill source buffer
|
||||
for (int i = 0; i < src_buf_len * 3; i++) {
|
||||
src_buf_uint8[i] = i + ((i & 1) ? 0x55 : 0xAA);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
TEST_ASSERT_MESSAGE(false, "LV Operation not found");
|
||||
break;
|
||||
}
|
||||
|
||||
// Shift array pointers by (Canary pixels amount * data type length) forward
|
||||
dest_buf_asm += canary_pixels * dest_data_type_size;
|
||||
dest_buf_ansi += canary_pixels * dest_data_type_size;
|
||||
|
||||
// Save a pointer to the working part of the memory, where the test data are stored
|
||||
test_case->buf.p_src = (void *)src_buf_common;
|
||||
test_case->buf.p_dest_asm = (void *)dest_buf_asm;
|
||||
test_case->buf.p_dest_ansi = (void *)dest_buf_ansi;
|
||||
|
||||
#if DBG_PRINT_OUTPUT
|
||||
printf("Destination buffers fill:\n");
|
||||
for (uint32_t i = 0; i < test_case->active_dest_buf_len; i++) {
|
||||
printf("dest_buf[%"PRIi32"] %s ansi = %8"PRIx16" \t asm = %8"PRIx16" \n", i, ((i < 10) ? (" ") : ("")), ((uint16_t *)test_case->buf.p_dest_ansi)[i], ((uint16_t *)test_case->buf.p_dest_asm)[i]);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
printf("Source buffer fill:\n");
|
||||
for (uint32_t i = 0; i < test_case->src_buf_len; i++) {
|
||||
printf("src_buf[%"PRIi32"] %s = %8"PRIx16" \n", i, ((i < 10) ? (" ") : ("")), ((uint16_t *)test_case->buf.p_src)[i]);
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
static void test_eval_image_16bit_data(func_test_case_lv_image_params_t *test_case)
|
||||
{
|
||||
// Print results, 16bit data
|
||||
#if DBG_PRINT_OUTPUT
|
||||
printf("\nEval\nDestination buffers fill:\n");
|
||||
for (uint32_t i = 0; i < test_case->total_dest_buf_len; i++) {
|
||||
printf("dest_buf[%"PRIi32"] %s ansi = %8"PRIx16" \t asm = %8"PRIx16" %s \n", i, ((i < 10) ? (" ") : ("")), ((uint16_t *)test_case->buf.p_dest_ansi)[i], ((uint16_t *)test_case->buf.p_dest_asm)[i], (((uint16_t *)test_case->buf.p_dest_ansi)[i] == ((uint16_t *)test_case->buf.p_dest_asm)[i]) ? ("OK") : ("FAIL"));
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
printf("Source buffer fill:\n");
|
||||
for (uint32_t i = 0; i < test_case->src_buf_len; i++) {
|
||||
printf("src_buf[%"PRIi32"] %s = %8"PRIx16" \n", i, ((i < 10) ? (" ") : ("")), ((uint16_t *)test_case->buf.p_src)[i]);
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
|
||||
// Canary pixels area must stay 0
|
||||
const size_t canary_pixels = test_case->canary_pixels;
|
||||
TEST_ASSERT_EACH_EQUAL_UINT16_MESSAGE(0, (uint16_t *)test_case->buf.p_dest_ansi, canary_pixels, test_msg_buf);
|
||||
TEST_ASSERT_EACH_EQUAL_UINT16_MESSAGE(0, (uint16_t *)test_case->buf.p_dest_asm, canary_pixels, test_msg_buf);
|
||||
|
||||
// dest_buf_asm and dest_buf_ansi must be equal
|
||||
TEST_ASSERT_EQUAL_UINT16_ARRAY_MESSAGE((uint16_t *)test_case->buf.p_dest_ansi + canary_pixels, (uint16_t *)test_case->buf.p_dest_asm + canary_pixels, test_case->active_dest_buf_len, test_msg_buf);
|
||||
|
||||
// Data part of the destination buffer and source buffer (not considering matrix padding) must be equal
|
||||
uint16_t *dest_row_begin = (uint16_t *)test_case->buf.p_dest_asm + canary_pixels;
|
||||
uint16_t *src_row_begin = (uint16_t *)test_case->buf.p_src;
|
||||
for (int row = 0; row < test_case->dest_h; row++) {
|
||||
TEST_ASSERT_EQUAL_UINT16_ARRAY_MESSAGE(dest_row_begin, src_row_begin, test_case->dest_w, test_msg_buf);
|
||||
dest_row_begin += test_case->dest_stride; // Move pointer of the destination buffer to the next row
|
||||
src_row_begin += test_case->src_stride; // Move pointer of the source buffer to the next row
|
||||
}
|
||||
|
||||
// Canary pixels area must stay 0
|
||||
TEST_ASSERT_EACH_EQUAL_UINT16_MESSAGE(0, (uint16_t *)test_case->buf.p_dest_ansi + (test_case->total_dest_buf_len - canary_pixels), canary_pixels, test_msg_buf);
|
||||
TEST_ASSERT_EACH_EQUAL_UINT16_MESSAGE(0, (uint16_t *)test_case->buf.p_dest_asm + (test_case->total_dest_buf_len - canary_pixels), canary_pixels, test_msg_buf);
|
||||
}
|
||||
|
||||
static void test_eval_image_24bit_data(func_test_case_lv_image_params_t *test_case)
|
||||
{
|
||||
|
||||
// Print results, 24bit data
|
||||
#if DBG_PRINT_OUTPUT
|
||||
|
||||
printf("\nEval\nDestination buffers fill:\n");
|
||||
size_t dest_data_type_size = test_case->dest_data_type_size;
|
||||
for (uint32_t i = 0; i < test_case->total_dest_buf_len; i++) {
|
||||
uint32_t ansi_value = ((uint8_t *)test_case->buf.p_dest_ansi)[i * dest_data_type_size]
|
||||
| (((uint8_t *)test_case->buf.p_dest_ansi)[i * dest_data_type_size + 1] << 8)
|
||||
| (((uint8_t *)test_case->buf.p_dest_ansi)[i * dest_data_type_size + 2] << 16);
|
||||
uint32_t asm_value = ((uint8_t *)test_case->buf.p_dest_asm)[i * dest_data_type_size]
|
||||
| (((uint8_t *)test_case->buf.p_dest_asm)[i * dest_data_type_size + 1] << 8)
|
||||
| (((uint8_t *)test_case->buf.p_dest_asm)[i * dest_data_type_size + 2] << 16);
|
||||
printf("dest_buf[%"PRIi32"] %s ansi = %8"PRIx32" \t asm = %8"PRIx32" \n", i, ((i < 10) ? (" ") : ("")), ansi_value, asm_value);
|
||||
}
|
||||
printf("\n");
|
||||
|
||||
printf("Source buffer fill:\n");
|
||||
printf("src_buf_len = %d\n", test_case->src_buf_len);
|
||||
size_t src_data_type_size = test_case->src_data_type_size;
|
||||
for (uint32_t i = 0; i < test_case->src_buf_len; i++) {
|
||||
uint32_t value = ((uint8_t *)test_case->buf.p_src)[i * src_data_type_size]
|
||||
| (((uint8_t *)test_case->buf.p_src)[i * src_data_type_size + 1] << 8)
|
||||
| (((uint8_t *)test_case->buf.p_src)[i * src_data_type_size + 2] << 16);
|
||||
printf("dest_buf[%"PRIi32"] %s = %8"PRIx32" \n", i, ((i < 10) ? (" ") : ("")), value);
|
||||
}
|
||||
printf("\n");
|
||||
#endif
|
||||
|
||||
// Canary pixels area must stay 0
|
||||
const size_t canary_pixels = test_case->canary_pixels;
|
||||
TEST_ASSERT_EACH_EQUAL_UINT8_MESSAGE(0, (uint8_t *)test_case->buf.p_dest_ansi, canary_pixels * 3, test_msg_buf);
|
||||
TEST_ASSERT_EACH_EQUAL_UINT8_MESSAGE(0, (uint8_t *)test_case->buf.p_dest_asm, canary_pixels * 3, test_msg_buf);
|
||||
|
||||
// dest_buf_asm and dest_buf_ansi must be equal
|
||||
TEST_ASSERT_EQUAL_UINT8_ARRAY_MESSAGE((uint8_t *)test_case->buf.p_dest_ansi + (canary_pixels * 3), (uint8_t *)test_case->buf.p_dest_asm + (canary_pixels * 3), test_case->active_dest_buf_len, test_msg_buf);
|
||||
|
||||
// Data part of the destination buffer and source buffer (not considering matrix padding) must be equal
|
||||
uint8_t *dest_row_begin = (uint8_t *)test_case->buf.p_dest_asm + (canary_pixels * 3);
|
||||
uint8_t *src_row_begin = (uint8_t *)test_case->buf.p_src;
|
||||
for (int row = 0; row < test_case->dest_h; row++) {
|
||||
TEST_ASSERT_EQUAL_UINT8_ARRAY_MESSAGE(dest_row_begin, src_row_begin, test_case->dest_w * 3, test_msg_buf);
|
||||
dest_row_begin += (test_case->dest_stride * 3); // Move pointer of the destination buffer to the next row
|
||||
src_row_begin += (test_case->src_stride * 3); // Move pointer of the source buffer to the next row
|
||||
}
|
||||
|
||||
// Canary pixels area must stay 0
|
||||
TEST_ASSERT_EACH_EQUAL_UINT8_MESSAGE(0, (uint8_t *)test_case->buf.p_dest_ansi + ((test_case->total_dest_buf_len * 3) - (canary_pixels * 3)), canary_pixels * 3, test_msg_buf);
|
||||
TEST_ASSERT_EACH_EQUAL_UINT8_MESSAGE(0, (uint8_t *)test_case->buf.p_dest_asm + ((test_case->total_dest_buf_len * 3) - (canary_pixels * 3)), canary_pixels * 3, test_msg_buf);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
CONFIG_ESP_TASK_WDT=n
|
||||
CONFIG_OPTIMIZATION_LEVEL_RELEASE=y
|
||||
CONFIG_COMPILER_OPTIMIZATION_PERF=y
|
||||
Reference in New Issue
Block a user