feat(platform): migrate LCD assets to PNG and expand flash partitions
@@ -3,9 +3,9 @@
|
|||||||
nvs, data, nvs, 0x9000, 0x4000,
|
nvs, data, nvs, 0x9000, 0x4000,
|
||||||
otadata, data, ota, 0xd000, 0x2000,
|
otadata, data, ota, 0xd000, 0x2000,
|
||||||
phy_init, data, phy, 0xf000, 0x1000,
|
phy_init, data, phy, 0xf000, 0x1000,
|
||||||
gifpack, data, littlefs, , 0xa80000,
|
gifpack, data, littlefs, , 0x200000,
|
||||||
logfs, data, littlefs, , 0x40000,
|
logfs, data, littlefs, , 0x40000,
|
||||||
ota_0, app, ota_0, , 0x1d0000,
|
ota_0, app, ota_0, , 0x380000,
|
||||||
ota_1, app, ota_1, , 0x1d0000,
|
ota_1, app, ota_1, , 0x380000,
|
||||||
model, data, spiffs, , 0x180000,
|
model, data, spiffs, , 0x300000,
|
||||||
coredump, data, coredump,, 0x10000,
|
coredump, data, coredump,, 0x10000,
|
||||||
|
|||||||
|
@@ -119,6 +119,7 @@ static const char *const s_wealth_direction_asset_dirs[] = {
|
|||||||
|
|
||||||
#define DOMAIN_WEALTH_DIRECTION_FRAME_MS 100U
|
#define DOMAIN_WEALTH_DIRECTION_FRAME_MS 100U
|
||||||
#define DOMAIN_CHOUQIAN_FRAME_MS 500U
|
#define DOMAIN_CHOUQIAN_FRAME_MS 500U
|
||||||
|
#define DOMAIN_CHOUQIAN_FINAL_FRAME_MS 1000U
|
||||||
#define DOMAIN_WUXING_QIANZHI_FRAME_MS 100U
|
#define DOMAIN_WUXING_QIANZHI_FRAME_MS 100U
|
||||||
#define DOMAIN_WUXING_REVEAL_FRAME_MS 100U
|
#define DOMAIN_WUXING_REVEAL_FRAME_MS 100U
|
||||||
#define DOMAIN_WUXING_HOLD_FRAME_MS 500U
|
#define DOMAIN_WUXING_HOLD_FRAME_MS 500U
|
||||||
@@ -1329,7 +1330,7 @@ static esp_err_t domain_resolve_expression_platform_request(
|
|||||||
.asset_dir = result_asset,
|
.asset_dir = result_asset,
|
||||||
.frame_index = 2,
|
.frame_index = 2,
|
||||||
.icon_id = PLATFORM_LCD_PRESENTATION_NO_ICON,
|
.icon_id = PLATFORM_LCD_PRESENTATION_NO_ICON,
|
||||||
.delay_ms = DOMAIN_CHOUQIAN_FRAME_MS,
|
.delay_ms = DOMAIN_CHOUQIAN_FINAL_FRAME_MS,
|
||||||
};
|
};
|
||||||
|
|
||||||
*out_platform_request = (platform_lcd_presentation_request_t) {
|
*out_platform_request = (platform_lcd_presentation_request_t) {
|
||||||
|
|||||||
@@ -13,6 +13,9 @@ set(platform_srcs
|
|||||||
"src/platform_io_effects.c"
|
"src/platform_io_effects.c"
|
||||||
"src/lcd_direct.c"
|
"src/lcd_direct.c"
|
||||||
"src/lcd_direct_assets.c"
|
"src/lcd_direct_assets.c"
|
||||||
|
"third_party/lodepng/lodepng.cpp"
|
||||||
|
"third_party/lodepng/lodepng_esp_alloc.cpp"
|
||||||
|
"third_party/lodepng/lodepng_esp_bridge.cpp"
|
||||||
"src/multimodal/audio_pipeline.c"
|
"src/multimodal/audio_pipeline.c"
|
||||||
"src/multimodal/transport_ws.c"
|
"src/multimodal/transport_ws.c"
|
||||||
"src/wifi_portal_bridge.cpp"
|
"src/wifi_portal_bridge.cpp"
|
||||||
@@ -56,10 +59,17 @@ idf_component_register(
|
|||||||
"include"
|
"include"
|
||||||
PRIV_INCLUDE_DIRS
|
PRIV_INCLUDE_DIRS
|
||||||
"internal"
|
"internal"
|
||||||
|
"third_party/lodepng"
|
||||||
PRIV_REQUIRES
|
PRIV_REQUIRES
|
||||||
${platform_priv_requires}
|
${platform_priv_requires}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
set_source_files_properties(
|
||||||
|
"third_party/lodepng/lodepng.cpp"
|
||||||
|
PROPERTIES
|
||||||
|
COMPILE_DEFINITIONS "LODEPNG_NO_COMPILE_ENCODER;LODEPNG_NO_COMPILE_DISK;LODEPNG_NO_COMPILE_CPP;LODEPNG_NO_COMPILE_ALLOCATORS"
|
||||||
|
)
|
||||||
|
|
||||||
set(platform_audio_prompt_src_dir "${CMAKE_CURRENT_SOURCE_DIR}/assets/audio_prompts")
|
set(platform_audio_prompt_src_dir "${CMAKE_CURRENT_SOURCE_DIR}/assets/audio_prompts")
|
||||||
set(platform_lcd_asset_src_dir "${CMAKE_CURRENT_SOURCE_DIR}/assets/lcd")
|
set(platform_lcd_asset_src_dir "${CMAKE_CURRENT_SOURCE_DIR}/assets/lcd")
|
||||||
set(platform_asset_image_dir "${CMAKE_CURRENT_BINARY_DIR}/asset_image")
|
set(platform_asset_image_dir "${CMAKE_CURRENT_BINARY_DIR}/asset_image")
|
||||||
@@ -67,7 +77,7 @@ set(platform_asset_stage_stamp "${CMAKE_CURRENT_BINARY_DIR}/asset_image.stamp")
|
|||||||
file(GLOB_RECURSE platform_audio_prompt_files CONFIGURE_DEPENDS
|
file(GLOB_RECURSE platform_audio_prompt_files CONFIGURE_DEPENDS
|
||||||
"${platform_audio_prompt_src_dir}/*.opus"
|
"${platform_audio_prompt_src_dir}/*.opus"
|
||||||
)
|
)
|
||||||
set(platform_lcd_flash_assets
|
set(platform_lcd_png_assets
|
||||||
buyunqian1
|
buyunqian1
|
||||||
jichubiaoqing0
|
jichubiaoqing0
|
||||||
jichubiaoqing1
|
jichubiaoqing1
|
||||||
@@ -110,10 +120,10 @@ set(platform_lcd_flash_assets
|
|||||||
icon_tu0
|
icon_tu0
|
||||||
icon_jin0
|
icon_jin0
|
||||||
)
|
)
|
||||||
set(platform_lcd_flash_asset_files)
|
set(platform_lcd_png_asset_files)
|
||||||
foreach(lcd_rle_asset IN LISTS platform_lcd_flash_assets)
|
foreach(lcd_png_asset IN LISTS platform_lcd_png_assets)
|
||||||
list(APPEND platform_lcd_flash_asset_files
|
list(APPEND platform_lcd_png_asset_files
|
||||||
"${platform_lcd_asset_src_dir}/${lcd_rle_asset}.rle"
|
"${platform_lcd_asset_src_dir}/${lcd_png_asset}.png"
|
||||||
)
|
)
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
@@ -127,12 +137,12 @@ foreach(audio_file IN LISTS platform_audio_prompt_files)
|
|||||||
"${platform_asset_image_dir}/audio_prompts/${audio_rel_path}"
|
"${platform_asset_image_dir}/audio_prompts/${audio_rel_path}"
|
||||||
)
|
)
|
||||||
endforeach()
|
endforeach()
|
||||||
foreach(lcd_rle_asset IN LISTS platform_lcd_flash_assets)
|
foreach(lcd_png_asset IN LISTS platform_lcd_png_assets)
|
||||||
list(APPEND platform_asset_stage_commands
|
list(APPEND platform_asset_stage_commands
|
||||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${platform_asset_image_dir}/lcd"
|
COMMAND ${CMAKE_COMMAND} -E make_directory "${platform_asset_image_dir}/lcd"
|
||||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||||
"${platform_lcd_asset_src_dir}/${lcd_rle_asset}.rle"
|
"${platform_lcd_asset_src_dir}/${lcd_png_asset}.png"
|
||||||
"${platform_asset_image_dir}/lcd/${lcd_rle_asset}.rle"
|
"${platform_asset_image_dir}/lcd/${lcd_png_asset}.png"
|
||||||
)
|
)
|
||||||
endforeach()
|
endforeach()
|
||||||
|
|
||||||
@@ -144,7 +154,7 @@ add_custom_command(
|
|||||||
COMMAND ${CMAKE_COMMAND} -E touch "${platform_asset_stage_stamp}"
|
COMMAND ${CMAKE_COMMAND} -E touch "${platform_asset_stage_stamp}"
|
||||||
DEPENDS
|
DEPENDS
|
||||||
${platform_audio_prompt_files}
|
${platform_audio_prompt_files}
|
||||||
${platform_lcd_flash_asset_files}
|
${platform_lcd_png_asset_files}
|
||||||
)
|
)
|
||||||
|
|
||||||
littlefs_create_partition_image(
|
littlefs_create_partition_image(
|
||||||
|
|||||||
|
After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 283 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 283 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 283 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 284 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 283 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 285 KiB |
|
After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 287 KiB |
|
After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 289 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 286 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 285 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 283 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 285 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 286 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 283 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 284 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 285 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 283 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 282 KiB |
|
After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 282 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 291 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 283 KiB |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 294 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 281 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 287 KiB |
|
After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 271 KiB |
|
After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 248 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 249 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 250 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 290 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 281 KiB |
|
After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 374 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 281 KiB |
@@ -8,22 +8,11 @@ extern "C" {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint16_t color;
|
|
||||||
uint16_t count;
|
|
||||||
} lcd_direct_rle_run_t;
|
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
const lcd_direct_rle_run_t *runs;
|
|
||||||
uint32_t run_count;
|
|
||||||
const char *path;
|
const char *path;
|
||||||
} lcd_direct_image_t;
|
} lcd_direct_image_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
const lcd_direct_rle_run_t *runs;
|
|
||||||
uint32_t run_count;
|
|
||||||
const char *path;
|
const char *path;
|
||||||
uint16_t width;
|
|
||||||
uint16_t height;
|
|
||||||
} lcd_direct_icon_t;
|
} lcd_direct_icon_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|||||||
@@ -24,10 +24,12 @@
|
|||||||
#include "esp_lcd_panel_ops.h"
|
#include "esp_lcd_panel_ops.h"
|
||||||
#include "esp_lcd_panel_vendor.h"
|
#include "esp_lcd_panel_vendor.h"
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
|
#include "esp_heap_caps.h"
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/semphr.h"
|
#include "freertos/semphr.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
|
#include "soc/soc_caps.h"
|
||||||
|
|
||||||
#define LCD_DIRECT_SPI_HOST SPI2_HOST
|
#define LCD_DIRECT_SPI_HOST SPI2_HOST
|
||||||
#define LCD_DIRECT_NATIVE_WIDTH 240U
|
#define LCD_DIRECT_NATIVE_WIDTH 240U
|
||||||
@@ -43,12 +45,24 @@
|
|||||||
#define LCD_DIRECT_PIXELS_PER_TRANSFER (LCD_DIRECT_WIDTH * LCD_DIRECT_ROWS_PER_TRANSFER)
|
#define LCD_DIRECT_PIXELS_PER_TRANSFER (LCD_DIRECT_WIDTH * LCD_DIRECT_ROWS_PER_TRANSFER)
|
||||||
#define LCD_DIRECT_PRESENTATION_WAIT_POLL_MS 20U
|
#define LCD_DIRECT_PRESENTATION_WAIT_POLL_MS 20U
|
||||||
#define LCD_DIRECT_TRANSFER_TIMEOUT_MS 100U
|
#define LCD_DIRECT_TRANSFER_TIMEOUT_MS 100U
|
||||||
|
#define LCD_DIRECT_FRAME_CACHE_MAX 4U
|
||||||
|
#define LCD_DIRECT_PATH_MAX 96U
|
||||||
|
#define LCD_DIRECT_LAST_FRAME_EXTRA_HOLD_MS 200U
|
||||||
|
#define LCD_DIRECT_BASIC_FACE_FRAME_HOLD_MS 300U
|
||||||
|
#define LCD_DIRECT_BASIC_FACE_LAST_FRAME_HOLD_MS 300U
|
||||||
|
|
||||||
#define LCD_CMD_MADCTL 0x36
|
#define LCD_CMD_MADCTL 0x36
|
||||||
#define LCD_CMD_RAMCTRL 0xB0
|
#define LCD_CMD_RAMCTRL 0xB0
|
||||||
|
|
||||||
static const char *TAG = "lcd_direct";
|
static const char *TAG = "lcd_direct";
|
||||||
|
|
||||||
|
unsigned lodepng_esp_decode32(unsigned char **out,
|
||||||
|
unsigned *w,
|
||||||
|
unsigned *h,
|
||||||
|
const unsigned char *in,
|
||||||
|
size_t insize);
|
||||||
|
const char *lodepng_esp_error_text(unsigned code);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
platform_lcd_presentation_kind_t kind;
|
platform_lcd_presentation_kind_t kind;
|
||||||
uint8_t priority;
|
uint8_t priority;
|
||||||
@@ -64,10 +78,25 @@ typedef struct {
|
|||||||
size_t step_count;
|
size_t step_count;
|
||||||
} lcd_direct_request_t;
|
} lcd_direct_request_t;
|
||||||
|
|
||||||
|
typedef struct lcd_direct_decoded_image_t {
|
||||||
|
bool valid;
|
||||||
|
bool alpha_valid;
|
||||||
|
char path[LCD_DIRECT_PATH_MAX];
|
||||||
|
uint16_t width;
|
||||||
|
uint16_t height;
|
||||||
|
uint16_t *pixels;
|
||||||
|
uint8_t *alpha;
|
||||||
|
uint32_t last_used;
|
||||||
|
bool pinned;
|
||||||
|
} lcd_direct_decoded_image_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
lcd_direct_icon_draw_t draw;
|
lcd_direct_icon_draw_t draw;
|
||||||
const lcd_direct_icon_t *icon;
|
const lcd_direct_icon_t *icon;
|
||||||
lcd_direct_rle_run_t *loaded_runs;
|
const lcd_direct_decoded_image_t *decoded;
|
||||||
|
lcd_direct_decoded_image_t *cached;
|
||||||
|
lcd_direct_decoded_image_t transient;
|
||||||
|
bool owns_transient;
|
||||||
} lcd_direct_prepared_icon_t;
|
} lcd_direct_prepared_icon_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@@ -107,7 +136,8 @@ static lcd_direct_request_t s_pending;
|
|||||||
static uint32_t s_completion_count;
|
static uint32_t s_completion_count;
|
||||||
static portMUX_TYPE s_state_lock = portMUX_INITIALIZER_UNLOCKED;
|
static portMUX_TYPE s_state_lock = portMUX_INITIALIZER_UNLOCKED;
|
||||||
static uint16_t s_line_buffer[LCD_DIRECT_PIXELS_PER_TRANSFER];
|
static uint16_t s_line_buffer[LCD_DIRECT_PIXELS_PER_TRANSFER];
|
||||||
static lcd_direct_rle_run_t s_rle_file_chunk[128];
|
static lcd_direct_decoded_image_t s_frame_cache[LCD_DIRECT_FRAME_CACHE_MAX];
|
||||||
|
static uint32_t s_cache_use_counter;
|
||||||
|
|
||||||
static void lcd_direct_signal_completion(void);
|
static void lcd_direct_signal_completion(void);
|
||||||
|
|
||||||
@@ -160,6 +190,264 @@ static esp_err_t lcd_direct_draw_rows(uint16_t row_start, uint16_t row_count)
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void *lcd_direct_alloc_psram(size_t size)
|
||||||
|
{
|
||||||
|
#if SOC_SPIRAM_SUPPORTED
|
||||||
|
void *ptr = heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||||
|
if (ptr != NULL) {
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return malloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t lcd_direct_rgb888_to_rgb565(uint8_t red, uint8_t green, uint8_t blue)
|
||||||
|
{
|
||||||
|
return (uint16_t)(((red & 0xF8U) << 8) | ((green & 0xFCU) << 3) | (blue >> 3));
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint16_t lcd_direct_blend_rgb565(uint16_t dst, uint16_t src, uint8_t alpha)
|
||||||
|
{
|
||||||
|
if (alpha == 0U) {
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
if (alpha == 255U) {
|
||||||
|
return src;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t src_r = (uint8_t)(((src >> 11) & 0x1FU) << 3);
|
||||||
|
const uint8_t src_g = (uint8_t)(((src >> 5) & 0x3FU) << 2);
|
||||||
|
const uint8_t src_b = (uint8_t)((src & 0x1FU) << 3);
|
||||||
|
const uint8_t dst_r = (uint8_t)(((dst >> 11) & 0x1FU) << 3);
|
||||||
|
const uint8_t dst_g = (uint8_t)(((dst >> 5) & 0x3FU) << 2);
|
||||||
|
const uint8_t dst_b = (uint8_t)((dst & 0x1FU) << 3);
|
||||||
|
|
||||||
|
const uint8_t out_r = (uint8_t)(((uint16_t)src_r * alpha + (uint16_t)dst_r * (255U - alpha)) / 255U);
|
||||||
|
const uint8_t out_g = (uint8_t)(((uint16_t)src_g * alpha + (uint16_t)dst_g * (255U - alpha)) / 255U);
|
||||||
|
const uint8_t out_b = (uint8_t)(((uint16_t)src_b * alpha + (uint16_t)dst_b * (255U - alpha)) / 255U);
|
||||||
|
return lcd_direct_rgb888_to_rgb565(out_r, out_g, out_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t lcd_direct_read_file_psram(const char *path, unsigned char **out_data, size_t *out_size)
|
||||||
|
{
|
||||||
|
ESP_RETURN_ON_FALSE(path != NULL && out_data != NULL && out_size != NULL,
|
||||||
|
ESP_ERR_INVALID_ARG,
|
||||||
|
TAG,
|
||||||
|
"invalid PNG read request");
|
||||||
|
*out_data = NULL;
|
||||||
|
*out_size = 0;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
int fd = open(path, O_RDONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
ESP_LOGE(TAG, "open PNG asset failed path=%s errno=%d", path, errno);
|
||||||
|
return ESP_ERR_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
off_t file_size = lseek(fd, 0, SEEK_END);
|
||||||
|
if (file_size <= 0 || lseek(fd, 0, SEEK_SET) < 0) {
|
||||||
|
ESP_LOGE(TAG, "stat PNG asset failed path=%s errno=%d", path, errno);
|
||||||
|
close(fd);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *data = (unsigned char *)lcd_direct_alloc_psram((size_t)file_size);
|
||||||
|
if (data == NULL) {
|
||||||
|
ESP_LOGE(TAG, "allocate PNG file buffer failed path=%s size=%u", path, (unsigned)file_size);
|
||||||
|
close(fd);
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t offset = 0;
|
||||||
|
while (offset < (size_t)file_size) {
|
||||||
|
ssize_t bytes_read = read(fd, data + offset, (size_t)file_size - offset);
|
||||||
|
if (bytes_read <= 0) {
|
||||||
|
ESP_LOGE(TAG, "read PNG asset failed path=%s errno=%d", path, errno);
|
||||||
|
free(data);
|
||||||
|
close(fd);
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
offset += (size_t)bytes_read;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(fd);
|
||||||
|
*out_data = data;
|
||||||
|
*out_size = (size_t)file_size;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void lcd_direct_release_decoded_image(lcd_direct_decoded_image_t *decoded)
|
||||||
|
{
|
||||||
|
if (decoded == NULL) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
free(decoded->pixels);
|
||||||
|
free(decoded->alpha);
|
||||||
|
memset(decoded, 0, sizeof(*decoded));
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t lcd_direct_decode_png(const char *path,
|
||||||
|
bool keep_alpha,
|
||||||
|
lcd_direct_decoded_image_t *out_decoded)
|
||||||
|
{
|
||||||
|
ESP_RETURN_ON_FALSE(path != NULL && out_decoded != NULL,
|
||||||
|
ESP_ERR_INVALID_ARG,
|
||||||
|
TAG,
|
||||||
|
"invalid PNG decode request");
|
||||||
|
memset(out_decoded, 0, sizeof(*out_decoded));
|
||||||
|
|
||||||
|
unsigned char *png_data = NULL;
|
||||||
|
size_t png_size = 0;
|
||||||
|
esp_err_t read_err = lcd_direct_read_file_psram(path, &png_data, &png_size);
|
||||||
|
if (read_err != ESP_OK) {
|
||||||
|
return read_err;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned char *rgba = NULL;
|
||||||
|
unsigned width = 0;
|
||||||
|
unsigned height = 0;
|
||||||
|
unsigned decode_err = lodepng_esp_decode32(&rgba, &width, &height, png_data, png_size);
|
||||||
|
free(png_data);
|
||||||
|
if (decode_err != 0U) {
|
||||||
|
ESP_LOGE(TAG, "decode PNG failed path=%s error=%u text=%s",
|
||||||
|
path,
|
||||||
|
decode_err,
|
||||||
|
lodepng_esp_error_text(decode_err));
|
||||||
|
return ESP_FAIL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (width == 0U || height == 0U || width > UINT16_MAX || height > UINT16_MAX) {
|
||||||
|
ESP_LOGE(TAG, "unsupported PNG dimensions path=%s width=%u height=%u", path, width, height);
|
||||||
|
free(rgba);
|
||||||
|
return ESP_ERR_INVALID_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t pixel_count = (size_t)width * (size_t)height;
|
||||||
|
uint16_t *pixels = (uint16_t *)lcd_direct_alloc_psram(pixel_count * sizeof(uint16_t));
|
||||||
|
if (pixels == NULL) {
|
||||||
|
ESP_LOGE(TAG, "allocate RGB565 PNG cache failed path=%s pixels=%u", path, (unsigned)pixel_count);
|
||||||
|
free(rgba);
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t *alpha = NULL;
|
||||||
|
bool alpha_needed = false;
|
||||||
|
if (keep_alpha) {
|
||||||
|
alpha = (uint8_t *)lcd_direct_alloc_psram(pixel_count);
|
||||||
|
if (alpha == NULL) {
|
||||||
|
ESP_LOGE(TAG, "allocate PNG alpha cache failed path=%s pixels=%u", path, (unsigned)pixel_count);
|
||||||
|
free(pixels);
|
||||||
|
free(rgba);
|
||||||
|
return ESP_ERR_NO_MEM;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < pixel_count; ++i) {
|
||||||
|
const unsigned char *src = &rgba[i * 4U];
|
||||||
|
pixels[i] = lcd_direct_rgb888_to_rgb565(src[0], src[1], src[2]);
|
||||||
|
if (alpha != NULL) {
|
||||||
|
alpha[i] = src[3];
|
||||||
|
if (src[3] != 255U) {
|
||||||
|
alpha_needed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
free(rgba);
|
||||||
|
|
||||||
|
strlcpy(out_decoded->path, path, sizeof(out_decoded->path));
|
||||||
|
out_decoded->width = (uint16_t)width;
|
||||||
|
out_decoded->height = (uint16_t)height;
|
||||||
|
out_decoded->pixels = pixels;
|
||||||
|
out_decoded->alpha = alpha_needed ? alpha : NULL;
|
||||||
|
out_decoded->alpha_valid = alpha_needed;
|
||||||
|
out_decoded->valid = true;
|
||||||
|
if (!alpha_needed) {
|
||||||
|
free(alpha);
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG,
|
||||||
|
"PNG decoded path=%s size=%ux%u bytes=%u alpha=%d",
|
||||||
|
path,
|
||||||
|
width,
|
||||||
|
height,
|
||||||
|
(unsigned)(pixel_count * sizeof(uint16_t)),
|
||||||
|
alpha_needed);
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static lcd_direct_decoded_image_t *lcd_direct_find_cached_image(const char *path, bool require_alpha)
|
||||||
|
{
|
||||||
|
if (path == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < LCD_DIRECT_FRAME_CACHE_MAX; ++i) {
|
||||||
|
lcd_direct_decoded_image_t *entry = &s_frame_cache[i];
|
||||||
|
if (entry->valid && strcmp(entry->path, path) == 0) {
|
||||||
|
(void)require_alpha;
|
||||||
|
entry->last_used = ++s_cache_use_counter;
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static lcd_direct_decoded_image_t *lcd_direct_find_cache_slot(void)
|
||||||
|
{
|
||||||
|
lcd_direct_decoded_image_t *oldest = NULL;
|
||||||
|
for (size_t i = 0; i < LCD_DIRECT_FRAME_CACHE_MAX; ++i) {
|
||||||
|
if (!s_frame_cache[i].valid) {
|
||||||
|
return &s_frame_cache[i];
|
||||||
|
}
|
||||||
|
if (!s_frame_cache[i].pinned &&
|
||||||
|
(oldest == NULL || s_frame_cache[i].last_used < oldest->last_used)) {
|
||||||
|
oldest = &s_frame_cache[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (oldest != NULL) {
|
||||||
|
lcd_direct_release_decoded_image(oldest);
|
||||||
|
}
|
||||||
|
return oldest;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t lcd_direct_get_png_image(const char *path,
|
||||||
|
bool keep_alpha,
|
||||||
|
bool cache,
|
||||||
|
lcd_direct_decoded_image_t **out_image,
|
||||||
|
lcd_direct_decoded_image_t *transient)
|
||||||
|
{
|
||||||
|
ESP_RETURN_ON_FALSE(path != NULL && out_image != NULL,
|
||||||
|
ESP_ERR_INVALID_ARG,
|
||||||
|
TAG,
|
||||||
|
"invalid PNG image request");
|
||||||
|
*out_image = NULL;
|
||||||
|
|
||||||
|
lcd_direct_decoded_image_t *cached = lcd_direct_find_cached_image(path, keep_alpha);
|
||||||
|
if (cached != NULL) {
|
||||||
|
*out_image = cached;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
lcd_direct_decoded_image_t *target = NULL;
|
||||||
|
if (cache) {
|
||||||
|
target = lcd_direct_find_cache_slot();
|
||||||
|
}
|
||||||
|
if (target == NULL) {
|
||||||
|
ESP_RETURN_ON_FALSE(transient != NULL, ESP_ERR_NO_MEM, TAG, "PNG cache full and no transient target");
|
||||||
|
target = transient;
|
||||||
|
}
|
||||||
|
|
||||||
|
esp_err_t err = lcd_direct_decode_png(path, keep_alpha, target);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
if (target != transient) {
|
||||||
|
target->last_used = ++s_cache_use_counter;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_image = target;
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
static esp_err_t lcd_direct_gpio_init(void)
|
static esp_err_t lcd_direct_gpio_init(void)
|
||||||
{
|
{
|
||||||
uint64_t pin_mask = (1ULL << CONFIG_TQ_SCREEN_BACKLIGHT_GPIO) |
|
uint64_t pin_mask = (1ULL << CONFIG_TQ_SCREEN_BACKLIGHT_GPIO) |
|
||||||
@@ -319,48 +607,6 @@ static esp_err_t lcd_direct_draw_test_pattern(const lcd_direct_request_t *reques
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static lcd_direct_rle_run_t *lcd_direct_load_icon_runs(const lcd_direct_icon_t *icon)
|
|
||||||
{
|
|
||||||
if (icon == NULL || icon->path == NULL || icon->run_count == 0U) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t bytes_to_read = icon->run_count * sizeof(lcd_direct_rle_run_t);
|
|
||||||
lcd_direct_rle_run_t *runs = (lcd_direct_rle_run_t *)malloc(bytes_to_read);
|
|
||||||
if (runs == NULL) {
|
|
||||||
ESP_LOGW(TAG, "allocate LCD icon failed path=%s bytes=%u", icon->path, (unsigned)bytes_to_read);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
int fd = open(icon->path, O_RDONLY);
|
|
||||||
if (fd < 0) {
|
|
||||||
ESP_LOGW(TAG, "open LCD icon failed path=%s errno=%d", icon->path, errno);
|
|
||||||
free(runs);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t offset = 0;
|
|
||||||
while (offset < bytes_to_read) {
|
|
||||||
ssize_t bytes_read = read(fd, ((uint8_t *)runs) + offset, bytes_to_read - offset);
|
|
||||||
if (bytes_read <= 0) {
|
|
||||||
ESP_LOGW(TAG,
|
|
||||||
"read LCD icon failed path=%s requested=%u read=%d errno=%d",
|
|
||||||
icon->path,
|
|
||||||
(unsigned)bytes_to_read,
|
|
||||||
(int)bytes_read,
|
|
||||||
errno);
|
|
||||||
close(fd);
|
|
||||||
free(runs);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
offset += (size_t)bytes_read;
|
|
||||||
}
|
|
||||||
|
|
||||||
close(fd);
|
|
||||||
return runs;
|
|
||||||
}
|
|
||||||
|
|
||||||
static size_t lcd_direct_prepare_icons(const lcd_direct_icon_draw_t *icons,
|
static size_t lcd_direct_prepare_icons(const lcd_direct_icon_draw_t *icons,
|
||||||
size_t icon_count,
|
size_t icon_count,
|
||||||
lcd_direct_prepared_icon_t *prepared,
|
lcd_direct_prepared_icon_t *prepared,
|
||||||
@@ -374,26 +620,35 @@ static size_t lcd_direct_prepare_icons(const lcd_direct_icon_draw_t *icons,
|
|||||||
for (size_t i = 0; i < icon_count && prepared_count < prepared_capacity; ++i) {
|
for (size_t i = 0; i < icon_count && prepared_count < prepared_capacity; ++i) {
|
||||||
const lcd_direct_icon_draw_t *draw = &icons[i];
|
const lcd_direct_icon_draw_t *draw = &icons[i];
|
||||||
const lcd_direct_icon_t *icon = lcd_direct_icon(draw->icon_id);
|
const lcd_direct_icon_t *icon = lcd_direct_icon(draw->icon_id);
|
||||||
if (icon == NULL || icon->width == 0U || icon->height == 0U) {
|
if (icon == NULL || icon->path == NULL) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (draw->x >= LCD_DIRECT_WIDTH || draw->y >= LCD_DIRECT_HEIGHT) {
|
if (draw->x >= LCD_DIRECT_WIDTH || draw->y >= LCD_DIRECT_HEIGHT) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
lcd_direct_rle_run_t *loaded_runs = NULL;
|
lcd_direct_prepared_icon_t *slot = &prepared[prepared_count];
|
||||||
if (icon->runs == NULL) {
|
lcd_direct_decoded_image_t *decoded = NULL;
|
||||||
loaded_runs = lcd_direct_load_icon_runs(icon);
|
esp_err_t decode_err = lcd_direct_get_png_image(icon->path,
|
||||||
if (loaded_runs == NULL) {
|
true,
|
||||||
continue;
|
true,
|
||||||
}
|
&decoded,
|
||||||
|
&slot->transient);
|
||||||
|
if (decode_err != ESP_OK || decoded == NULL) {
|
||||||
|
lcd_direct_release_decoded_image(&slot->transient);
|
||||||
|
ESP_LOGW(TAG, "prepare icon PNG failed path=%s err=%s", icon->path, esp_err_to_name(decode_err));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (decoded == &slot->transient) {
|
||||||
|
slot->owns_transient = true;
|
||||||
|
} else {
|
||||||
|
decoded->pinned = true;
|
||||||
|
slot->cached = decoded;
|
||||||
}
|
}
|
||||||
|
|
||||||
prepared[prepared_count] = (lcd_direct_prepared_icon_t) {
|
slot->draw = *draw;
|
||||||
.draw = *draw,
|
slot->icon = icon;
|
||||||
.icon = icon,
|
slot->decoded = decoded;
|
||||||
.loaded_runs = loaded_runs,
|
|
||||||
};
|
|
||||||
prepared_count++;
|
prepared_count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -407,8 +662,15 @@ static void lcd_direct_release_prepared_icons(lcd_direct_prepared_icon_t *prepar
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < prepared_count; ++i) {
|
for (size_t i = 0; i < prepared_count; ++i) {
|
||||||
free(prepared[i].loaded_runs);
|
if (prepared[i].cached != NULL) {
|
||||||
prepared[i].loaded_runs = NULL;
|
prepared[i].cached->pinned = false;
|
||||||
|
prepared[i].cached = NULL;
|
||||||
|
}
|
||||||
|
if (prepared[i].owns_transient) {
|
||||||
|
lcd_direct_release_decoded_image(&prepared[i].transient);
|
||||||
|
prepared[i].owns_transient = false;
|
||||||
|
}
|
||||||
|
prepared[i].decoded = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -425,19 +687,14 @@ static void lcd_direct_overlay_icons(uint16_t *pixel_buffer,
|
|||||||
const uint16_t row_end = row_start + row_count;
|
const uint16_t row_end = row_start + row_count;
|
||||||
for (size_t icon_index = 0; icon_index < icon_count; ++icon_index) {
|
for (size_t icon_index = 0; icon_index < icon_count; ++icon_index) {
|
||||||
const lcd_direct_icon_draw_t *draw = &icons[icon_index].draw;
|
const lcd_direct_icon_draw_t *draw = &icons[icon_index].draw;
|
||||||
const lcd_direct_icon_t *icon = icons[icon_index].icon;
|
const lcd_direct_decoded_image_t *icon = icons[icon_index].decoded;
|
||||||
if (icon == NULL || icon->width == 0U || icon->height == 0U) {
|
if (icon == NULL || !icon->valid || icon->pixels == NULL || icon->width == 0U || icon->height == 0U) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (draw->x >= LCD_DIRECT_WIDTH || draw->y >= LCD_DIRECT_HEIGHT) {
|
if (draw->x >= LCD_DIRECT_WIDTH || draw->y >= LCD_DIRECT_HEIGHT) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const lcd_direct_rle_run_t *runs = icon->runs != NULL ? icon->runs : icons[icon_index].loaded_runs;
|
|
||||||
if (runs == NULL) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t draw_width = icon->width;
|
uint16_t draw_width = icon->width;
|
||||||
uint16_t draw_height = icon->height;
|
uint16_t draw_height = icon->height;
|
||||||
if (draw_width > LCD_DIRECT_WIDTH - draw->x) {
|
if (draw_width > LCD_DIRECT_WIDTH - draw->x) {
|
||||||
@@ -460,175 +717,21 @@ static void lcd_direct_overlay_icons(uint16_t *pixel_buffer,
|
|||||||
const uint16_t image_y = y - draw->y;
|
const uint16_t image_y = y - draw->y;
|
||||||
const uint32_t icon_pixel_start = (uint32_t)image_y * icon->width;
|
const uint32_t icon_pixel_start = (uint32_t)image_y * icon->width;
|
||||||
const uint32_t icon_pixel_end = icon_pixel_start + draw_width;
|
const uint32_t icon_pixel_end = icon_pixel_start + draw_width;
|
||||||
uint32_t icon_pixel_pos = 0;
|
|
||||||
|
|
||||||
for (uint32_t run_index = 0;
|
for (uint32_t x = icon_pixel_start; x < icon_pixel_end; ++x) {
|
||||||
run_index < icon->run_count && icon_pixel_pos < icon_pixel_end;
|
const uint32_t dst_index = (uint32_t)local_y * LCD_DIRECT_WIDTH + draw->x + (x - icon_pixel_start);
|
||||||
++run_index) {
|
uint16_t color = icon->pixels[x];
|
||||||
const uint32_t run_start = icon_pixel_pos;
|
if (icon->alpha_valid && icon->alpha != NULL) {
|
||||||
const uint32_t run_end = run_start + runs[run_index].count;
|
const uint16_t dst = lcd_direct_swap_color(pixel_buffer[dst_index]);
|
||||||
if (run_end > icon_pixel_start && run_start < icon_pixel_end) {
|
color = lcd_direct_blend_rgb565(dst, color, icon->alpha[x]);
|
||||||
const uint32_t copy_start = run_start > icon_pixel_start ? run_start : icon_pixel_start;
|
|
||||||
const uint32_t copy_end = run_end < icon_pixel_end ? run_end : icon_pixel_end;
|
|
||||||
const uint16_t color = lcd_direct_swap_color(runs[run_index].color);
|
|
||||||
for (uint32_t x = copy_start; x < copy_end; ++x) {
|
|
||||||
pixel_buffer[(uint32_t)local_y * LCD_DIRECT_WIDTH + draw->x + (x - icon_pixel_start)] = color;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
icon_pixel_pos = run_end;
|
pixel_buffer[dst_index] = lcd_direct_swap_color(color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static esp_err_t lcd_direct_draw_rle_memory_image(const lcd_direct_image_t *image,
|
static esp_err_t lcd_direct_draw_png_image(const lcd_direct_image_t *image,
|
||||||
const lcd_direct_prepared_icon_t *icons,
|
|
||||||
size_t icon_count)
|
|
||||||
{
|
|
||||||
ESP_RETURN_ON_FALSE(image != NULL && image->runs != NULL,
|
|
||||||
ESP_ERR_INVALID_ARG,
|
|
||||||
TAG,
|
|
||||||
"invalid image");
|
|
||||||
uint32_t run_index = 0;
|
|
||||||
uint32_t run_remaining = 0;
|
|
||||||
uint16_t run_color = 0;
|
|
||||||
|
|
||||||
for (uint16_t row_start = 0; row_start < LCD_DIRECT_HEIGHT; row_start += LCD_DIRECT_ROWS_PER_TRANSFER) {
|
|
||||||
uint16_t row_count = LCD_DIRECT_HEIGHT - row_start;
|
|
||||||
if (row_count > LCD_DIRECT_ROWS_PER_TRANSFER) {
|
|
||||||
row_count = LCD_DIRECT_ROWS_PER_TRANSFER;
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint32_t pixels_needed = (uint32_t)LCD_DIRECT_WIDTH * row_count;
|
|
||||||
uint32_t buffered_pixels = 0;
|
|
||||||
while (buffered_pixels < pixels_needed) {
|
|
||||||
if (run_remaining == 0U) {
|
|
||||||
if (run_index >= image->run_count) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
run_color = lcd_direct_swap_color(image->runs[run_index].color);
|
|
||||||
run_remaining = image->runs[run_index].count;
|
|
||||||
run_index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t pixels_to_copy = run_remaining;
|
|
||||||
const uint32_t pixels_left = pixels_needed - buffered_pixels;
|
|
||||||
if (pixels_to_copy > pixels_left) {
|
|
||||||
pixels_to_copy = pixels_left;
|
|
||||||
}
|
|
||||||
for (uint32_t i = 0; i < pixels_to_copy; ++i) {
|
|
||||||
s_line_buffer[buffered_pixels + i] = run_color;
|
|
||||||
}
|
|
||||||
buffered_pixels += pixels_to_copy;
|
|
||||||
run_remaining -= pixels_to_copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (buffered_pixels < pixels_needed) {
|
|
||||||
s_line_buffer[buffered_pixels++] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
lcd_direct_overlay_icons(s_line_buffer, row_start, row_count, icons, icon_count);
|
|
||||||
ESP_RETURN_ON_ERROR(lcd_direct_draw_rows(row_start, row_count),
|
|
||||||
TAG,
|
|
||||||
"write image pixels failed");
|
|
||||||
}
|
|
||||||
|
|
||||||
return ESP_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t lcd_direct_draw_rle_file_image(const lcd_direct_image_t *image,
|
|
||||||
const lcd_direct_prepared_icon_t *icons,
|
|
||||||
size_t icon_count)
|
|
||||||
{
|
|
||||||
ESP_RETURN_ON_FALSE(image != NULL && image->path != NULL,
|
|
||||||
ESP_ERR_INVALID_ARG,
|
|
||||||
TAG,
|
|
||||||
"invalid file image");
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
int fd = open(image->path, O_RDONLY);
|
|
||||||
if (fd < 0) {
|
|
||||||
ESP_LOGE(TAG, "open LCD asset failed path=%s errno=%d", image->path, errno);
|
|
||||||
return ESP_ERR_NOT_FOUND;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t run_index = 0;
|
|
||||||
uint32_t chunk_index = 0;
|
|
||||||
uint32_t chunk_count = 0;
|
|
||||||
uint32_t run_remaining = 0;
|
|
||||||
uint16_t run_color = 0;
|
|
||||||
esp_err_t result = ESP_OK;
|
|
||||||
|
|
||||||
for (uint16_t row_start = 0; row_start < LCD_DIRECT_HEIGHT; row_start += LCD_DIRECT_ROWS_PER_TRANSFER) {
|
|
||||||
uint16_t row_count = LCD_DIRECT_HEIGHT - row_start;
|
|
||||||
if (row_count > LCD_DIRECT_ROWS_PER_TRANSFER) {
|
|
||||||
row_count = LCD_DIRECT_ROWS_PER_TRANSFER;
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint32_t pixels_needed = (uint32_t)LCD_DIRECT_WIDTH * row_count;
|
|
||||||
uint32_t buffered_pixels = 0;
|
|
||||||
while (buffered_pixels < pixels_needed) {
|
|
||||||
if (run_remaining == 0U) {
|
|
||||||
if (run_index >= image->run_count) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (chunk_index >= chunk_count) {
|
|
||||||
const size_t runs_left = image->run_count - run_index;
|
|
||||||
const size_t runs_to_read = runs_left < (sizeof(s_rle_file_chunk) / sizeof(s_rle_file_chunk[0])) ?
|
|
||||||
runs_left :
|
|
||||||
(sizeof(s_rle_file_chunk) / sizeof(s_rle_file_chunk[0]));
|
|
||||||
const size_t bytes_to_read = runs_to_read * sizeof(s_rle_file_chunk[0]);
|
|
||||||
ssize_t bytes_read = read(fd, s_rle_file_chunk, bytes_to_read);
|
|
||||||
if (bytes_read != (ssize_t)bytes_to_read) {
|
|
||||||
ESP_LOGE(TAG,
|
|
||||||
"read LCD asset failed path=%s requested=%u read=%d errno=%d",
|
|
||||||
image->path,
|
|
||||||
(unsigned)bytes_to_read,
|
|
||||||
(int)bytes_read,
|
|
||||||
errno);
|
|
||||||
result = ESP_FAIL;
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
chunk_index = 0;
|
|
||||||
chunk_count = runs_to_read;
|
|
||||||
}
|
|
||||||
|
|
||||||
run_color = lcd_direct_swap_color(s_rle_file_chunk[chunk_index].color);
|
|
||||||
run_remaining = s_rle_file_chunk[chunk_index].count;
|
|
||||||
chunk_index++;
|
|
||||||
run_index++;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t pixels_to_copy = run_remaining;
|
|
||||||
const uint32_t pixels_left = pixels_needed - buffered_pixels;
|
|
||||||
if (pixels_to_copy > pixels_left) {
|
|
||||||
pixels_to_copy = pixels_left;
|
|
||||||
}
|
|
||||||
for (uint32_t i = 0; i < pixels_to_copy; ++i) {
|
|
||||||
s_line_buffer[buffered_pixels + i] = run_color;
|
|
||||||
}
|
|
||||||
buffered_pixels += pixels_to_copy;
|
|
||||||
run_remaining -= pixels_to_copy;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (buffered_pixels < pixels_needed) {
|
|
||||||
s_line_buffer[buffered_pixels++] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
lcd_direct_overlay_icons(s_line_buffer, row_start, row_count, icons, icon_count);
|
|
||||||
result = lcd_direct_draw_rows(row_start, row_count);
|
|
||||||
if (result != ESP_OK) {
|
|
||||||
ESP_LOGE(TAG, "write image pixels failed path=%s", image->path);
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
out:
|
|
||||||
close(fd);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
static esp_err_t lcd_direct_draw_rle_image(const lcd_direct_image_t *image,
|
|
||||||
const lcd_direct_icon_draw_t *icons,
|
const lcd_direct_icon_draw_t *icons,
|
||||||
size_t icon_count)
|
size_t icon_count)
|
||||||
{
|
{
|
||||||
@@ -636,21 +739,164 @@ static esp_err_t lcd_direct_draw_rle_image(const lcd_direct_image_t *image,
|
|||||||
ESP_ERR_INVALID_ARG,
|
ESP_ERR_INVALID_ARG,
|
||||||
TAG,
|
TAG,
|
||||||
"invalid image");
|
"invalid image");
|
||||||
lcd_direct_prepared_icon_t prepared_icons[LCD_DIRECT_DEFAULT_ICON_COUNT];
|
ESP_RETURN_ON_FALSE(image->path != NULL,
|
||||||
|
ESP_ERR_INVALID_ARG,
|
||||||
|
TAG,
|
||||||
|
"invalid image path");
|
||||||
|
|
||||||
|
lcd_direct_decoded_image_t transient = {0};
|
||||||
|
lcd_direct_decoded_image_t *decoded = NULL;
|
||||||
|
ESP_RETURN_ON_ERROR(lcd_direct_get_png_image(image->path, false, true, &decoded, &transient),
|
||||||
|
TAG,
|
||||||
|
"load PNG image failed");
|
||||||
|
ESP_RETURN_ON_FALSE(decoded != NULL && decoded->pixels != NULL,
|
||||||
|
ESP_FAIL,
|
||||||
|
TAG,
|
||||||
|
"decoded PNG image is invalid");
|
||||||
|
if (decoded != &transient) {
|
||||||
|
decoded->pinned = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
lcd_direct_prepared_icon_t prepared_icons[LCD_DIRECT_DEFAULT_ICON_COUNT] = {0};
|
||||||
size_t prepared_icon_count = lcd_direct_prepare_icons(icons,
|
size_t prepared_icon_count = lcd_direct_prepare_icons(icons,
|
||||||
icon_count,
|
icon_count,
|
||||||
prepared_icons,
|
prepared_icons,
|
||||||
sizeof(prepared_icons) / sizeof(prepared_icons[0]));
|
sizeof(prepared_icons) / sizeof(prepared_icons[0]));
|
||||||
esp_err_t err = ESP_OK;
|
esp_err_t err = ESP_OK;
|
||||||
if (image->runs != NULL) {
|
|
||||||
err = lcd_direct_draw_rle_memory_image(image, prepared_icons, prepared_icon_count);
|
for (uint16_t row_start = 0; row_start < LCD_DIRECT_HEIGHT; row_start += LCD_DIRECT_ROWS_PER_TRANSFER) {
|
||||||
} else {
|
uint16_t row_count = LCD_DIRECT_HEIGHT - row_start;
|
||||||
err = lcd_direct_draw_rle_file_image(image, prepared_icons, prepared_icon_count);
|
if (row_count > LCD_DIRECT_ROWS_PER_TRANSFER) {
|
||||||
|
row_count = LCD_DIRECT_ROWS_PER_TRANSFER;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint16_t y = 0; y < row_count; ++y) {
|
||||||
|
const uint32_t dst_row_offset = (uint32_t)y * LCD_DIRECT_WIDTH;
|
||||||
|
const uint32_t src_y = row_start + y;
|
||||||
|
for (uint16_t x = 0; x < LCD_DIRECT_WIDTH; ++x) {
|
||||||
|
uint16_t color = 0;
|
||||||
|
if (x < decoded->width && src_y < decoded->height) {
|
||||||
|
color = decoded->pixels[(uint32_t)src_y * decoded->width + x];
|
||||||
|
}
|
||||||
|
s_line_buffer[dst_row_offset + x] = lcd_direct_swap_color(color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lcd_direct_overlay_icons(s_line_buffer, row_start, row_count, prepared_icons, prepared_icon_count);
|
||||||
|
err = lcd_direct_draw_rows(row_start, row_count);
|
||||||
|
if (err != ESP_OK) {
|
||||||
|
ESP_LOGE(TAG, "write PNG image pixels failed path=%s", image->path);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
lcd_direct_release_prepared_icons(prepared_icons, prepared_icon_count);
|
lcd_direct_release_prepared_icons(prepared_icons, prepared_icon_count);
|
||||||
|
if (decoded == &transient) {
|
||||||
|
lcd_direct_release_decoded_image(&transient);
|
||||||
|
} else {
|
||||||
|
decoded->pinned = false;
|
||||||
|
}
|
||||||
return err;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static esp_err_t lcd_direct_preheat_image(const lcd_direct_image_t *image)
|
||||||
|
{
|
||||||
|
ESP_RETURN_ON_FALSE(image != NULL && image->path != NULL,
|
||||||
|
ESP_ERR_INVALID_ARG,
|
||||||
|
TAG,
|
||||||
|
"invalid preheat image");
|
||||||
|
lcd_direct_decoded_image_t transient = {0};
|
||||||
|
lcd_direct_decoded_image_t *decoded = NULL;
|
||||||
|
esp_err_t err = lcd_direct_get_png_image(image->path, false, true, &decoded, &transient);
|
||||||
|
if (decoded == &transient) {
|
||||||
|
lcd_direct_release_decoded_image(&transient);
|
||||||
|
}
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t lcd_direct_preheat_asset_sequence(const lcd_direct_request_t *request)
|
||||||
|
{
|
||||||
|
ESP_RETURN_ON_FALSE(request != NULL,
|
||||||
|
ESP_ERR_INVALID_ARG,
|
||||||
|
TAG,
|
||||||
|
"invalid preheat request");
|
||||||
|
const size_t frame_count = request->frame_order_count > 0U ? request->frame_order_count : 1U;
|
||||||
|
for (size_t i = 0; i < frame_count; ++i) {
|
||||||
|
const uint8_t frame_index = request->frame_order_count > 0U ? request->frame_order[i] : (uint8_t)i;
|
||||||
|
const lcd_direct_image_t *frame = lcd_direct_asset_frame(request->asset_dir, frame_index);
|
||||||
|
if (frame == NULL) {
|
||||||
|
return ESP_ERR_NOT_FOUND;
|
||||||
|
}
|
||||||
|
ESP_RETURN_ON_ERROR(lcd_direct_preheat_image(frame), TAG, "preheat PNG frame failed");
|
||||||
|
}
|
||||||
|
return ESP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static esp_err_t lcd_direct_preheat_basic_face(void)
|
||||||
|
{
|
||||||
|
const lcd_direct_request_t request = {
|
||||||
|
.kind = PLATFORM_LCD_PRESENTATION_ASSET_SEQUENCE,
|
||||||
|
.asset_dir = "jichubiaoqing",
|
||||||
|
.frame_order = {0, 1, 2},
|
||||||
|
.frame_order_count = 3,
|
||||||
|
};
|
||||||
|
return lcd_direct_preheat_asset_sequence(&request);
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t lcd_direct_asset_sequence_frame_ms(const lcd_direct_request_t *request)
|
||||||
|
{
|
||||||
|
return (request != NULL && request->frame_ms > 0U) ? request->frame_ms : 100U;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t lcd_direct_asset_sequence_hold_ms(const lcd_direct_request_t *request,
|
||||||
|
size_t frame_index,
|
||||||
|
size_t frame_count)
|
||||||
|
{
|
||||||
|
const uint32_t frame_ms = lcd_direct_asset_sequence_frame_ms(request);
|
||||||
|
if (request != NULL && strcmp(request->asset_dir, "jichubiaoqing") == 0) {
|
||||||
|
return (frame_index + 1U == frame_count) ?
|
||||||
|
LCD_DIRECT_BASIC_FACE_LAST_FRAME_HOLD_MS :
|
||||||
|
LCD_DIRECT_BASIC_FACE_FRAME_HOLD_MS;
|
||||||
|
}
|
||||||
|
if (frame_index + 1U != frame_count) {
|
||||||
|
return frame_ms;
|
||||||
|
}
|
||||||
|
return frame_ms + LCD_DIRECT_LAST_FRAME_EXTRA_HOLD_MS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint32_t lcd_direct_asset_sequence_duration_ms(const lcd_direct_request_t *request)
|
||||||
|
{
|
||||||
|
if (request == NULL || request->duration_ms == 0U) {
|
||||||
|
return 0U;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t frame_count = request->frame_order_count > 0U ? request->frame_order_count : 1U;
|
||||||
|
const uint32_t frame_ms = lcd_direct_asset_sequence_frame_ms(request);
|
||||||
|
const uint64_t base_cycle_ms = (uint64_t)frame_count * (uint64_t)frame_ms;
|
||||||
|
if (base_cycle_ms == 0U) {
|
||||||
|
return request->duration_ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint64_t completed_cycles = (uint64_t)request->duration_ms / base_cycle_ms;
|
||||||
|
uint32_t last_frame_extra_ms = LCD_DIRECT_LAST_FRAME_EXTRA_HOLD_MS;
|
||||||
|
if (strcmp(request->asset_dir, "jichubiaoqing") == 0) {
|
||||||
|
const uint64_t base_cycle_ms = (uint64_t)frame_count * (uint64_t)frame_ms;
|
||||||
|
const uint64_t adjusted_cycle_ms = frame_count > 0U ?
|
||||||
|
((uint64_t)(frame_count - 1U) *
|
||||||
|
(uint64_t)LCD_DIRECT_BASIC_FACE_FRAME_HOLD_MS) +
|
||||||
|
(uint64_t)LCD_DIRECT_BASIC_FACE_LAST_FRAME_HOLD_MS :
|
||||||
|
base_cycle_ms;
|
||||||
|
last_frame_extra_ms = adjusted_cycle_ms > base_cycle_ms ?
|
||||||
|
(uint32_t)(adjusted_cycle_ms - base_cycle_ms) :
|
||||||
|
0U;
|
||||||
|
}
|
||||||
|
uint64_t adjusted_duration_ms = (uint64_t)request->duration_ms +
|
||||||
|
(completed_cycles * (uint64_t)last_frame_extra_ms);
|
||||||
|
if (adjusted_duration_ms > UINT32_MAX) {
|
||||||
|
adjusted_duration_ms = UINT32_MAX;
|
||||||
|
}
|
||||||
|
return (uint32_t)adjusted_duration_ms;
|
||||||
|
}
|
||||||
|
|
||||||
static esp_err_t lcd_direct_draw_asset_sequence_once(const lcd_direct_request_t *request)
|
static esp_err_t lcd_direct_draw_asset_sequence_once(const lcd_direct_request_t *request)
|
||||||
{
|
{
|
||||||
ESP_RETURN_ON_FALSE(request != NULL,
|
ESP_RETURN_ON_FALSE(request != NULL,
|
||||||
@@ -677,12 +923,12 @@ static esp_err_t lcd_direct_draw_asset_sequence_once(const lcd_direct_request_t
|
|||||||
request->asset_dir,
|
request->asset_dir,
|
||||||
(unsigned)frame_index);
|
(unsigned)frame_index);
|
||||||
|
|
||||||
ESP_RETURN_ON_ERROR(lcd_direct_draw_rle_image(frame, NULL, 0),
|
ESP_RETURN_ON_ERROR(lcd_direct_draw_png_image(frame, NULL, 0),
|
||||||
TAG,
|
TAG,
|
||||||
"draw asset frame failed");
|
"draw asset frame failed");
|
||||||
|
|
||||||
const uint32_t frame_ms = request->frame_ms > 0U ? request->frame_ms : 100U;
|
const uint32_t hold_ms = lcd_direct_asset_sequence_hold_ms(request, i, frame_count);
|
||||||
vTaskDelay(pdMS_TO_TICKS(frame_ms));
|
vTaskDelay(pdMS_TO_TICKS(hold_ms));
|
||||||
}
|
}
|
||||||
|
|
||||||
static TickType_t s_last_draw_success_tick;
|
static TickType_t s_last_draw_success_tick;
|
||||||
@@ -728,7 +974,7 @@ static esp_err_t lcd_direct_draw_asset_sequence_for_duration(const lcd_direct_re
|
|||||||
request->asset_dir,
|
request->asset_dir,
|
||||||
(unsigned)frame_index);
|
(unsigned)frame_index);
|
||||||
|
|
||||||
ESP_RETURN_ON_ERROR(lcd_direct_draw_rle_image(frame, NULL, 0),
|
ESP_RETURN_ON_ERROR(lcd_direct_draw_png_image(frame, NULL, 0),
|
||||||
TAG,
|
TAG,
|
||||||
"draw timed asset frame failed");
|
"draw timed asset frame failed");
|
||||||
|
|
||||||
@@ -738,8 +984,8 @@ static esp_err_t lcd_direct_draw_asset_sequence_for_duration(const lcd_direct_re
|
|||||||
return ESP_OK;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
const uint32_t frame_ms = request->frame_ms > 0U ? request->frame_ms : 100U;
|
const uint32_t hold_ms = lcd_direct_asset_sequence_hold_ms(request, i, frame_count);
|
||||||
const TickType_t frame_ticks = pdMS_TO_TICKS(frame_ms);
|
const TickType_t frame_ticks = pdMS_TO_TICKS(hold_ms);
|
||||||
const TickType_t remaining_ticks = duration_ticks - elapsed_ticks;
|
const TickType_t remaining_ticks = duration_ticks - elapsed_ticks;
|
||||||
vTaskDelay(frame_ticks < remaining_ticks ? frame_ticks : remaining_ticks);
|
vTaskDelay(frame_ticks < remaining_ticks ? frame_ticks : remaining_ticks);
|
||||||
}
|
}
|
||||||
@@ -776,7 +1022,7 @@ static esp_err_t lcd_direct_play_asset_sequence(const lcd_direct_request_t *requ
|
|||||||
|
|
||||||
if (request->duration_ms > 0U) {
|
if (request->duration_ms > 0U) {
|
||||||
const TickType_t start_tick = xTaskGetTickCount();
|
const TickType_t start_tick = xTaskGetTickCount();
|
||||||
const TickType_t duration_ticks = pdMS_TO_TICKS(request->duration_ms);
|
const TickType_t duration_ticks = pdMS_TO_TICKS(lcd_direct_asset_sequence_duration_ms(request));
|
||||||
esp_err_t err = lcd_direct_draw_asset_sequence_for_duration(request, start_tick, duration_ticks);
|
esp_err_t err = lcd_direct_draw_asset_sequence_for_duration(request, start_tick, duration_ticks);
|
||||||
if (err != ESP_OK) {
|
if (err != ESP_OK) {
|
||||||
return err;
|
return err;
|
||||||
@@ -838,7 +1084,7 @@ static esp_err_t lcd_direct_play_step_sequence(const lcd_direct_request_t *reque
|
|||||||
icon_count = 1;
|
icon_count = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_RETURN_ON_ERROR(lcd_direct_draw_rle_image(frame, icons, icon_count),
|
ESP_RETURN_ON_ERROR(lcd_direct_draw_png_image(frame, icons, icon_count),
|
||||||
TAG,
|
TAG,
|
||||||
"draw step frame failed");
|
"draw step frame failed");
|
||||||
|
|
||||||
@@ -956,6 +1202,10 @@ void lcd_direct_start_render_loop(void)
|
|||||||
portEXIT_CRITICAL(&s_state_lock);
|
portEXIT_CRITICAL(&s_state_lock);
|
||||||
|
|
||||||
ESP_LOGI(TAG, "Direct LCD render loop started");
|
ESP_LOGI(TAG, "Direct LCD render loop started");
|
||||||
|
esp_err_t preheat_err = lcd_direct_preheat_basic_face();
|
||||||
|
if (preheat_err != ESP_OK) {
|
||||||
|
ESP_LOGW(TAG, "Default LCD face PNG preheat failed: %s", esp_err_to_name(preheat_err));
|
||||||
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
bool stop = false;
|
bool stop = false;
|
||||||
@@ -1122,8 +1372,41 @@ esp_err_t lcd_direct_request_presentation(const platform_lcd_presentation_reques
|
|||||||
|
|
||||||
esp_err_t lcd_direct_preheat_presentation(const platform_lcd_presentation_request_t *request)
|
esp_err_t lcd_direct_preheat_presentation(const platform_lcd_presentation_request_t *request)
|
||||||
{
|
{
|
||||||
(void)request;
|
if (!s_initialized || request == NULL) {
|
||||||
return s_initialized ? ESP_OK : ESP_ERR_INVALID_STATE;
|
return ESP_ERR_INVALID_STATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
lcd_direct_request_t copied = {
|
||||||
|
.kind = request->kind,
|
||||||
|
.priority = request->priority,
|
||||||
|
.interrupt_current = request->interrupt_current,
|
||||||
|
.return_to_idle = request->return_to_idle,
|
||||||
|
.repeat_count = request->repeat_count,
|
||||||
|
.frame_ms = request->frame_ms,
|
||||||
|
.duration_ms = request->duration_ms,
|
||||||
|
};
|
||||||
|
if (request->asset_dir != NULL) {
|
||||||
|
strlcpy(copied.asset_dir, request->asset_dir, sizeof(copied.asset_dir));
|
||||||
|
}
|
||||||
|
if (request->frame_order != NULL && request->frame_order_count > 0U) {
|
||||||
|
copied.frame_order_count = request->frame_order_count;
|
||||||
|
if (copied.frame_order_count > PLATFORM_LCD_PRESENTATION_FRAME_ORDER_MAX) {
|
||||||
|
copied.frame_order_count = PLATFORM_LCD_PRESENTATION_FRAME_ORDER_MAX;
|
||||||
|
}
|
||||||
|
memcpy(copied.frame_order, request->frame_order, copied.frame_order_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (xSemaphoreTake(s_lock, pdMS_TO_TICKS(1000)) != pdTRUE) {
|
||||||
|
return ESP_ERR_TIMEOUT;
|
||||||
|
}
|
||||||
|
esp_err_t err = ESP_ERR_INVALID_ARG;
|
||||||
|
if (copied.kind == PLATFORM_LCD_PRESENTATION_IDLE) {
|
||||||
|
err = lcd_direct_preheat_basic_face();
|
||||||
|
} else if (copied.kind == PLATFORM_LCD_PRESENTATION_ASSET_SEQUENCE) {
|
||||||
|
err = lcd_direct_preheat_asset_sequence(&copied);
|
||||||
|
}
|
||||||
|
xSemaphoreGive(s_lock);
|
||||||
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t lcd_direct_presentation_completion_count(void)
|
uint32_t lcd_direct_presentation_completion_count(void)
|
||||||
|
|||||||
@@ -1,226 +1,175 @@
|
|||||||
#include "lcd_direct_assets.h"
|
#include "lcd_direct_assets.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#define LCD_ASSET_BASE_PATH "/littlefs/lcd/"
|
#define LCD_ASSET_BASE_PATH "/littlefs/lcd/"
|
||||||
|
|
||||||
static const lcd_direct_image_t s_default_image = {
|
static const lcd_direct_image_t s_default_image = {
|
||||||
.path = LCD_ASSET_BASE_PATH "buyunqian1.rle",
|
.path = LCD_ASSET_BASE_PATH "buyunqian1.png",
|
||||||
.run_count = 72552U,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const lcd_direct_image_t s_basic_face_frames[] = {
|
static const lcd_direct_image_t s_basic_face_frames[] = {
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "jichubiaoqing0.rle",
|
.path = LCD_ASSET_BASE_PATH "jichubiaoqing0.png",
|
||||||
.run_count = 5411U,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "jichubiaoqing1.rle",
|
.path = LCD_ASSET_BASE_PATH "jichubiaoqing1.png",
|
||||||
.run_count = 4099U,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "jichubiaoqing2.rle",
|
.path = LCD_ASSET_BASE_PATH "jichubiaoqing2.png",
|
||||||
.run_count = 3169U,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const lcd_direct_image_t s_muyu_frames[] = {
|
static const lcd_direct_image_t s_muyu_frames[] = {
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "muyu0.rle",
|
.path = LCD_ASSET_BASE_PATH "muyu0.png",
|
||||||
.run_count = 63459U,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "muyu1.rle",
|
.path = LCD_ASSET_BASE_PATH "muyu1.png",
|
||||||
.run_count = 63665U,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "muyu2.rle",
|
.path = LCD_ASSET_BASE_PATH "muyu2.png",
|
||||||
.run_count = 63894U,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const lcd_direct_image_t s_chouqian_qianzhi_frames[] = {
|
static const lcd_direct_image_t s_chouqian_qianzhi_frames[] = {
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "chouqian_qianzhi0.rle",
|
.path = LCD_ASSET_BASE_PATH "chouqian_qianzhi0.png",
|
||||||
.run_count = 74056U,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "chouqian_qianzhi1.rle",
|
.path = LCD_ASSET_BASE_PATH "chouqian_qianzhi1.png",
|
||||||
.run_count = 73259U,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "chouqian_qianzhi2.rle",
|
.path = LCD_ASSET_BASE_PATH "chouqian_qianzhi2.png",
|
||||||
.run_count = 72906U,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const lcd_direct_image_t s_chouqian_buyun_frames[] = {
|
static const lcd_direct_image_t s_chouqian_buyun_frames[] = {
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "chouqian_buyun0.rle",
|
.path = LCD_ASSET_BASE_PATH "chouqian_buyun0.png",
|
||||||
.run_count = 72370U,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "chouqian_buyun1.rle",
|
.path = LCD_ASSET_BASE_PATH "chouqian_buyun1.png",
|
||||||
.run_count = 72552U,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "chouqian_buyun2.rle",
|
.path = LCD_ASSET_BASE_PATH "chouqian_buyun2.png",
|
||||||
.run_count = 72757U,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const lcd_direct_image_t s_chouqian_heping_frames[] = {
|
static const lcd_direct_image_t s_chouqian_heping_frames[] = {
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "chouqian_heping0.rle",
|
.path = LCD_ASSET_BASE_PATH "chouqian_heping0.png",
|
||||||
.run_count = 72370U,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "chouqian_heping1.rle",
|
.path = LCD_ASSET_BASE_PATH "chouqian_heping1.png",
|
||||||
.run_count = 73049U,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "chouqian_heping2.rle",
|
.path = LCD_ASSET_BASE_PATH "chouqian_heping2.png",
|
||||||
.run_count = 73365U,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const lcd_direct_image_t s_chouqian_shangshang_frames[] = {
|
static const lcd_direct_image_t s_chouqian_shangshang_frames[] = {
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "chouqian_shangshang0.rle",
|
.path = LCD_ASSET_BASE_PATH "chouqian_shangshang0.png",
|
||||||
.run_count = 72370U,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "chouqian_shangshang1.rle",
|
.path = LCD_ASSET_BASE_PATH "chouqian_shangshang1.png",
|
||||||
.run_count = 72944U,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "chouqian_shangshang2.rle",
|
.path = LCD_ASSET_BASE_PATH "chouqian_shangshang2.png",
|
||||||
.run_count = 73190U,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const lcd_direct_image_t s_chouqian_xiaoji_frames[] = {
|
static const lcd_direct_image_t s_chouqian_xiaoji_frames[] = {
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "chouqian_xiaoji0.rle",
|
.path = LCD_ASSET_BASE_PATH "chouqian_xiaoji0.png",
|
||||||
.run_count = 72370U,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "chouqian_xiaoji1.rle",
|
.path = LCD_ASSET_BASE_PATH "chouqian_xiaoji1.png",
|
||||||
.run_count = 72827U,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "chouqian_xiaoji2.rle",
|
.path = LCD_ASSET_BASE_PATH "chouqian_xiaoji2.png",
|
||||||
.run_count = 73006U,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const lcd_direct_image_t s_chouqian_zhongshang_frames[] = {
|
static const lcd_direct_image_t s_chouqian_zhongshang_frames[] = {
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "chouqian_zhongshang0.rle",
|
.path = LCD_ASSET_BASE_PATH "chouqian_zhongshang0.png",
|
||||||
.run_count = 72370U,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "chouqian_zhongshang1.rle",
|
.path = LCD_ASSET_BASE_PATH "chouqian_zhongshang1.png",
|
||||||
.run_count = 72135U,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "chouqian_zhongshang2.rle",
|
.path = LCD_ASSET_BASE_PATH "chouqian_zhongshang2.png",
|
||||||
.run_count = 72292U,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const lcd_direct_image_t s_qianzhiwuxing_frames[] = {
|
static const lcd_direct_image_t s_qianzhiwuxing_frames[] = {
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "qianzhiwuxing0.rle",
|
.path = LCD_ASSET_BASE_PATH "qianzhiwuxing0.png",
|
||||||
.run_count = 4157U,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const lcd_direct_image_t s_shui_frames[] = {
|
static const lcd_direct_image_t s_shui_frames[] = {
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "shui0.rle",
|
.path = LCD_ASSET_BASE_PATH "shui0.png",
|
||||||
.run_count = 74146U,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "shui1.rle",
|
.path = LCD_ASSET_BASE_PATH "shui1.png",
|
||||||
.run_count = 71868U,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const lcd_direct_image_t s_mu_frames[] = {
|
static const lcd_direct_image_t s_mu_frames[] = {
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "mu0.rle",
|
.path = LCD_ASSET_BASE_PATH "mu0.png",
|
||||||
.run_count = 73537U,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "mu1.rle",
|
.path = LCD_ASSET_BASE_PATH "mu1.png",
|
||||||
.run_count = 69337U,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const lcd_direct_image_t s_huo_frames[] = {
|
static const lcd_direct_image_t s_huo_frames[] = {
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "huo0.rle",
|
.path = LCD_ASSET_BASE_PATH "huo0.png",
|
||||||
.run_count = 74455U,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "huo1.rle",
|
.path = LCD_ASSET_BASE_PATH "huo1.png",
|
||||||
.run_count = 72474U,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const lcd_direct_image_t s_tu_frames[] = {
|
static const lcd_direct_image_t s_tu_frames[] = {
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "tu0.rle",
|
.path = LCD_ASSET_BASE_PATH "tu0.png",
|
||||||
.run_count = 95829U,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "tu1.rle",
|
.path = LCD_ASSET_BASE_PATH "tu1.png",
|
||||||
.run_count = 71964U,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const lcd_direct_image_t s_jin_frames[] = {
|
static const lcd_direct_image_t s_jin_frames[] = {
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "jin0.rle",
|
.path = LCD_ASSET_BASE_PATH "jin0.png",
|
||||||
.run_count = 75151U,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "jin1.rle",
|
.path = LCD_ASSET_BASE_PATH "jin1.png",
|
||||||
.run_count = 72012U,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
static const lcd_direct_icon_t s_wuxing_icons[] = {
|
static const lcd_direct_icon_t s_wuxing_icons[] = {
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "icon_shui0.rle",
|
.path = LCD_ASSET_BASE_PATH "icon_shui0.png",
|
||||||
.run_count = 549U,
|
|
||||||
.width = 60U,
|
|
||||||
.height = 56U,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "icon_mu0.rle",
|
.path = LCD_ASSET_BASE_PATH "icon_mu0.png",
|
||||||
.run_count = 1005U,
|
|
||||||
.width = 86U,
|
|
||||||
.height = 50U,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "icon_huo0.rle",
|
.path = LCD_ASSET_BASE_PATH "icon_huo0.png",
|
||||||
.run_count = 565U,
|
|
||||||
.width = 56U,
|
|
||||||
.height = 58U,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "icon_tu0.rle",
|
.path = LCD_ASSET_BASE_PATH "icon_tu0.png",
|
||||||
.run_count = 754U,
|
|
||||||
.width = 75U,
|
|
||||||
.height = 42U,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.path = LCD_ASSET_BASE_PATH "icon_jin0.rle",
|
.path = LCD_ASSET_BASE_PATH "icon_jin0.png",
|
||||||
.run_count = 605U,
|
|
||||||
.width = 65U,
|
|
||||||
.height = 44U,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
Copyright (c) 2005-2018 Lode Vandevenne
|
||||||
|
|
||||||
|
This software is provided 'as-is', without any express or implied
|
||||||
|
warranty. In no event will the authors be held liable for any damages
|
||||||
|
arising from the use of this software.
|
||||||
|
|
||||||
|
Permission is granted to anyone to use this software for any purpose,
|
||||||
|
including commercial applications, and to alter it and redistribute it
|
||||||
|
freely, subject to the following restrictions:
|
||||||
|
|
||||||
|
1. The origin of this software must not be misrepresented; you must not
|
||||||
|
claim that you wrote the original software. If you use this software
|
||||||
|
in a product, an acknowledgment in the product documentation would be
|
||||||
|
appreciated but is not required.
|
||||||
|
|
||||||
|
2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
misrepresented as being the original software.
|
||||||
|
|
||||||
|
3. This notice may not be removed or altered from any source
|
||||||
|
distribution.
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "esp_heap_caps.h"
|
||||||
|
#include "soc/soc_caps.h"
|
||||||
|
|
||||||
|
void *lodepng_malloc(size_t size)
|
||||||
|
{
|
||||||
|
#if SOC_SPIRAM_SUPPORTED
|
||||||
|
void *ptr = heap_caps_malloc(size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||||
|
if (ptr != NULL) {
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return malloc(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *lodepng_realloc(void *ptr, size_t new_size)
|
||||||
|
{
|
||||||
|
#if SOC_SPIRAM_SUPPORTED
|
||||||
|
void *new_ptr = heap_caps_realloc(ptr, new_size, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
|
||||||
|
if (new_ptr != NULL) {
|
||||||
|
return new_ptr;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return realloc(ptr, new_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lodepng_free(void *ptr)
|
||||||
|
{
|
||||||
|
free(ptr);
|
||||||
|
}
|
||||||