feat(platform): migrate LCD assets to PNG and expand flash partitions

This commit is contained in:
chenjintao
2026-08-10 16:04:23 +08:00
parent 42187ba21d
commit d9660164a6
105 changed files with 10103 additions and 527 deletions
+19 -9
View File
@@ -13,6 +13,9 @@ set(platform_srcs
"src/platform_io_effects.c"
"src/lcd_direct.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/transport_ws.c"
"src/wifi_portal_bridge.cpp"
@@ -56,10 +59,17 @@ idf_component_register(
"include"
PRIV_INCLUDE_DIRS
"internal"
"third_party/lodepng"
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_lcd_asset_src_dir "${CMAKE_CURRENT_SOURCE_DIR}/assets/lcd")
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
"${platform_audio_prompt_src_dir}/*.opus"
)
set(platform_lcd_flash_assets
set(platform_lcd_png_assets
buyunqian1
jichubiaoqing0
jichubiaoqing1
@@ -110,10 +120,10 @@ set(platform_lcd_flash_assets
icon_tu0
icon_jin0
)
set(platform_lcd_flash_asset_files)
foreach(lcd_rle_asset IN LISTS platform_lcd_flash_assets)
list(APPEND platform_lcd_flash_asset_files
"${platform_lcd_asset_src_dir}/${lcd_rle_asset}.rle"
set(platform_lcd_png_asset_files)
foreach(lcd_png_asset IN LISTS platform_lcd_png_assets)
list(APPEND platform_lcd_png_asset_files
"${platform_lcd_asset_src_dir}/${lcd_png_asset}.png"
)
endforeach()
@@ -127,12 +137,12 @@ foreach(audio_file IN LISTS platform_audio_prompt_files)
"${platform_asset_image_dir}/audio_prompts/${audio_rel_path}"
)
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
COMMAND ${CMAKE_COMMAND} -E make_directory "${platform_asset_image_dir}/lcd"
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${platform_lcd_asset_src_dir}/${lcd_rle_asset}.rle"
"${platform_asset_image_dir}/lcd/${lcd_rle_asset}.rle"
"${platform_lcd_asset_src_dir}/${lcd_png_asset}.png"
"${platform_asset_image_dir}/lcd/${lcd_png_asset}.png"
)
endforeach()
@@ -144,7 +154,7 @@ add_custom_command(
COMMAND ${CMAKE_COMMAND} -E touch "${platform_asset_stage_stamp}"
DEPENDS
${platform_audio_prompt_files}
${platform_lcd_flash_asset_files}
${platform_lcd_png_asset_files}
)
littlefs_create_partition_image(
Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 283 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 283 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 283 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 284 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 283 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 285 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 287 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 289 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 285 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 283 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 285 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 283 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 284 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 285 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 283 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 282 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 282 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 291 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 283 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 294 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 281 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 287 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 271 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 249 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 250 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 290 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 281 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 374 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 281 KiB

@@ -8,22 +8,11 @@ extern "C" {
#endif
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;
} lcd_direct_image_t;
typedef struct {
const lcd_direct_rle_run_t *runs;
uint32_t run_count;
const char *path;
uint16_t width;
uint16_t height;
} lcd_direct_icon_t;
typedef struct {
+525 -242
View File
@@ -24,10 +24,12 @@
#include "esp_lcd_panel_ops.h"
#include "esp_lcd_panel_vendor.h"
#include "esp_log.h"
#include "esp_heap_caps.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
#include "sdkconfig.h"
#include "soc/soc_caps.h"
#define LCD_DIRECT_SPI_HOST SPI2_HOST
#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_PRESENTATION_WAIT_POLL_MS 20U
#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_RAMCTRL 0xB0
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 {
platform_lcd_presentation_kind_t kind;
uint8_t priority;
@@ -64,10 +78,25 @@ typedef struct {
size_t step_count;
} 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 {
lcd_direct_icon_draw_t draw;
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;
typedef struct {
@@ -107,7 +136,8 @@ static lcd_direct_request_t s_pending;
static uint32_t s_completion_count;
static portMUX_TYPE s_state_lock = portMUX_INITIALIZER_UNLOCKED;
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);
@@ -160,6 +190,264 @@ static esp_err_t lcd_direct_draw_rows(uint16_t row_start, uint16_t row_count)
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)
{
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;
}
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,
size_t icon_count,
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) {
const lcd_direct_icon_draw_t *draw = &icons[i];
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;
}
if (draw->x >= LCD_DIRECT_WIDTH || draw->y >= LCD_DIRECT_HEIGHT) {
continue;
}
lcd_direct_rle_run_t *loaded_runs = NULL;
if (icon->runs == NULL) {
loaded_runs = lcd_direct_load_icon_runs(icon);
if (loaded_runs == NULL) {
continue;
}
lcd_direct_prepared_icon_t *slot = &prepared[prepared_count];
lcd_direct_decoded_image_t *decoded = NULL;
esp_err_t decode_err = lcd_direct_get_png_image(icon->path,
true,
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) {
.draw = *draw,
.icon = icon,
.loaded_runs = loaded_runs,
};
slot->draw = *draw;
slot->icon = icon;
slot->decoded = decoded;
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) {
free(prepared[i].loaded_runs);
prepared[i].loaded_runs = NULL;
if (prepared[i].cached != 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;
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_t *icon = icons[icon_index].icon;
if (icon == NULL || icon->width == 0U || icon->height == 0U) {
const lcd_direct_decoded_image_t *icon = icons[icon_index].decoded;
if (icon == NULL || !icon->valid || icon->pixels == NULL || icon->width == 0U || icon->height == 0U) {
continue;
}
if (draw->x >= LCD_DIRECT_WIDTH || draw->y >= LCD_DIRECT_HEIGHT) {
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_height = icon->height;
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 uint32_t icon_pixel_start = (uint32_t)image_y * icon->width;
const uint32_t icon_pixel_end = icon_pixel_start + draw_width;
uint32_t icon_pixel_pos = 0;
for (uint32_t run_index = 0;
run_index < icon->run_count && icon_pixel_pos < icon_pixel_end;
++run_index) {
const uint32_t run_start = icon_pixel_pos;
const uint32_t run_end = run_start + runs[run_index].count;
if (run_end > icon_pixel_start && run_start < icon_pixel_end) {
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;
}
for (uint32_t x = icon_pixel_start; x < icon_pixel_end; ++x) {
const uint32_t dst_index = (uint32_t)local_y * LCD_DIRECT_WIDTH + draw->x + (x - icon_pixel_start);
uint16_t color = icon->pixels[x];
if (icon->alpha_valid && icon->alpha != NULL) {
const uint16_t dst = lcd_direct_swap_color(pixel_buffer[dst_index]);
color = lcd_direct_blend_rgb565(dst, color, icon->alpha[x]);
}
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,
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,
static esp_err_t lcd_direct_draw_png_image(const lcd_direct_image_t *image,
const lcd_direct_icon_draw_t *icons,
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,
TAG,
"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,
icon_count,
prepared_icons,
sizeof(prepared_icons) / sizeof(prepared_icons[0]));
esp_err_t err = ESP_OK;
if (image->runs != NULL) {
err = lcd_direct_draw_rle_memory_image(image, prepared_icons, prepared_icon_count);
} else {
err = lcd_direct_draw_rle_file_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) {
uint16_t row_count = LCD_DIRECT_HEIGHT - row_start;
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);
if (decoded == &transient) {
lcd_direct_release_decoded_image(&transient);
} else {
decoded->pinned = false;
}
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)
{
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,
(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,
"draw asset frame failed");
const uint32_t frame_ms = request->frame_ms > 0U ? request->frame_ms : 100U;
vTaskDelay(pdMS_TO_TICKS(frame_ms));
const uint32_t hold_ms = lcd_direct_asset_sequence_hold_ms(request, i, frame_count);
vTaskDelay(pdMS_TO_TICKS(hold_ms));
}
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,
(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,
"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;
}
const uint32_t frame_ms = request->frame_ms > 0U ? request->frame_ms : 100U;
const TickType_t frame_ticks = pdMS_TO_TICKS(frame_ms);
const uint32_t hold_ms = lcd_direct_asset_sequence_hold_ms(request, i, frame_count);
const TickType_t frame_ticks = pdMS_TO_TICKS(hold_ms);
const TickType_t remaining_ticks = duration_ticks - elapsed_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) {
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);
if (err != ESP_OK) {
return err;
@@ -838,7 +1084,7 @@ static esp_err_t lcd_direct_play_step_sequence(const lcd_direct_request_t *reque
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,
"draw step frame failed");
@@ -956,6 +1202,10 @@ void lcd_direct_start_render_loop(void)
portEXIT_CRITICAL(&s_state_lock);
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) {
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)
{
(void)request;
return s_initialized ? ESP_OK : ESP_ERR_INVALID_STATE;
if (!s_initialized || request == NULL) {
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)
+42 -93
View File
@@ -1,226 +1,175 @@
#include "lcd_direct_assets.h"
#include "lcd_direct_assets.h"
#include <string.h>
#define LCD_ASSET_BASE_PATH "/littlefs/lcd/"
static const lcd_direct_image_t s_default_image = {
.path = LCD_ASSET_BASE_PATH "buyunqian1.rle",
.run_count = 72552U,
.path = LCD_ASSET_BASE_PATH "buyunqian1.png",
};
static const lcd_direct_image_t s_basic_face_frames[] = {
{
.path = LCD_ASSET_BASE_PATH "jichubiaoqing0.rle",
.run_count = 5411U,
.path = LCD_ASSET_BASE_PATH "jichubiaoqing0.png",
},
{
.path = LCD_ASSET_BASE_PATH "jichubiaoqing1.rle",
.run_count = 4099U,
.path = LCD_ASSET_BASE_PATH "jichubiaoqing1.png",
},
{
.path = LCD_ASSET_BASE_PATH "jichubiaoqing2.rle",
.run_count = 3169U,
.path = LCD_ASSET_BASE_PATH "jichubiaoqing2.png",
},
};
static const lcd_direct_image_t s_muyu_frames[] = {
{
.path = LCD_ASSET_BASE_PATH "muyu0.rle",
.run_count = 63459U,
.path = LCD_ASSET_BASE_PATH "muyu0.png",
},
{
.path = LCD_ASSET_BASE_PATH "muyu1.rle",
.run_count = 63665U,
.path = LCD_ASSET_BASE_PATH "muyu1.png",
},
{
.path = LCD_ASSET_BASE_PATH "muyu2.rle",
.run_count = 63894U,
.path = LCD_ASSET_BASE_PATH "muyu2.png",
},
};
static const lcd_direct_image_t s_chouqian_qianzhi_frames[] = {
{
.path = LCD_ASSET_BASE_PATH "chouqian_qianzhi0.rle",
.run_count = 74056U,
.path = LCD_ASSET_BASE_PATH "chouqian_qianzhi0.png",
},
{
.path = LCD_ASSET_BASE_PATH "chouqian_qianzhi1.rle",
.run_count = 73259U,
.path = LCD_ASSET_BASE_PATH "chouqian_qianzhi1.png",
},
{
.path = LCD_ASSET_BASE_PATH "chouqian_qianzhi2.rle",
.run_count = 72906U,
.path = LCD_ASSET_BASE_PATH "chouqian_qianzhi2.png",
},
};
static const lcd_direct_image_t s_chouqian_buyun_frames[] = {
{
.path = LCD_ASSET_BASE_PATH "chouqian_buyun0.rle",
.run_count = 72370U,
.path = LCD_ASSET_BASE_PATH "chouqian_buyun0.png",
},
{
.path = LCD_ASSET_BASE_PATH "chouqian_buyun1.rle",
.run_count = 72552U,
.path = LCD_ASSET_BASE_PATH "chouqian_buyun1.png",
},
{
.path = LCD_ASSET_BASE_PATH "chouqian_buyun2.rle",
.run_count = 72757U,
.path = LCD_ASSET_BASE_PATH "chouqian_buyun2.png",
},
};
static const lcd_direct_image_t s_chouqian_heping_frames[] = {
{
.path = LCD_ASSET_BASE_PATH "chouqian_heping0.rle",
.run_count = 72370U,
.path = LCD_ASSET_BASE_PATH "chouqian_heping0.png",
},
{
.path = LCD_ASSET_BASE_PATH "chouqian_heping1.rle",
.run_count = 73049U,
.path = LCD_ASSET_BASE_PATH "chouqian_heping1.png",
},
{
.path = LCD_ASSET_BASE_PATH "chouqian_heping2.rle",
.run_count = 73365U,
.path = LCD_ASSET_BASE_PATH "chouqian_heping2.png",
},
};
static const lcd_direct_image_t s_chouqian_shangshang_frames[] = {
{
.path = LCD_ASSET_BASE_PATH "chouqian_shangshang0.rle",
.run_count = 72370U,
.path = LCD_ASSET_BASE_PATH "chouqian_shangshang0.png",
},
{
.path = LCD_ASSET_BASE_PATH "chouqian_shangshang1.rle",
.run_count = 72944U,
.path = LCD_ASSET_BASE_PATH "chouqian_shangshang1.png",
},
{
.path = LCD_ASSET_BASE_PATH "chouqian_shangshang2.rle",
.run_count = 73190U,
.path = LCD_ASSET_BASE_PATH "chouqian_shangshang2.png",
},
};
static const lcd_direct_image_t s_chouqian_xiaoji_frames[] = {
{
.path = LCD_ASSET_BASE_PATH "chouqian_xiaoji0.rle",
.run_count = 72370U,
.path = LCD_ASSET_BASE_PATH "chouqian_xiaoji0.png",
},
{
.path = LCD_ASSET_BASE_PATH "chouqian_xiaoji1.rle",
.run_count = 72827U,
.path = LCD_ASSET_BASE_PATH "chouqian_xiaoji1.png",
},
{
.path = LCD_ASSET_BASE_PATH "chouqian_xiaoji2.rle",
.run_count = 73006U,
.path = LCD_ASSET_BASE_PATH "chouqian_xiaoji2.png",
},
};
static const lcd_direct_image_t s_chouqian_zhongshang_frames[] = {
{
.path = LCD_ASSET_BASE_PATH "chouqian_zhongshang0.rle",
.run_count = 72370U,
.path = LCD_ASSET_BASE_PATH "chouqian_zhongshang0.png",
},
{
.path = LCD_ASSET_BASE_PATH "chouqian_zhongshang1.rle",
.run_count = 72135U,
.path = LCD_ASSET_BASE_PATH "chouqian_zhongshang1.png",
},
{
.path = LCD_ASSET_BASE_PATH "chouqian_zhongshang2.rle",
.run_count = 72292U,
.path = LCD_ASSET_BASE_PATH "chouqian_zhongshang2.png",
},
};
static const lcd_direct_image_t s_qianzhiwuxing_frames[] = {
{
.path = LCD_ASSET_BASE_PATH "qianzhiwuxing0.rle",
.run_count = 4157U,
.path = LCD_ASSET_BASE_PATH "qianzhiwuxing0.png",
},
};
static const lcd_direct_image_t s_shui_frames[] = {
{
.path = LCD_ASSET_BASE_PATH "shui0.rle",
.run_count = 74146U,
.path = LCD_ASSET_BASE_PATH "shui0.png",
},
{
.path = LCD_ASSET_BASE_PATH "shui1.rle",
.run_count = 71868U,
.path = LCD_ASSET_BASE_PATH "shui1.png",
},
};
static const lcd_direct_image_t s_mu_frames[] = {
{
.path = LCD_ASSET_BASE_PATH "mu0.rle",
.run_count = 73537U,
.path = LCD_ASSET_BASE_PATH "mu0.png",
},
{
.path = LCD_ASSET_BASE_PATH "mu1.rle",
.run_count = 69337U,
.path = LCD_ASSET_BASE_PATH "mu1.png",
},
};
static const lcd_direct_image_t s_huo_frames[] = {
{
.path = LCD_ASSET_BASE_PATH "huo0.rle",
.run_count = 74455U,
.path = LCD_ASSET_BASE_PATH "huo0.png",
},
{
.path = LCD_ASSET_BASE_PATH "huo1.rle",
.run_count = 72474U,
.path = LCD_ASSET_BASE_PATH "huo1.png",
},
};
static const lcd_direct_image_t s_tu_frames[] = {
{
.path = LCD_ASSET_BASE_PATH "tu0.rle",
.run_count = 95829U,
.path = LCD_ASSET_BASE_PATH "tu0.png",
},
{
.path = LCD_ASSET_BASE_PATH "tu1.rle",
.run_count = 71964U,
.path = LCD_ASSET_BASE_PATH "tu1.png",
},
};
static const lcd_direct_image_t s_jin_frames[] = {
{
.path = LCD_ASSET_BASE_PATH "jin0.rle",
.run_count = 75151U,
.path = LCD_ASSET_BASE_PATH "jin0.png",
},
{
.path = LCD_ASSET_BASE_PATH "jin1.rle",
.run_count = 72012U,
.path = LCD_ASSET_BASE_PATH "jin1.png",
},
};
static const lcd_direct_icon_t s_wuxing_icons[] = {
{
.path = LCD_ASSET_BASE_PATH "icon_shui0.rle",
.run_count = 549U,
.width = 60U,
.height = 56U,
.path = LCD_ASSET_BASE_PATH "icon_shui0.png",
},
{
.path = LCD_ASSET_BASE_PATH "icon_mu0.rle",
.run_count = 1005U,
.width = 86U,
.height = 50U,
.path = LCD_ASSET_BASE_PATH "icon_mu0.png",
},
{
.path = LCD_ASSET_BASE_PATH "icon_huo0.rle",
.run_count = 565U,
.width = 56U,
.height = 58U,
.path = LCD_ASSET_BASE_PATH "icon_huo0.png",
},
{
.path = LCD_ASSET_BASE_PATH "icon_tu0.rle",
.run_count = 754U,
.width = 75U,
.height = 42U,
.path = LCD_ASSET_BASE_PATH "icon_tu0.png",
},
{
.path = LCD_ASSET_BASE_PATH "icon_jin0.rle",
.run_count = 605U,
.width = 65U,
.height = 44U,
.path = LCD_ASSET_BASE_PATH "icon_jin0.png",
},
};
+20
View File
@@ -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.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -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);
}
@@ -0,0 +1,17 @@
#include <stddef.h>
#include "lodepng.h"
extern "C" unsigned lodepng_esp_decode32(unsigned char **out,
unsigned *w,
unsigned *h,
const unsigned char *in,
size_t insize)
{
return lodepng_decode32(out, w, h, in, insize);
}
extern "C" const char *lodepng_esp_error_text(unsigned code)
{
return lodepng_error_text(code);
}