diff --git a/components/platform/CMakeLists.txt b/components/platform/CMakeLists.txt index 70b66f9..bc87d31 100644 --- a/components/platform/CMakeLists.txt +++ b/components/platform/CMakeLists.txt @@ -58,30 +58,28 @@ idf_component_register( ${platform_priv_requires} ) -target_add_binary_data(${COMPONENT_LIB} - "assets/lcd/buyunqian1.rle" - BINARY -) - -foreach(lcd_rle_asset - jichubiaoqing0 - jichubiaoqing1 - jichubiaoqing2 - muyu0 - muyu1 - muyu2) - target_add_binary_data(${COMPONENT_LIB} - "assets/lcd/${lcd_rle_asset}.rle" - BINARY - ) -endforeach() - 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") 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 + buyunqian1 + jichubiaoqing0 + jichubiaoqing1 + jichubiaoqing2 + muyu0 + muyu1 + muyu2 +) +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" + ) +endforeach() set(platform_asset_stage_commands) foreach(audio_file IN LISTS platform_audio_prompt_files) @@ -93,6 +91,14 @@ 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) + 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" + ) +endforeach() add_custom_command( OUTPUT "${platform_asset_stage_stamp}" @@ -100,7 +106,9 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E make_directory "${platform_asset_image_dir}" ${platform_asset_stage_commands} COMMAND ${CMAKE_COMMAND} -E touch "${platform_asset_stage_stamp}" - DEPENDS ${platform_audio_prompt_files} + DEPENDS + ${platform_audio_prompt_files} + ${platform_lcd_flash_asset_files} ) littlefs_create_partition_image( diff --git a/components/platform/internal/lcd_direct_assets.h b/components/platform/internal/lcd_direct_assets.h index e77f6e0..c59e812 100644 --- a/components/platform/internal/lcd_direct_assets.h +++ b/components/platform/internal/lcd_direct_assets.h @@ -15,6 +15,7 @@ typedef struct { typedef struct { const lcd_direct_rle_run_t *runs; uint32_t run_count; + const char *path; } lcd_direct_image_t; typedef struct { diff --git a/components/platform/src/lcd_direct.c b/components/platform/src/lcd_direct.c index 109bf1a..0efc316 100644 --- a/components/platform/src/lcd_direct.c +++ b/components/platform/src/lcd_direct.c @@ -8,9 +8,12 @@ #include "lcd_direct.h" #include "lcd_direct_assets.h" +#include +#include #include #include #include +#include #include "driver/gpio.h" #include "driver/spi_master.h" @@ -85,6 +88,7 @@ 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 void lcd_direct_signal_completion(void); @@ -330,9 +334,9 @@ static void lcd_direct_overlay_icons(uint16_t *pixel_buffer, } } -static esp_err_t lcd_direct_draw_rle_image(const lcd_direct_image_t *image, - const lcd_direct_icon_draw_t *icons, - size_t icon_count) +static esp_err_t lcd_direct_draw_rle_memory_image(const lcd_direct_image_t *image, + const lcd_direct_icon_draw_t *icons, + size_t icon_count) { ESP_RETURN_ON_FALSE(image != NULL && image->runs != NULL, ESP_ERR_INVALID_ARG, @@ -385,6 +389,112 @@ static esp_err_t lcd_direct_draw_rle_image(const lcd_direct_image_t *image, return ESP_OK; } +static esp_err_t lcd_direct_draw_rle_file_image(const lcd_direct_image_t *image, + const lcd_direct_icon_draw_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, + size_t icon_count) +{ + ESP_RETURN_ON_FALSE(image != NULL, + ESP_ERR_INVALID_ARG, + TAG, + "invalid image"); + if (image->runs != NULL) { + return lcd_direct_draw_rle_memory_image(image, icons, icon_count); + } + return lcd_direct_draw_rle_file_image(image, icons, icon_count); +} + static esp_err_t lcd_direct_draw_asset_sequence_once(const lcd_direct_request_t *request) { ESP_RETURN_ON_FALSE(request != NULL, diff --git a/components/platform/src/lcd_direct_assets.c b/components/platform/src/lcd_direct_assets.c index 111fbfc..7ac242c 100644 --- a/components/platform/src/lcd_direct_assets.c +++ b/components/platform/src/lcd_direct_assets.c @@ -2,46 +2,39 @@ #include -extern const uint8_t _binary_buyunqian1_rle_start[] asm("_binary_buyunqian1_rle_start"); -extern const uint8_t _binary_buyunqian1_rle_end[] asm("_binary_buyunqian1_rle_end"); -extern const uint8_t _binary_jichubiaoqing0_rle_start[] asm("_binary_jichubiaoqing0_rle_start"); -extern const uint8_t _binary_jichubiaoqing1_rle_start[] asm("_binary_jichubiaoqing1_rle_start"); -extern const uint8_t _binary_jichubiaoqing2_rle_start[] asm("_binary_jichubiaoqing2_rle_start"); -extern const uint8_t _binary_muyu0_rle_start[] asm("_binary_muyu0_rle_start"); -extern const uint8_t _binary_muyu1_rle_start[] asm("_binary_muyu1_rle_start"); -extern const uint8_t _binary_muyu2_rle_start[] asm("_binary_muyu2_rle_start"); +#define LCD_ASSET_BASE_PATH "/littlefs/lcd/" static const lcd_direct_image_t s_default_image = { - .runs = (const lcd_direct_rle_run_t *)_binary_buyunqian1_rle_start, + .path = LCD_ASSET_BASE_PATH "buyunqian1.rle", .run_count = 72552U, }; static const lcd_direct_image_t s_basic_face_frames[] = { { - .runs = (const lcd_direct_rle_run_t *)_binary_jichubiaoqing0_rle_start, + .path = LCD_ASSET_BASE_PATH "jichubiaoqing0.rle", .run_count = 5893U, }, { - .runs = (const lcd_direct_rle_run_t *)_binary_jichubiaoqing1_rle_start, + .path = LCD_ASSET_BASE_PATH "jichubiaoqing1.rle", .run_count = 4581U, }, { - .runs = (const lcd_direct_rle_run_t *)_binary_jichubiaoqing2_rle_start, + .path = LCD_ASSET_BASE_PATH "jichubiaoqing2.rle", .run_count = 3651U, }, }; static const lcd_direct_image_t s_muyu_frames[] = { { - .runs = (const lcd_direct_rle_run_t *)_binary_muyu0_rle_start, + .path = LCD_ASSET_BASE_PATH "muyu0.rle", .run_count = 63459U, }, { - .runs = (const lcd_direct_rle_run_t *)_binary_muyu1_rle_start, + .path = LCD_ASSET_BASE_PATH "muyu1.rle", .run_count = 63665U, }, { - .runs = (const lcd_direct_rle_run_t *)_binary_muyu2_rle_start, + .path = LCD_ASSET_BASE_PATH "muyu2.rle", .run_count = 63894U, }, };