add some code
This commit is contained in:
@@ -0,0 +1 @@
|
||||
9b6eea296488103300f31a9e0fe9096f830b99341332a0dfccd7e3ce43814302
|
||||
34
managed_components/espressif__esp_mmap_assets/CHANGELOG.md
Normal file
34
managed_components/espressif__esp_mmap_assets/CHANGELOG.md
Normal file
@@ -0,0 +1,34 @@
|
||||
# ChangeLog
|
||||
|
||||
## v1.3.1~2 (2025-08-13)
|
||||
|
||||
* Support all target.
|
||||
|
||||
## v1.3.1~1 (2025-05-30)
|
||||
|
||||
* Add IMPORT_INC_PATH support.
|
||||
|
||||
## v1.3.1 (2025-03-17)
|
||||
|
||||
* Allow appending files to the end of the app_bin.
|
||||
|
||||
## v1.3.0 (2024-10-18)
|
||||
|
||||
* CMake Enhancements: Transitioned from Kconfig options to CMake arguments, supporting independent parameters for each partition.
|
||||
* JPG/PNG to QOI Conversion: Enabled conversion of JPG and PNG to QOI for optimized image handling.
|
||||
* LVGL Image Conversion: Added support for converting images to LVGL-compatible binary formats.
|
||||
|
||||
## v1.2.0 (2024-07-31)
|
||||
|
||||
* Added mmap_enable flag.
|
||||
* Added mmap_assets_copy_mem for reading files from flash to memory.
|
||||
* Configurable filename length.
|
||||
* Support multiple partitions.
|
||||
|
||||
## v1.1.0 (2024-06-27)
|
||||
|
||||
* Added support for split height setting.
|
||||
|
||||
## v1.0.0 Initial Version
|
||||
|
||||
* Added support for file packaging during build.
|
||||
@@ -0,0 +1 @@
|
||||
{"version": "1.0", "algorithm": "sha256", "created_at": "2025-08-15T03:37:02.838522+00:00", "files": [{"path": "CHANGELOG.md", "size": 873, "hash": "63f8532b08f31f1b1ae137cc5ce10a58a8542403ef549226d6220a944c04bf50"}, {"path": "CMakeLists.txt", "size": 415, "hash": "d2227a6d91c15a82d6e74f5f9cb665a3ce6064dd44c9a2a7f86724efd65d11f5"}, {"path": "Kconfig", "size": 172, "hash": "ad099e39b7642387b96f95fbfaf72fc074516e31b44fe03c7ad42ec406a9c2ce"}, {"path": "README.md", "size": 6535, "hash": "7cc70b753691fb86515afa25665f77274a6bd1f8a13df6fda0827b0967aae61f"}, {"path": "config_template.json.in", "size": 785, "hash": "797fac958e94efa0cee56797bd47cb89ecb1886b4bff034385ca4a03499424d7"}, {"path": "esp_mmap_assets.c", "size": 12694, "hash": "5fd7f4d53eec4df75119b433bf62ca06fb49757dbf2783c4375eb204ed3e12df"}, {"path": "idf_component.yml", "size": 682, "hash": "293f38e7bc7bd7fe0aa3874e9fc83bd70407e91715374c26ef57cf2bf7ec135a"}, {"path": "license.txt", "size": 11358, "hash": "cfc7749b96f63bd31c3c42b5c471bf756814053e847c10f3eb003417bc523d30"}, {"path": "project_include.cmake", "size": 8806, "hash": "488c6ab50eb4aa04afcbf9d0109d5d9c21265b5215643694bbb9ce1551be9973"}, {"path": "spiffs_assets_gen.py", "size": 23174, "hash": "4447159c543d4c726e8a73d65c82e745706a4145bb751b51c7e332993bd9b574"}, {"path": "include/esp_mmap_assets.h", "size": 4861, "hash": "1c649bd2a848b7d1e5289d6967389acb81d6baf5dd7cf174ba38bd198cd514ee"}, {"path": "test_apps/CMakeLists.txt", "size": 316, "hash": "36af1e66b99570baa74db914ec137469e7b1e3040e37f42e6999f0b6d1054bf6"}, {"path": "test_apps/partitions.csv", "size": 294, "hash": "738ac20499c5a61749227a51b7858ddb1bb04a11909caf3fcb0d2ca087743d66"}, {"path": "test_apps/pytest_esp_mmap_assets.py", "size": 462, "hash": "8e94b5fd578abc2a5e059ac5ad520e0a6c985ab7dd5fee6af858a271e86590ef"}, {"path": "test_apps/sdkconfig.defaults", "size": 105, "hash": "a0a0718588fedd1211433b7d439272208fd5ed9fc71b5375d7b1a42637fde284"}, {"path": "test_apps/main/CMakeLists.txt", "size": 819, "hash": "fb3b3d04a708d510efca69c73865d99052436d4aa88a8965cda8ebc7f3a2ddc4"}, {"path": "test_apps/main/idf_component.yml", "size": 150, "hash": "2f6a1ceec448679f7d6f9891ff4305e1839a0c1fe2e262279803dcbc3bfc08fb"}, {"path": "test_apps/main/test_esp_mmap_assets.c", "size": 5590, "hash": "b8731f034f8b475036da0e445ac64153a481d3be3f159838d6ddc9b750b965fc"}, {"path": "test_apps/spiffs_assets/jpg.jpg", "size": 17291, "hash": "b58db758b84f153ebe328c84ac9e01ba1ce5af82f271906cea5cfed33df69477"}, {"path": "test_apps/spiffs_assets/png.png", "size": 829, "hash": "4dba710889d8198e452d1b9b9a117f575b2630a9a4e04ad2042703e09ba5bd22"}]}
|
||||
16
managed_components/espressif__esp_mmap_assets/CMakeLists.txt
Normal file
16
managed_components/espressif__esp_mmap_assets/CMakeLists.txt
Normal file
@@ -0,0 +1,16 @@
|
||||
set(PRIV_REQUIRES_LIST spi_flash esp_partition app_update)
|
||||
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_LESS "5.5")
|
||||
list(APPEND bootloader_support)
|
||||
else()
|
||||
list(APPEND esp_bootloader_format)
|
||||
endif()
|
||||
|
||||
idf_component_register(
|
||||
SRCS "esp_mmap_assets.c"
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_REQUIRES ${PRIV_REQUIRES_LIST}
|
||||
)
|
||||
|
||||
include(package_manager)
|
||||
cu_pkg_define_version(${CMAKE_CURRENT_LIST_DIR})
|
||||
9
managed_components/espressif__esp_mmap_assets/Kconfig
Normal file
9
managed_components/espressif__esp_mmap_assets/Kconfig
Normal file
@@ -0,0 +1,9 @@
|
||||
# Kconfig file for MMAP
|
||||
|
||||
menu "mmap file support format"
|
||||
|
||||
config MMAP_FILE_NAME_LENGTH
|
||||
int "Max file name length"
|
||||
default 16
|
||||
range 1 32
|
||||
endmenu
|
||||
184
managed_components/espressif__esp_mmap_assets/README.md
Normal file
184
managed_components/espressif__esp_mmap_assets/README.md
Normal file
@@ -0,0 +1,184 @@
|
||||
[](https://components.espressif.com/components/espressif/esp_mmap_assets)
|
||||
|
||||
## Instructions and Details
|
||||
|
||||
This module is primarily used for packaging assets (such as images, fonts, etc.) and directly mapping them for user access.
|
||||
|
||||
### Features
|
||||
|
||||
1. **Separation of Code and Assets**:
|
||||
- Assets are kept separate from the application code, reducing the size of the application binary and improving performance compared to using SPIFFS.
|
||||
|
||||
2. **Efficient Resource Management**:
|
||||
- Simplifies assets management by using automatically generated enums to access resource information.
|
||||
|
||||
3. **Memory-Efficient Image Decoding**:
|
||||
- Includes an image splitting script to reduce the memory required for image decoding.
|
||||
|
||||
4. **Support for Image Conversion (JPG/PNG to QOI)**:
|
||||
- Supports image conversion from JPG and PNG formats to QOI (Quite OK Image), enabling faster decoding times for resource-limited systems.
|
||||
|
||||
5. **LVGL-Specific Binary Format Support**:
|
||||
- Supports image conversion to binary files required by LVGL, ensuring compatibility with the LVGL graphics library and optimizing performance for display rendering.
|
||||
|
||||
## Add to Project
|
||||
|
||||
Packages from this repository are uploaded to [Espressif's component service](https://components.espressif.com/). You can add them to your project via `idf.py add-dependency`, e.g.
|
||||
|
||||
```sh
|
||||
idf.py add-dependency esp_mmap_assets
|
||||
```
|
||||
|
||||
Alternatively, you can create `idf_component.yml`. More details are available in [Espressif's documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/tools/idf-component-manager.html).
|
||||
|
||||
## Usage
|
||||
|
||||
### CMake
|
||||
|
||||
The following options are supported. These options allow you to configure various aspects of image handling.
|
||||
|
||||
```c
|
||||
set(options
|
||||
FLASH_IN_PROJECT,
|
||||
FLASH_APPEND_APP,
|
||||
MMAP_SUPPORT_SJPG,
|
||||
MMAP_SUPPORT_SPNG,
|
||||
MMAP_SUPPORT_QOI,
|
||||
MMAP_SUPPORT_SQOI,
|
||||
MMAP_SUPPORT_RAW,
|
||||
MMAP_RAW_DITHER,
|
||||
MMAP_RAW_BGR_MODE)
|
||||
```
|
||||
|
||||
```c
|
||||
set(one_value_args
|
||||
MMAP_FILE_SUPPORT_FORMAT,
|
||||
MMAP_SPLIT_HEIGHT,
|
||||
MMAP_RAW_FILE_FORMAT
|
||||
IMPORT_INC_PATH)
|
||||
```
|
||||
|
||||
### Option Explanations
|
||||
|
||||
#### General Options
|
||||
|
||||
- **`FLASH_IN_PROJECT`**: Users can opt to have the image automatically flashed together with the app binaries, partition tables, etc. on `idf.py flash`
|
||||
- **`FLASH_APPEND_APP`**: Enables appending binary data (`bin`) to the application binary (`app_bin`).
|
||||
- **`IMPORT_INC_PATH`**: Target path for generated include files. Defaults to referencing component location.
|
||||
- **`MMAP_FILE_SUPPORT_FORMAT`**: Specifies supported file formats (e.g., `.png`, `.jpg`, `.ttf`).
|
||||
- **`MMAP_SPLIT_HEIGHT`**: Defines height for image splitting to reduce memory usage. Depends on:
|
||||
- `MMAP_SUPPORT_SJPG`
|
||||
- `MMAP_SUPPORT_SPNG`
|
||||
- `MMAP_SUPPORT_SQOI`
|
||||
|
||||
##### General demo
|
||||
|
||||
```c
|
||||
spiffs_create_partition_assets(
|
||||
my_spiffs_partition
|
||||
my_folder
|
||||
FLASH_IN_PROJECT
|
||||
MMAP_FILE_SUPPORT_FORMAT ".jpg,.png,.ttf"
|
||||
)
|
||||
```
|
||||
|
||||
#### Supported Image Formats
|
||||
|
||||
- **`MMAP_SUPPORT_SJPG`**: Enables support for **SJPG** format.
|
||||
- **`MMAP_SUPPORT_SPNG`**: Enables support for **SPNG** format.
|
||||
- **`MMAP_SUPPORT_QOI`**: Enables support for **QOI** format.
|
||||
- **`MMAP_SUPPORT_SQOI`**: Enables support for **SQOI** format. Depends on:
|
||||
- `MMAP_SUPPORT_QOI`
|
||||
|
||||
##### Image Splitting Demo
|
||||
|
||||
```c
|
||||
spiffs_create_partition_assets(
|
||||
my_spiffs_partition
|
||||
my_folder
|
||||
FLASH_IN_PROJECT
|
||||
MMAP_FILE_SUPPORT_FORMAT ".jpg"
|
||||
MMAP_SUPPORT_SJPG
|
||||
MMAP_SPLIT_HEIGHT 16
|
||||
)
|
||||
```
|
||||
|
||||
#### LVGL Bin Support
|
||||
|
||||
- **`MMAP_SUPPORT_RAW`**: Converts images to LVGL-supported **Binary** data.
|
||||
|
||||
**References:**
|
||||
- LVGL v8: [Use detailed reference](https://github.com/W-Mai/lvgl_image_converter)
|
||||
- LVGL v9: [Use detailed reference](https://github.com/lvgl/lvgl/blob/master/scripts/LVGLImage.py)
|
||||
|
||||
- **`MMAP_RAW_FILE_FORMAT`**: Specifies file format for **RAW** images.
|
||||
- **LVGL v8**: `{true_color, true_color_alpha, true_color_chroma, indexed_1, indexed_2, indexed_4, indexed_8, alpha_1, alpha_2, alpha_4, alpha_8, raw, raw_alpha, raw_chroma}`
|
||||
- **LVGL v9**: Not used.
|
||||
|
||||
- **`MMAP_RAW_COLOR_FORMAT`**: Specifies color format for **RAW** images.
|
||||
- **LVGL v8**: `{RGB332, RGB565, RGB565SWAP, RGB888}`
|
||||
- **LVGL v9**: `{L8, I1, I2, I4, I8, A1, A2, A4, A8, ARGB8888, XRGB8888, RGB565, RGB565A8, ARGB8565, RGB888, AUTO, RAW, RAW_ALPHA}`
|
||||
|
||||
- **`MMAP_RAW_DITHER`**: Enables **dithering** for **RAW** images.
|
||||
- **LVGL v8**: Requires dithering.
|
||||
- **LVGL v9**: Not used.
|
||||
|
||||
- **`MMAP_RAW_BGR_MODE`**: Enables **BGR mode** for **RAW** images.
|
||||
- **LVGL v8**: Not used.
|
||||
- **LVGL v9**: Not used.
|
||||
|
||||
##### LVGL v9 demo
|
||||
|
||||
```c
|
||||
spiffs_create_partition_assets(
|
||||
.........
|
||||
MMAP_FILE_SUPPORT_FORMAT ".png"
|
||||
MMAP_SUPPORT_RAW
|
||||
MMAP_RAW_COLOR_FORMAT "ARGB8888"
|
||||
)
|
||||
```
|
||||
|
||||
##### LVGL v8 demo
|
||||
|
||||
```c
|
||||
spiffs_create_partition_assets(
|
||||
.........
|
||||
MMAP_FILE_SUPPORT_FORMAT ".png"
|
||||
MMAP_SUPPORT_RAW
|
||||
MMAP_RAW_FILE_FORMAT "true_color_alpha"
|
||||
MMAP_RAW_COLOR_FORMAT "RGB565SWAP"
|
||||
)
|
||||
```
|
||||
|
||||
### Initialization
|
||||
```c
|
||||
mmap_assets_handle_t asset_handle;
|
||||
|
||||
/* partitions.csv
|
||||
* --------------------------------------------------------
|
||||
* | Name | Type | SubType | Offset | Size | Flags |
|
||||
* --------------------------------------------------------
|
||||
* | my_spiffs_partition | data | spiffs | | 6000K | |
|
||||
* --------------------------------------------------------
|
||||
*/
|
||||
const mmap_assets_config_t config = {
|
||||
.partition_label = "my_spiffs_partition",
|
||||
.max_files = MMAP_MY_FOLDER_FILES, //Get it from the compiled .h
|
||||
.checksum = MMAP_MY_FOLDER_CHECKSUM, //Get it from the compiled .h
|
||||
.flags = {
|
||||
.mmap_enable = true,
|
||||
.app_bin_check = true,
|
||||
},
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(mmap_assets_new(&config, &asset_handle));
|
||||
|
||||
const char *name = mmap_assets_get_name(asset_handle, 0);
|
||||
const void *mem = mmap_assets_get_mem(asset_handle, 0);
|
||||
int size = mmap_assets_get_size(asset_handle, 0);
|
||||
int width = mmap_assets_get_width(asset_handle, 0);
|
||||
int height = mmap_assets_get_height(asset_handle, 0);
|
||||
|
||||
ESP_LOGI(TAG, "Asset - Name:[%s], Memory:[%p], Size:[%d bytes], Width:[%d px], Height:[%d px]", name, mem, size, width, height);
|
||||
|
||||
```
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"project_dir": "@PROJECT_DIR@",
|
||||
"include_path": "@import_include_path@",
|
||||
"assets_path": "@base_dir_full_path@",
|
||||
"image_file": "@image_file@",
|
||||
"lvgl_ver": "@lvgl_ver@",
|
||||
"assets_size": "@size@",
|
||||
"support_format": "@arg_MMAP_FILE_SUPPORT_FORMAT@",
|
||||
"name_length": "@CONFIG_MMAP_FILE_NAME_LENGTH@",
|
||||
"split_height": "@arg_MMAP_SPLIT_HEIGHT@",
|
||||
"support_spng": @support_spng@,
|
||||
"support_sjpg": @support_sjpg@,
|
||||
"support_qoi": @support_qoi@,
|
||||
"support_sqoi": @support_sqoi@,
|
||||
"support_raw": @support_raw@,
|
||||
"support_raw_dither": @support_raw_dither@,
|
||||
"support_raw_bgr": @support_raw_bgr@,
|
||||
"support_raw_ff": "@arg_MMAP_RAW_FILE_FORMAT@",
|
||||
"support_raw_cf": "@arg_MMAP_RAW_COLOR_FORMAT@",
|
||||
"app_bin_path": "@app_bin_path@"
|
||||
}
|
||||
352
managed_components/espressif__esp_mmap_assets/esp_mmap_assets.c
Normal file
352
managed_components/espressif__esp_mmap_assets/esp_mmap_assets.c
Normal file
@@ -0,0 +1,352 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include <spi_flash_mmap.h>
|
||||
#include <esp_attr.h>
|
||||
#include <esp_partition.h>
|
||||
#include "esp_ota_ops.h"
|
||||
#include "esp_image_format.h"
|
||||
#include "esp_mmap_assets.h"
|
||||
|
||||
static const char *TAG = "mmap_assets";
|
||||
|
||||
#define ASSETS_FILE_NUM_OFFSET 0
|
||||
#define ASSETS_CHECKSUM_OFFSET 4
|
||||
#define ASSETS_TABLE_LEN 8
|
||||
#define ASSETS_TABLE_OFFSET 12
|
||||
|
||||
#define ASSETS_FILE_MAGIC_HEAD 0x5A5A
|
||||
#define ASSETS_FILE_MAGIC_LEN 2
|
||||
|
||||
/**
|
||||
* @brief Asset table structure, contains detailed information for each asset.
|
||||
*/
|
||||
#pragma pack(1)
|
||||
typedef struct {
|
||||
char asset_name[CONFIG_MMAP_FILE_NAME_LENGTH]; /*!< Name of the asset */
|
||||
uint32_t asset_size; /*!< Size of the asset */
|
||||
uint32_t asset_offset; /*!< Offset of the asset */
|
||||
uint16_t asset_width; /*!< Width of the asset */
|
||||
uint16_t asset_height; /*!< Height of the asset */
|
||||
} mmap_assets_table_t;
|
||||
#pragma pack()
|
||||
|
||||
typedef struct {
|
||||
const char *asset_mem;
|
||||
const mmap_assets_table_t *table;
|
||||
} mmap_assets_item_t;
|
||||
|
||||
typedef struct {
|
||||
esp_partition_mmap_handle_t *mmap_handle;
|
||||
const esp_partition_t *partition;
|
||||
mmap_assets_item_t *item;
|
||||
int max_asset;
|
||||
struct {
|
||||
unsigned int mmap_enable: 1; /*!< Flag to indicate if memory-mapped I/O is enabled */
|
||||
unsigned int reserved: 31; /*!< Reserved for future use */
|
||||
} flags;
|
||||
int stored_files;
|
||||
} mmap_assets_t;
|
||||
|
||||
static uint32_t compute_checksum(const uint8_t *data, uint32_t length)
|
||||
{
|
||||
uint32_t checksum = 0;
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
checksum += data[i];
|
||||
}
|
||||
return checksum & 0xFFFF;
|
||||
}
|
||||
|
||||
esp_err_t mmap_assets_new(const mmap_assets_config_t *config, mmap_assets_handle_t *ret_item)
|
||||
{
|
||||
esp_err_t ret = ESP_OK;
|
||||
const void *root = NULL;
|
||||
mmap_assets_item_t *item = NULL;
|
||||
mmap_assets_t *map_asset = NULL;
|
||||
esp_partition_mmap_handle_t *mmap_handle = NULL;
|
||||
|
||||
ESP_GOTO_ON_FALSE(config && ret_item, ESP_ERR_INVALID_ARG, err, TAG, "invalid argument");
|
||||
|
||||
map_asset = (mmap_assets_t *)calloc(1, sizeof(mmap_assets_t));
|
||||
ESP_GOTO_ON_FALSE(map_asset, ESP_ERR_NO_MEM, err, TAG, "no mem for map_asset handle");
|
||||
|
||||
map_asset->flags.mmap_enable = config->flags.mmap_enable;
|
||||
|
||||
const esp_partition_t *running = esp_ota_get_running_partition();
|
||||
ESP_GOTO_ON_FALSE(running, ESP_ERR_NOT_FOUND, err, TAG, "Can not find running partition");
|
||||
|
||||
const esp_partition_t *partition = esp_partition_find_first(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, config->partition_label);
|
||||
ESP_GOTO_ON_FALSE(partition, ESP_ERR_NOT_FOUND, err, TAG, "Can not find \"%s\" in partition table", config->partition_label);
|
||||
map_asset->partition = partition;
|
||||
|
||||
int stored_files = 0;
|
||||
int stored_len = 0;
|
||||
uint32_t stored_chksum = 0;
|
||||
uint32_t calculated_checksum = 0;
|
||||
|
||||
uint32_t offset = 0;
|
||||
uint32_t size = 0;
|
||||
|
||||
if ((partition->address == running->address) && (partition->size == running->size)) {
|
||||
|
||||
const esp_partition_pos_t part = {
|
||||
.offset = running->address,
|
||||
.size = running->size,
|
||||
};
|
||||
esp_image_metadata_t metadata;
|
||||
esp_image_get_metadata(&part, &metadata);
|
||||
|
||||
offset = metadata.image_len;
|
||||
ESP_LOGD(TAG, "partition:%s(0x%x), offset:0x%X", partition->label, (unsigned int)partition->address, (unsigned int)offset);
|
||||
ESP_GOTO_ON_FALSE(partition->size > offset, ESP_ERR_INVALID_SIZE, err, TAG, "Partition %s size is insufficient", partition->label);
|
||||
}
|
||||
|
||||
size = partition->size - offset;
|
||||
|
||||
if (map_asset->flags.mmap_enable) {
|
||||
int free_pages = spi_flash_mmap_get_free_pages(ESP_PARTITION_MMAP_DATA);
|
||||
uint32_t storage_size = free_pages * 64 * 1024;
|
||||
ESP_LOGD(TAG, "The storage free size is %ld KB", storage_size / 1024);
|
||||
ESP_LOGD(TAG, "The partition size is %ld KB", size / 1024);
|
||||
ESP_GOTO_ON_FALSE((storage_size > size), ESP_ERR_INVALID_SIZE, err, TAG, "The free size is less than %s partition required", partition->label);
|
||||
|
||||
mmap_handle = (esp_partition_mmap_handle_t *)malloc(sizeof(esp_partition_mmap_handle_t));
|
||||
ESP_GOTO_ON_ERROR(esp_partition_mmap(partition, offset, size, ESP_PARTITION_MMAP_DATA, &root, mmap_handle), err, TAG, "esp_partition_mmap failed");
|
||||
|
||||
stored_files = *(int *)(root + ASSETS_FILE_NUM_OFFSET);
|
||||
stored_chksum = *(uint32_t *)(root + ASSETS_CHECKSUM_OFFSET);
|
||||
stored_len = *(uint32_t *)(root + ASSETS_TABLE_LEN);
|
||||
|
||||
if (config->flags.full_check) {
|
||||
calculated_checksum = compute_checksum((uint8_t *)(root + ASSETS_TABLE_OFFSET), stored_len);
|
||||
}
|
||||
} else {
|
||||
esp_partition_read(partition, ASSETS_FILE_NUM_OFFSET, &stored_files, sizeof(stored_files));
|
||||
esp_partition_read(partition, ASSETS_CHECKSUM_OFFSET, &stored_chksum, sizeof(stored_chksum));
|
||||
esp_partition_read(partition, ASSETS_TABLE_LEN, &stored_len, sizeof(stored_len));
|
||||
|
||||
if (config->flags.full_check) {
|
||||
uint32_t read_offset = ASSETS_TABLE_OFFSET;
|
||||
uint32_t bytes_left = stored_len;
|
||||
uint8_t buffer[1024];
|
||||
|
||||
while (bytes_left > 0) {
|
||||
uint32_t read_size = (bytes_left > sizeof(buffer)) ? sizeof(buffer) : bytes_left;
|
||||
ESP_GOTO_ON_ERROR(esp_partition_read(partition, read_offset, buffer, read_size), err, TAG, "esp_partition_read failed");
|
||||
|
||||
calculated_checksum += compute_checksum(buffer, read_size);
|
||||
read_offset += read_size;
|
||||
bytes_left -= read_size;
|
||||
}
|
||||
calculated_checksum &= 0xFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
if (config->flags.full_check) {
|
||||
ESP_GOTO_ON_FALSE(calculated_checksum == stored_chksum, ESP_ERR_INVALID_CRC, err, TAG, "bad full checksum");
|
||||
}
|
||||
|
||||
if (config->flags.app_bin_check) {
|
||||
if (config->max_files != stored_files) {
|
||||
ret = ESP_ERR_INVALID_SIZE;
|
||||
ESP_LOGE(TAG, "bad input file num: Input %d, Stored %d", config->max_files, stored_files);
|
||||
}
|
||||
|
||||
if (config->checksum != stored_chksum) {
|
||||
ret = ESP_ERR_INVALID_CRC;
|
||||
ESP_LOGE(TAG, "bad input chksum: Input 0x%x, Stored 0x%x", (unsigned int)config->checksum, (unsigned int)stored_chksum);
|
||||
}
|
||||
}
|
||||
|
||||
map_asset->stored_files = stored_files;
|
||||
|
||||
item = (mmap_assets_item_t *)malloc(sizeof(mmap_assets_item_t) * config->max_files);
|
||||
ESP_GOTO_ON_FALSE(item, ESP_ERR_NO_MEM, err, TAG, "no mem for asset item");
|
||||
|
||||
if (map_asset->flags.mmap_enable) {
|
||||
mmap_assets_table_t *table = (mmap_assets_table_t *)(root + ASSETS_TABLE_OFFSET);
|
||||
for (int i = 0; i < config->max_files; i++) {
|
||||
(item + i)->table = (table + i);
|
||||
(item + i)->asset_mem = (void *)(root + ASSETS_TABLE_OFFSET + config->max_files * sizeof(mmap_assets_table_t) + table[i].asset_offset);
|
||||
}
|
||||
} else {
|
||||
mmap_assets_table_t *table = malloc(sizeof(mmap_assets_table_t) * config->max_files);
|
||||
for (int i = 0; i < config->max_files; i++) {
|
||||
esp_partition_read(partition, ASSETS_TABLE_OFFSET + i * sizeof(mmap_assets_table_t), (table + i), sizeof(mmap_assets_table_t));
|
||||
(item + i)->table = (table + i);
|
||||
(item + i)->asset_mem = (char *)(ASSETS_TABLE_OFFSET + config->max_files * sizeof(mmap_assets_table_t) + table[i].asset_offset);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < config->max_files; i++) {
|
||||
ESP_LOGD(TAG, "[%d], offset:[%" PRIu32 "], size:[%" PRIu32 "], name:%s, %p",
|
||||
i,
|
||||
(item + i)->table->asset_offset,
|
||||
(item + i)->table->asset_size,
|
||||
(item + i)->table->asset_name,
|
||||
(item + i)->asset_mem);
|
||||
|
||||
if (config->flags.metadata_check) {
|
||||
uint16_t magic_data, *magic_ptr = NULL;
|
||||
if (map_asset->flags.mmap_enable) {
|
||||
magic_ptr = (uint16_t *)(item + i)->asset_mem;
|
||||
} else {
|
||||
esp_partition_read(map_asset->partition, (int)(item + i)->asset_mem, &magic_data, ASSETS_FILE_MAGIC_LEN);
|
||||
magic_ptr = &magic_data;
|
||||
}
|
||||
ESP_GOTO_ON_FALSE(*magic_ptr == ASSETS_FILE_MAGIC_HEAD, ESP_ERR_INVALID_CRC, err, TAG,
|
||||
"(%s) bad file magic header", (item + i)->table->asset_name);
|
||||
}
|
||||
}
|
||||
|
||||
map_asset->mmap_handle = mmap_handle;
|
||||
map_asset->item = item;
|
||||
map_asset->max_asset = config->max_files;
|
||||
*ret_item = (mmap_assets_handle_t)map_asset;
|
||||
|
||||
ESP_LOGD(TAG, "new asset handle:@%p", map_asset);
|
||||
|
||||
ESP_LOGI(TAG, "Partition '%s' successfully created, version: %d.%d.%d",
|
||||
config->partition_label, ESP_MMAP_ASSETS_VER_MAJOR, ESP_MMAP_ASSETS_VER_MINOR, ESP_MMAP_ASSETS_VER_PATCH);
|
||||
|
||||
return ret;
|
||||
|
||||
err:
|
||||
if (item) {
|
||||
if (false == map_asset->flags.mmap_enable) {
|
||||
free((void *)(item + 0)->table);
|
||||
}
|
||||
free(item);
|
||||
}
|
||||
|
||||
if (mmap_handle) {
|
||||
esp_partition_munmap(*mmap_handle);
|
||||
free(mmap_handle);
|
||||
}
|
||||
|
||||
if (map_asset) {
|
||||
free(map_asset);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
esp_err_t mmap_assets_del(mmap_assets_handle_t handle)
|
||||
{
|
||||
ESP_RETURN_ON_FALSE(handle, ESP_ERR_INVALID_ARG, TAG, "handle is invalid");
|
||||
mmap_assets_t *map_asset = (mmap_assets_t *)(handle);
|
||||
|
||||
if (map_asset->mmap_handle) {
|
||||
esp_partition_munmap(*(map_asset->mmap_handle));
|
||||
free(map_asset->mmap_handle);
|
||||
}
|
||||
|
||||
if (map_asset->item) {
|
||||
if (false == map_asset->flags.mmap_enable) {
|
||||
free((void *)(map_asset->item + 0)->table);
|
||||
}
|
||||
free(map_asset->item);
|
||||
}
|
||||
|
||||
if (map_asset) {
|
||||
free(map_asset);
|
||||
}
|
||||
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
int mmap_assets_get_stored_files(mmap_assets_handle_t handle)
|
||||
{
|
||||
assert(handle && "handle is invalid");
|
||||
mmap_assets_t *map_asset = (mmap_assets_t *)(handle);
|
||||
|
||||
return map_asset->stored_files;
|
||||
}
|
||||
|
||||
size_t mmap_assets_copy_mem(mmap_assets_handle_t handle, size_t offset, void *dest_buffer, size_t size)
|
||||
{
|
||||
assert(handle && "handle is invalid");
|
||||
mmap_assets_t *map_asset = (mmap_assets_t *)(handle);
|
||||
|
||||
if (true == map_asset->flags.mmap_enable) {
|
||||
memcpy(dest_buffer, (void *)offset, size);
|
||||
return size;
|
||||
} else if (offset) {
|
||||
esp_partition_read(map_asset->partition, offset, dest_buffer, size);
|
||||
return size;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Invalid offset: %zu.", offset);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const uint8_t *mmap_assets_get_mem(mmap_assets_handle_t handle, int index)
|
||||
{
|
||||
assert(handle && "handle is invalid");
|
||||
mmap_assets_t *map_asset = (mmap_assets_t *)(handle);
|
||||
|
||||
if (map_asset->max_asset > index) {
|
||||
return (const uint8_t *)((map_asset->item + index)->asset_mem + ASSETS_FILE_MAGIC_LEN);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Invalid index: %d. Maximum index is %d.", index, map_asset->max_asset);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
const char *mmap_assets_get_name(mmap_assets_handle_t handle, int index)
|
||||
{
|
||||
assert(handle && "handle is invalid");
|
||||
mmap_assets_t *map_asset = (mmap_assets_t *)(handle);
|
||||
|
||||
if (map_asset->max_asset > index) {
|
||||
return (map_asset->item + index)->table->asset_name;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Invalid index: %d. Maximum index is %d.", index, map_asset->max_asset);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int mmap_assets_get_size(mmap_assets_handle_t handle, int index)
|
||||
{
|
||||
assert(handle && "handle is invalid");
|
||||
mmap_assets_t *map_asset = (mmap_assets_t *)(handle);
|
||||
|
||||
if (map_asset->max_asset > index) {
|
||||
return (map_asset->item + index)->table->asset_size;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Invalid index: %d. Maximum index is %d.", index, map_asset->max_asset);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int mmap_assets_get_width(mmap_assets_handle_t handle, int index)
|
||||
{
|
||||
assert(handle && "handle is invalid");
|
||||
mmap_assets_t *map_asset = (mmap_assets_t *)(handle);
|
||||
|
||||
if (map_asset->max_asset > index) {
|
||||
return (map_asset->item + index)->table->asset_width;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Invalid index: %d. Maximum index is %d.", index, map_asset->max_asset);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int mmap_assets_get_height(mmap_assets_handle_t handle, int index)
|
||||
{
|
||||
assert(handle && "handle is invalid");
|
||||
mmap_assets_t *map_asset = (mmap_assets_t *)(handle);
|
||||
|
||||
if (map_asset->max_asset > index) {
|
||||
return (map_asset->item + index)->table->asset_height;
|
||||
} else {
|
||||
ESP_LOGE(TAG, "Invalid index: %d. Maximum index is %d.", index, map_asset->max_asset);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
dependencies:
|
||||
cmake_utilities:
|
||||
version: 0.*
|
||||
idf:
|
||||
version: '>=5.0'
|
||||
description: Tool for packaging and managing resource files into bin files using memory
|
||||
mapping for efficient asset handling.
|
||||
documentation: https://docs.espressif.com/projects/esp-iot-solution/en/latest/display/tools/esp_mmap_assets.html
|
||||
issues: https://github.com/espressif/esp-iot-solution/issues
|
||||
repository: git://github.com/espressif/esp-iot-solution.git
|
||||
repository_info:
|
||||
commit_sha: 09fe2f7800edcfea6ade9e3f0db4297e2adf9938
|
||||
path: components/display/tools/esp_mmap_assets
|
||||
url: https://github.com/espressif/esp-iot-solution/tree/master/components/display/tools/esp_mmap_assets
|
||||
version: 1.3.1~2
|
||||
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Asset configuration structure, contains the asset table and other configuration information.
|
||||
*/
|
||||
typedef struct {
|
||||
const char *partition_label; /*!< Label of the partition containing the assets */
|
||||
int max_files; /*!< Maximum number of assets supported */
|
||||
uint32_t checksum; /*!< Checksum of the asset table for integrity verification */
|
||||
struct {
|
||||
unsigned int mmap_enable: 1; /*!< Flag to indicate if memory-mapped I/O is enabled */
|
||||
unsigned int app_bin_check: 1; /*!< Flag to enable app header and bin file consistency check */
|
||||
unsigned int full_check: 1; /*!< Flag to enable self-consistency check */
|
||||
unsigned int metadata_check: 1; /*!< Flag to enable metadata verification */
|
||||
unsigned int reserved: 28; /*!< Reserved for future use */
|
||||
} flags; /*!< Configuration flags */
|
||||
} mmap_assets_config_t;
|
||||
|
||||
/**
|
||||
* @brief Asset handle type, points to the asset.
|
||||
*/
|
||||
typedef struct mmap_assets_t *mmap_assets_handle_t; /*!< Type of asset handle */
|
||||
|
||||
/**
|
||||
* @brief Create a new asset instance.
|
||||
*
|
||||
* @param[in] config Pointer to the asset configuration structure.
|
||||
* @param[out] ret_item Pointer to the handle of the newly created asset instance.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Success
|
||||
* - ESP_ERR_NO_MEM: Insufficient memory
|
||||
* - ESP_ERR_NOT_FOUND: Can't find partition
|
||||
* - ESP_ERR_INVALID_SIZE: File num mismatch
|
||||
* - ESP_ERR_INVALID_CRC: Checksum mismatch
|
||||
*/
|
||||
esp_err_t mmap_assets_new(const mmap_assets_config_t *config, mmap_assets_handle_t *ret_item);
|
||||
|
||||
/**
|
||||
* @brief Delete an asset instance.
|
||||
*
|
||||
* @param[in] handle Asset instance handle.
|
||||
*
|
||||
* @return
|
||||
* - ESP_OK: Success
|
||||
* - ESP_ERR_INVALID_ARG: Invalid argument
|
||||
*/
|
||||
esp_err_t mmap_assets_del(mmap_assets_handle_t handle);
|
||||
|
||||
/**
|
||||
* @brief Get the memory of the asset at the specified index.
|
||||
*
|
||||
* @param[in] handle Asset instance handle.
|
||||
* @param[in] index Index of the asset.
|
||||
*
|
||||
* @return Pointer to the asset memory, or NULL if index is invalid.
|
||||
*/
|
||||
const uint8_t *mmap_assets_get_mem(mmap_assets_handle_t handle, int index);
|
||||
|
||||
/**
|
||||
* @brief Copy a portion of an asset's memory to a destination buffer.
|
||||
*
|
||||
* @param[in] handle Asset instance handle.
|
||||
* @param[in] offset Offset within the asset's memory from which to start copying.
|
||||
* @param[out] dest_buffer Pointer to the destination buffer where the memory will be copied.
|
||||
* @param[in] size Number of bytes to copy from the asset's memory to the destination buffer.
|
||||
*
|
||||
* @return The actual number of bytes copied, or 0 if the operation fails.
|
||||
*/
|
||||
size_t mmap_assets_copy_mem(mmap_assets_handle_t handle, size_t offset, void *dest_buffer, size_t size);
|
||||
|
||||
/**
|
||||
* @brief Get the name of the asset at the specified index.
|
||||
*
|
||||
* @param[in] handle Asset instance handle.
|
||||
* @param[in] index Index of the asset.
|
||||
*
|
||||
* @return Pointer to the asset name, or NULL if index is invalid.
|
||||
*/
|
||||
const char *mmap_assets_get_name(mmap_assets_handle_t handle, int index);
|
||||
|
||||
/**
|
||||
* @brief Get the size of the asset at the specified index.
|
||||
*
|
||||
* @param[in] handle Asset instance handle.
|
||||
* @param[in] index Index of the asset.
|
||||
*
|
||||
* @return Size of the asset, or -1 if index is invalid.
|
||||
*/
|
||||
int mmap_assets_get_size(mmap_assets_handle_t handle, int index);
|
||||
|
||||
/**
|
||||
* @brief Get the width of the asset at the specified index.
|
||||
*
|
||||
* @param[in] handle Asset instance handle.
|
||||
* @param[in] index Index of the asset.
|
||||
*
|
||||
* @return Width of the asset, or -1 if index is invalid.
|
||||
*/
|
||||
int mmap_assets_get_width(mmap_assets_handle_t handle, int index);
|
||||
|
||||
/**
|
||||
* @brief Get the height of the asset at the specified index.
|
||||
*
|
||||
* @param[in] handle Asset instance handle.
|
||||
* @param[in] index Index of the asset.
|
||||
*
|
||||
* @return Height of the asset, or -1 if index is invalid.
|
||||
*/
|
||||
int mmap_assets_get_height(mmap_assets_handle_t handle, int index);
|
||||
|
||||
/**
|
||||
* @brief Get the number of stored files in the memory-mapped asset.
|
||||
*
|
||||
* This function returns the total number of assets stored in the memory-mapped file
|
||||
* associated with the given handle. This can be useful for iterating over all assets
|
||||
* or validating the file contents.
|
||||
*
|
||||
* @param[in] handle Asset instance handle.
|
||||
* This handle is used to identify the memory-mapped file instance
|
||||
* containing the assets.
|
||||
*
|
||||
* @return The number of stored assets.
|
||||
* Returns the total count of assets if the handle is valid, otherwise returns 0.
|
||||
*/
|
||||
int mmap_assets_get_stored_files(mmap_assets_handle_t handle);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
202
managed_components/espressif__esp_mmap_assets/license.txt
Normal file
202
managed_components/espressif__esp_mmap_assets/license.txt
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,225 @@
|
||||
# spiffs_create_partition_assets
|
||||
#
|
||||
# Create a spiffs image of the specified directory on the host during build and optionally
|
||||
# have the created image flashed using `idf.py flash`
|
||||
function(spiffs_create_partition_assets partition base_dir)
|
||||
# Define option flags (BOOL)
|
||||
set(options FLASH_IN_PROJECT
|
||||
FLASH_APPEND_APP
|
||||
MMAP_SUPPORT_SJPG
|
||||
MMAP_SUPPORT_SPNG
|
||||
MMAP_SUPPORT_QOI
|
||||
MMAP_SUPPORT_SQOI
|
||||
MMAP_SUPPORT_RAW
|
||||
MMAP_RAW_DITHER
|
||||
MMAP_RAW_BGR_MODE)
|
||||
|
||||
# Define one-value arguments (STRING and INT)
|
||||
set(one_value_args MMAP_FILE_SUPPORT_FORMAT
|
||||
MMAP_SPLIT_HEIGHT
|
||||
MMAP_RAW_FILE_FORMAT
|
||||
MMAP_RAW_COLOR_FORMAT
|
||||
IMPORT_INC_PATH)
|
||||
|
||||
# Define multi-value arguments
|
||||
set(multi DEPENDS)
|
||||
|
||||
# Parse the arguments passed to the function
|
||||
cmake_parse_arguments(arg
|
||||
"${options}"
|
||||
"${one_value_args}"
|
||||
"${multi}"
|
||||
"${ARGN}")
|
||||
|
||||
if(NOT DEFINED arg_MMAP_FILE_SUPPORT_FORMAT OR arg_MMAP_FILE_SUPPORT_FORMAT STREQUAL "")
|
||||
message(FATAL_ERROR "MMAP_FILE_SUPPORT_FORMAT is empty. Please input the file suffixes you want (e.g .png, .jpg).")
|
||||
endif()
|
||||
|
||||
if(arg_MMAP_SUPPORT_QOI AND (arg_MMAP_SUPPORT_SJPG OR arg_MMAP_SUPPORT_SPNG))
|
||||
message(FATAL_ERROR "MMAP_SUPPORT_QOI depends on !MMAP_SUPPORT_SJPG && !MMAP_SUPPORT_SPNG.")
|
||||
endif()
|
||||
|
||||
if(arg_MMAP_SUPPORT_SQOI AND NOT arg_MMAP_SUPPORT_QOI)
|
||||
message(FATAL_ERROR "MMAP_SUPPORT_SQOI depends on MMAP_SUPPORT_QOI.")
|
||||
endif()
|
||||
|
||||
if( (arg_MMAP_SUPPORT_SJPG OR arg_MMAP_SUPPORT_SPNG OR arg_MMAP_SUPPORT_SQOI) AND
|
||||
(NOT DEFINED arg_MMAP_SPLIT_HEIGHT OR arg_MMAP_SPLIT_HEIGHT LESS 1) )
|
||||
message(FATAL_ERROR "MMAP_SPLIT_HEIGHT must be defined and its value >= 1 when MMAP_SUPPORT_SJPG, MMAP_SUPPORT_SPNG, or MMAP_SUPPORT_SQOI is enabled.")
|
||||
endif()
|
||||
|
||||
if(DEFINED arg_MMAP_SPLIT_HEIGHT)
|
||||
if(NOT (arg_MMAP_SUPPORT_SJPG OR arg_MMAP_SUPPORT_SPNG OR arg_MMAP_SUPPORT_SQOI))
|
||||
message(FATAL_ERROR "MMAP_SPLIT_HEIGHT depends on MMAP_SUPPORT_SJPG || MMAP_SUPPORT_SPNG || MMAP_SUPPORT_SQOI.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(arg_MMAP_SUPPORT_RAW AND (arg_MMAP_SUPPORT_SJPG OR arg_MMAP_SUPPORT_SPNG OR arg_MMAP_SUPPORT_QOI OR arg_MMAP_SUPPORT_SQOI))
|
||||
message(FATAL_ERROR "MMAP_SUPPORT_RAW and MMAP_SUPPORT_SJPG/MMAP_SUPPORT_SPNG/MMAP_SUPPORT_QOI/MMAP_SUPPORT_SQOI cannot be enabled at the same time.")
|
||||
endif()
|
||||
|
||||
# Try to install Pillow using pip
|
||||
idf_build_get_property(python PYTHON)
|
||||
execute_process(
|
||||
COMMAND ${python} -c "import PIL"
|
||||
RESULT_VARIABLE PIL_FOUND
|
||||
OUTPUT_QUIET
|
||||
ERROR_QUIET
|
||||
)
|
||||
|
||||
if(NOT PIL_FOUND EQUAL 0)
|
||||
message(STATUS "Pillow not found. Attempting to install it using pip...")
|
||||
|
||||
execute_process(
|
||||
COMMAND ${python} -m pip install -U Pillow
|
||||
RESULT_VARIABLE result
|
||||
OUTPUT_VARIABLE output
|
||||
ERROR_VARIABLE error
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
if(result)
|
||||
message(FATAL_ERROR "Failed to install Pillow using pip. Please install it manually.\nError: ${error}")
|
||||
else()
|
||||
message(STATUS "Pillow successfully installed.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# Try to install qoi-conv using pip
|
||||
execute_process(
|
||||
COMMAND ${python} -c "import importlib; importlib.import_module('qoi-conv')"
|
||||
RESULT_VARIABLE PIL_FOUND
|
||||
OUTPUT_QUIET
|
||||
ERROR_QUIET
|
||||
)
|
||||
|
||||
if(NOT PIL_FOUND EQUAL 0)
|
||||
message(STATUS "qoi-conv not found. Attempting to install it using pip...")
|
||||
|
||||
execute_process(
|
||||
COMMAND ${python} -m pip install -U qoi-conv
|
||||
RESULT_VARIABLE result
|
||||
OUTPUT_VARIABLE output
|
||||
ERROR_VARIABLE error
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
ERROR_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
|
||||
if(result)
|
||||
message(FATAL_ERROR "Failed to install qoi-conv using pip. Please install it manually.\nError: ${error}")
|
||||
else()
|
||||
message(STATUS "qoi-conv successfully installed.")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
get_filename_component(base_dir_full_path ${base_dir} ABSOLUTE)
|
||||
get_filename_component(base_dir_name "${base_dir_full_path}" NAME)
|
||||
|
||||
partition_table_get_partition_info(size "--partition-name ${partition}" "size")
|
||||
partition_table_get_partition_info(offset "--partition-name ${partition}" "offset")
|
||||
|
||||
if("${size}" AND "${offset}")
|
||||
|
||||
set(TARGET_COMPONENT "")
|
||||
set(TARGET_COMPONENT_PATH "")
|
||||
|
||||
idf_build_get_property(build_components BUILD_COMPONENTS)
|
||||
foreach(COMPONENT ${build_components})
|
||||
if(COMPONENT MATCHES "esp_mmap_assets" OR COMPONENT MATCHES "espressif__esp_mmap_assets")
|
||||
set(TARGET_COMPONENT ${COMPONENT})
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if(TARGET_COMPONENT STREQUAL "")
|
||||
message(FATAL_ERROR "Component 'esp_mmap_assets' not found.")
|
||||
else()
|
||||
idf_component_get_property(TARGET_COMPONENT_PATH ${TARGET_COMPONENT} COMPONENT_DIR)
|
||||
endif()
|
||||
|
||||
set(image_file ${CMAKE_BINARY_DIR}/mmap_build/${base_dir_name}/${partition}.bin)
|
||||
set(MVMODEL_EXE ${TARGET_COMPONENT_PATH}/spiffs_assets_gen.py)
|
||||
|
||||
if(arg_MMAP_SUPPORT_RAW)
|
||||
foreach(COMPONENT ${build_components})
|
||||
if(COMPONENT MATCHES "^lvgl$" OR COMPONENT MATCHES "^lvgl__lvgl$")
|
||||
set(lvgl_name ${COMPONENT})
|
||||
if(COMPONENT STREQUAL "lvgl")
|
||||
set(lvgl_ver $ENV{LVGL_VERSION})
|
||||
else()
|
||||
idf_component_get_property(lvgl_ver ${lvgl_name} COMPONENT_VERSION)
|
||||
endif()
|
||||
break()
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
if("${lvgl_ver}" STREQUAL "")
|
||||
message("Could not determine LVGL version, assuming v8.x")
|
||||
set(lvgl_ver "8.0.0")
|
||||
endif()
|
||||
message(STATUS "LVGL version: ${lvgl_ver}")
|
||||
endif()
|
||||
|
||||
if(NOT arg_MMAP_SPLIT_HEIGHT)
|
||||
set(arg_MMAP_SPLIT_HEIGHT 0) # Default value
|
||||
endif()
|
||||
|
||||
# Handle IMPORT_INC_PATH parameter
|
||||
if(DEFINED arg_IMPORT_INC_PATH)
|
||||
set(import_include_path ${arg_IMPORT_INC_PATH})
|
||||
else()
|
||||
set(import_include_path ${CMAKE_CURRENT_LIST_DIR})
|
||||
endif()
|
||||
|
||||
string(TOLOWER "${arg_MMAP_SUPPORT_SJPG}" support_sjpg)
|
||||
string(TOLOWER "${arg_MMAP_SUPPORT_SPNG}" support_spng)
|
||||
string(TOLOWER "${arg_MMAP_SUPPORT_QOI}" support_qoi)
|
||||
string(TOLOWER "${arg_MMAP_SUPPORT_SQOI}" support_sqoi)
|
||||
string(TOLOWER "${arg_MMAP_SUPPORT_RAW}" support_raw)
|
||||
string(TOLOWER "${arg_MMAP_RAW_DITHER}" support_raw_dither)
|
||||
string(TOLOWER "${arg_MMAP_RAW_BGR_MODE}" support_raw_bgr)
|
||||
|
||||
set(app_bin_path "${CMAKE_BINARY_DIR}/${CMAKE_PROJECT_NAME}.bin")
|
||||
|
||||
set(CONFIG_FILE_PATH "${CMAKE_BINARY_DIR}/mmap_build/${base_dir_name}.json")
|
||||
configure_file(
|
||||
"${TARGET_COMPONENT_PATH}/config_template.json.in"
|
||||
"${CONFIG_FILE_PATH}"
|
||||
@ONLY
|
||||
)
|
||||
|
||||
add_custom_target(assets_${partition}_bin ALL
|
||||
COMMENT "Move and Pack assets..."
|
||||
COMMAND python ${MVMODEL_EXE} --config "${CONFIG_FILE_PATH}"
|
||||
DEPENDS ${arg_DEPENDS}
|
||||
VERBATIM)
|
||||
|
||||
if(arg_FLASH_APPEND_APP)
|
||||
add_custom_target(assets_${partition}_merge_bin ALL
|
||||
COMMENT "Merge Bin..."
|
||||
COMMAND python ${TARGET_COMPONENT_PATH}/spiffs_assets_gen.py --config "${CONFIG_FILE_PATH}" --merge
|
||||
COMMAND ${CMAKE_COMMAND} -E rm "${build_dir}/.bin_timestamp" # Remove the timestamp file to force re-run
|
||||
DEPENDS assets_${partition}_bin app
|
||||
VERBATIM)
|
||||
endif()
|
||||
|
||||
if(arg_FLASH_IN_PROJECT)
|
||||
set(assets_target "assets_${partition}_bin")
|
||||
|
||||
if(arg_FLASH_APPEND_APP)
|
||||
set(assets_target "assets_${partition}_merge_bin")
|
||||
add_dependencies(app-flash ${assets_target})
|
||||
else()
|
||||
esptool_py_flash_to_partition(flash "${partition}" "${image_file}")
|
||||
endif()
|
||||
|
||||
add_dependencies(flash ${assets_target})
|
||||
endif()
|
||||
|
||||
else()
|
||||
set(message "Failed to create assets bin for partition '${partition}'. "
|
||||
"Check project configuration if using the correct partition table file.")
|
||||
fail_at_build_time(assets_${partition}_bin "${message}")
|
||||
endif()
|
||||
endfunction()
|
||||
@@ -0,0 +1,647 @@
|
||||
# SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
import io
|
||||
import os
|
||||
import argparse
|
||||
import json
|
||||
import shutil
|
||||
import math
|
||||
import sys
|
||||
import time
|
||||
import numpy as np
|
||||
import importlib
|
||||
import subprocess
|
||||
import urllib.request
|
||||
|
||||
from PIL import Image
|
||||
from datetime import datetime
|
||||
from dataclasses import dataclass
|
||||
from typing import List
|
||||
from pathlib import Path
|
||||
from packaging import version
|
||||
|
||||
sys.dont_write_bytecode = True
|
||||
|
||||
GREEN = '\033[1;32m'
|
||||
RED = '\033[1;31m'
|
||||
RESET = '\033[0m'
|
||||
|
||||
@dataclass
|
||||
class AssetCopyConfig:
|
||||
assets_path: str
|
||||
target_path: str
|
||||
spng_enable: bool
|
||||
sjpg_enable: bool
|
||||
qoi_enable: bool
|
||||
sqoi_enable: bool
|
||||
row_enable: bool
|
||||
support_format: List[str]
|
||||
split_height: int
|
||||
|
||||
@dataclass
|
||||
class PackModelsConfig:
|
||||
target_path: str
|
||||
include_path: str
|
||||
image_file: str
|
||||
assets_path: str
|
||||
name_length: int
|
||||
|
||||
def generate_header_filename(path):
|
||||
asset_name = os.path.basename(path)
|
||||
|
||||
header_filename = f'mmap_generate_{asset_name}.h'
|
||||
return header_filename
|
||||
|
||||
def compute_checksum(data):
|
||||
checksum = sum(data) & 0xFFFF
|
||||
return checksum
|
||||
|
||||
def sort_key(filename):
|
||||
basename, extension = os.path.splitext(filename)
|
||||
return extension, basename
|
||||
|
||||
def download_v8_script(convert_path):
|
||||
"""
|
||||
Ensure that the lvgl_image_converter repository is present at the specified path.
|
||||
If not, clone the repository. Then, checkout to a specific commit.
|
||||
|
||||
Parameters:
|
||||
- convert_path (str): The directory path where lvgl_image_converter should be located.
|
||||
"""
|
||||
|
||||
# Check if convert_path is not empty
|
||||
if convert_path:
|
||||
# If the directory does not exist, create it and clone the repository
|
||||
if not os.path.exists(convert_path):
|
||||
os.makedirs(convert_path, exist_ok=True)
|
||||
try:
|
||||
subprocess.run(
|
||||
['git', 'clone', 'https://github.com/W-Mai/lvgl_image_converter.git', convert_path],
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
check=True
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f'Git clone failed: {e}')
|
||||
sys.exit(1)
|
||||
|
||||
# Checkout to the specific commit
|
||||
try:
|
||||
subprocess.run(
|
||||
['git', 'checkout', '9174634e9dcc1b21a63668969406897aad650f35'],
|
||||
cwd=convert_path,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
check=True
|
||||
)
|
||||
except subprocess.CalledProcessError as e:
|
||||
print(f'Failed to checkout to the specific commit: {e}')
|
||||
sys.exit(1)
|
||||
else:
|
||||
print('Error: convert_path is NULL')
|
||||
sys.exit(1)
|
||||
|
||||
def download_v9_script(url: str, destination: str) -> None:
|
||||
"""
|
||||
Download a Python script from a URL to a local destination.
|
||||
|
||||
Parameters:
|
||||
- url (str): URL to download the script from.
|
||||
- destination (str): Local path to save the downloaded script.
|
||||
|
||||
Raises:
|
||||
- Exception: If the download fails.
|
||||
"""
|
||||
file_path = Path(destination)
|
||||
|
||||
# Check if the file already exists
|
||||
if file_path.exists():
|
||||
if file_path.is_file():
|
||||
return
|
||||
|
||||
try:
|
||||
# Create the parent directories if they do not exist
|
||||
file_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Open the URL and retrieve the data
|
||||
with urllib.request.urlopen(url) as response, open(file_path, 'wb') as out_file:
|
||||
data = response.read() # Read the entire response
|
||||
out_file.write(data) # Write data to the local file
|
||||
|
||||
except urllib.error.HTTPError as e:
|
||||
print(f'HTTP Error: {e.code} - {e.reason} when accessing {url}')
|
||||
sys.exit(1)
|
||||
except urllib.error.URLError as e:
|
||||
print(f'URL Error: {e.reason} when accessing {url}')
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f'An unexpected error occurred: {e}')
|
||||
sys.exit(1)
|
||||
|
||||
def split_image(im, block_size, input_dir, ext, convert_to_qoi):
|
||||
"""Splits the image into blocks based on the block size."""
|
||||
width, height = im.size
|
||||
|
||||
if block_size:
|
||||
splits = math.ceil(height / block_size)
|
||||
else:
|
||||
splits = 1
|
||||
|
||||
for i in range(splits):
|
||||
if i < splits - 1:
|
||||
crop = im.crop((0, i * block_size, width, (i + 1) * block_size))
|
||||
else:
|
||||
crop = im.crop((0, i * block_size, width, height))
|
||||
|
||||
output_path = os.path.join(input_dir, str(i) + ext)
|
||||
crop.save(output_path, quality=100)
|
||||
|
||||
qoi_module = importlib.import_module('qoi-conv.qoi')
|
||||
Qoi = qoi_module.Qoi
|
||||
replace_extension = qoi_module.replace_extension
|
||||
|
||||
if convert_to_qoi:
|
||||
with Image.open(output_path) as img:
|
||||
if img.mode != 'RGBA':
|
||||
img = img.convert('RGBA')
|
||||
|
||||
img_data = np.asarray(img)
|
||||
out_path = qoi_module.replace_extension(output_path, 'qoi')
|
||||
new_image = qoi_module.Qoi().save(out_path, img_data)
|
||||
os.remove(output_path)
|
||||
|
||||
|
||||
return width, height, splits
|
||||
|
||||
def create_header(width, height, splits, split_height, lenbuf, ext):
|
||||
"""Creates the header for the output file based on the format."""
|
||||
header = bytearray()
|
||||
|
||||
if ext.lower() == '.jpg':
|
||||
header += bytearray('_SJPG__'.encode('UTF-8'))
|
||||
elif ext.lower() == '.png':
|
||||
header += bytearray('_SPNG__'.encode('UTF-8'))
|
||||
elif ext.lower() == '.qoi':
|
||||
header += bytearray('_SQOI__'.encode('UTF-8'))
|
||||
|
||||
# 6 BYTES VERSION
|
||||
header += bytearray(('\x00V1.00\x00').encode('UTF-8'))
|
||||
|
||||
# WIDTH 2 BYTES
|
||||
header += width.to_bytes(2, byteorder='little')
|
||||
|
||||
# HEIGHT 2 BYTES
|
||||
header += height.to_bytes(2, byteorder='little')
|
||||
|
||||
# NUMBER OF ITEMS 2 BYTES
|
||||
header += splits.to_bytes(2, byteorder='little')
|
||||
|
||||
# SPLIT HEIGHT 2 BYTES
|
||||
header += split_height.to_bytes(2, byteorder='little')
|
||||
|
||||
for item_len in lenbuf:
|
||||
# LENGTH 2 BYTES
|
||||
header += item_len.to_bytes(2, byteorder='little')
|
||||
|
||||
return header
|
||||
|
||||
def save_image(output_file_path, header, split_data):
|
||||
"""Saves the image with the constructed header and split data."""
|
||||
with open(output_file_path, 'wb') as f:
|
||||
if header is not None:
|
||||
f.write(header + split_data)
|
||||
else:
|
||||
f.write(split_data)
|
||||
|
||||
def handle_lvgl_version_v9(input_file: str, input_dir: str,
|
||||
input_filename: str, convert_path: str) -> None:
|
||||
"""
|
||||
Handle conversion for LVGL versions greater than 9.0.
|
||||
|
||||
Parameters:
|
||||
- input_file (str): Path to the input image file.
|
||||
- input_dir (str): Directory of the input file.
|
||||
- input_filename (str): Name of the input file.
|
||||
- convert_path (str): Path for conversion scripts and outputs.
|
||||
"""
|
||||
|
||||
convert_file = os.path.join(convert_path, 'LVGLImage.py')
|
||||
lvgl_image_url = 'https://raw.githubusercontent.com/lvgl/lvgl/master/scripts/LVGLImage.py'
|
||||
|
||||
download_v9_script(url=lvgl_image_url, destination=convert_file)
|
||||
lvgl_script = Path(convert_file)
|
||||
|
||||
cmd = [
|
||||
'python',
|
||||
str(lvgl_script),
|
||||
'--ofmt', 'BIN',
|
||||
'--cf', config_data['support_raw_cf'],
|
||||
'--compress', 'NONE',
|
||||
'--output', str(input_dir),
|
||||
input_file
|
||||
]
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
cmd,
|
||||
check=True,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE,
|
||||
text=True
|
||||
)
|
||||
print(f'Completed {input_filename} -> BIN')
|
||||
except subprocess.CalledProcessError as e:
|
||||
print('An error occurred while executing LVGLImage.py:')
|
||||
print(e.stderr)
|
||||
sys.exit(e.returncode)
|
||||
|
||||
def handle_lvgl_version_v8(input_file: str, input_dir: str, input_filename: str, convert_path: str) -> None:
|
||||
"""
|
||||
Handle conversion for supported LVGL versions (<= 9.0).
|
||||
|
||||
Parameters:
|
||||
- input_file (str): Path to the input image file.
|
||||
- input_dir (str): Directory of the input file.
|
||||
- input_filename (str): Name of the input file.
|
||||
- convert_path (str): Path for conversion scripts and outputs.
|
||||
"""
|
||||
|
||||
download_v8_script(convert_path=convert_path)
|
||||
|
||||
if convert_path not in sys.path:
|
||||
sys.path.append(convert_path)
|
||||
|
||||
try:
|
||||
import lv_img_conv
|
||||
except ImportError as e:
|
||||
print(f"Failed to import 'lv_img_conv' from '{convert_path}': {e}")
|
||||
sys.exit(1)
|
||||
|
||||
try:
|
||||
lv_img_conv.conv_one_file(
|
||||
root=Path(input_dir),
|
||||
filepath=Path(input_file),
|
||||
f=config_data['support_raw_ff'],
|
||||
cf=config_data['support_raw_cf'],
|
||||
ff='BIN',
|
||||
dither=config_data['support_raw_dither'],
|
||||
bgr_mode=config_data['support_raw_bgr'],
|
||||
)
|
||||
print(f'Completed {input_filename} -> BIN')
|
||||
except KeyError as e:
|
||||
print(f'Missing configuration key: {e}')
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f'An error occurred during conversion: {e}')
|
||||
sys.exit(1)
|
||||
|
||||
def process_image(input_file, height_str, output_extension, convert_to_qoi=False):
|
||||
"""Main function to process the image and save it as .sjpg, .spng, or .sqoi."""
|
||||
try:
|
||||
SPLIT_HEIGHT = int(height_str)
|
||||
if SPLIT_HEIGHT < 0:
|
||||
raise ValueError('Height must be a positive integer')
|
||||
except ValueError as e:
|
||||
print('Error: Height must be a positive integer')
|
||||
sys.exit(1)
|
||||
|
||||
input_dir, input_filename = os.path.split(input_file)
|
||||
base_filename, ext = os.path.splitext(input_filename)
|
||||
OUTPUT_FILE_NAME = base_filename
|
||||
|
||||
try:
|
||||
im = Image.open(input_file)
|
||||
except Exception as e:
|
||||
print('Error:', e)
|
||||
sys.exit(0)
|
||||
|
||||
width, height, splits = split_image(im, SPLIT_HEIGHT, input_dir, ext, convert_to_qoi)
|
||||
|
||||
split_data = bytearray()
|
||||
lenbuf = []
|
||||
|
||||
if convert_to_qoi:
|
||||
ext = '.qoi'
|
||||
|
||||
for i in range(splits):
|
||||
with open(os.path.join(input_dir, str(i) + ext), 'rb') as f:
|
||||
a = f.read()
|
||||
split_data += a
|
||||
lenbuf.append(len(a))
|
||||
os.remove(os.path.join(input_dir, str(i) + ext))
|
||||
|
||||
header = None
|
||||
if splits == 1 and convert_to_qoi:
|
||||
output_file_path = os.path.join(input_dir, OUTPUT_FILE_NAME + ext)
|
||||
else:
|
||||
header = create_header(width, height, splits, SPLIT_HEIGHT, lenbuf, ext)
|
||||
output_file_path = os.path.join(input_dir, OUTPUT_FILE_NAME + output_extension)
|
||||
|
||||
save_image(output_file_path, header, split_data)
|
||||
|
||||
print('Completed', input_filename, '->', os.path.basename(output_file_path))
|
||||
|
||||
def convert_image_to_qoi(input_file, height_str):
|
||||
process_image(input_file, height_str, '.sqoi', convert_to_qoi=True)
|
||||
|
||||
def convert_image_to_simg(input_file, height_str):
|
||||
input_dir, input_filename = os.path.split(input_file)
|
||||
_, ext = os.path.splitext(input_filename)
|
||||
output_extension = '.sjpg' if ext.lower() == '.jpg' else '.spng'
|
||||
process_image(input_file, height_str, output_extension, convert_to_qoi=False)
|
||||
|
||||
def convert_image_to_raw(input_file: str) -> None:
|
||||
"""
|
||||
Convert an image to raw binary format compatible with LVGL.
|
||||
|
||||
Parameters:
|
||||
- input_file (str): Path to the input image file.
|
||||
|
||||
Raises:
|
||||
- FileNotFoundError: If required scripts are not found.
|
||||
- subprocess.CalledProcessError: If the external conversion script fails.
|
||||
- KeyError: If required keys are missing in config_data.
|
||||
"""
|
||||
input_dir, input_filename = os.path.split(input_file)
|
||||
_, ext = os.path.splitext(input_filename)
|
||||
convert_path = os.path.join(os.path.dirname(input_file), 'lvgl_image_converter')
|
||||
lvgl_ver_str = config_data.get('lvgl_ver', '9.0.0')
|
||||
|
||||
try:
|
||||
lvgl_version = version.parse(lvgl_ver_str)
|
||||
except version.InvalidVersion:
|
||||
print(f'Invalid LVGL version format: {lvgl_ver_str}')
|
||||
sys.exit(1)
|
||||
|
||||
if lvgl_version >= version.parse('9.0.0'):
|
||||
handle_lvgl_version_v9(
|
||||
input_file=input_file,
|
||||
input_dir=input_dir,
|
||||
input_filename=input_filename,
|
||||
convert_path=convert_path
|
||||
)
|
||||
else:
|
||||
handle_lvgl_version_v8(
|
||||
input_file=input_file,
|
||||
input_dir=input_dir,
|
||||
input_filename=input_filename,
|
||||
convert_path=convert_path
|
||||
)
|
||||
|
||||
def pack_assets(config: PackModelsConfig):
|
||||
"""
|
||||
Pack models based on the provided configuration.
|
||||
"""
|
||||
|
||||
target_path = config.target_path
|
||||
assets_include_path = config.include_path
|
||||
out_file = config.image_file
|
||||
assets_path = config.assets_path
|
||||
max_name_len = config.name_length
|
||||
|
||||
merged_data = bytearray()
|
||||
file_info_list = []
|
||||
skip_files = ['config.json', 'lvgl_image_converter']
|
||||
|
||||
file_list = sorted(os.listdir(target_path), key=sort_key)
|
||||
for filename in file_list:
|
||||
if filename in skip_files:
|
||||
continue
|
||||
|
||||
file_path = os.path.join(target_path, filename)
|
||||
file_name = os.path.basename(file_path)
|
||||
file_size = os.path.getsize(file_path)
|
||||
|
||||
try:
|
||||
img = Image.open(file_path)
|
||||
width, height = img.size
|
||||
except Exception as e:
|
||||
# print("Error:", e)
|
||||
_, file_extension = os.path.splitext(file_path)
|
||||
if file_extension.lower() in ['.sjpg', '.spng', '.sqoi']:
|
||||
offset = 14
|
||||
with open(file_path, 'rb') as f:
|
||||
f.seek(offset)
|
||||
width_bytes = f.read(2)
|
||||
height_bytes = f.read(2)
|
||||
width = int.from_bytes(width_bytes, byteorder='little')
|
||||
height = int.from_bytes(height_bytes, byteorder='little')
|
||||
else:
|
||||
width, height = 0, 0
|
||||
|
||||
file_info_list.append((file_name, len(merged_data), file_size, width, height))
|
||||
# Add 0x5A5A prefix to merged_data
|
||||
merged_data.extend(b'\x5A' * 2)
|
||||
|
||||
with open(file_path, 'rb') as bin_file:
|
||||
bin_data = bin_file.read()
|
||||
|
||||
merged_data.extend(bin_data)
|
||||
|
||||
total_files = len(file_info_list)
|
||||
|
||||
mmap_table = bytearray()
|
||||
for file_name, offset, file_size, width, height in file_info_list:
|
||||
if len(file_name) > int(max_name_len):
|
||||
print(f'\033[1;33mWarn:\033[0m "{file_name}" exceeds {max_name_len} bytes and will be truncated.')
|
||||
fixed_name = file_name.ljust(int(max_name_len), '\0')[:int(max_name_len)]
|
||||
mmap_table.extend(fixed_name.encode('utf-8'))
|
||||
mmap_table.extend(file_size.to_bytes(4, byteorder='little'))
|
||||
mmap_table.extend(offset.to_bytes(4, byteorder='little'))
|
||||
mmap_table.extend(width.to_bytes(2, byteorder='little'))
|
||||
mmap_table.extend(height.to_bytes(2, byteorder='little'))
|
||||
|
||||
combined_data = mmap_table + merged_data
|
||||
combined_checksum = compute_checksum(combined_data)
|
||||
combined_data_length = len(combined_data).to_bytes(4, byteorder='little')
|
||||
header_data = total_files.to_bytes(4, byteorder='little') + combined_checksum.to_bytes(4, byteorder='little')
|
||||
final_data = header_data + combined_data_length + combined_data
|
||||
|
||||
with open(out_file, 'wb') as output_bin:
|
||||
output_bin.write(final_data)
|
||||
|
||||
os.makedirs(assets_include_path, exist_ok=True)
|
||||
current_year = datetime.now().year
|
||||
|
||||
asset_name = os.path.basename(assets_path)
|
||||
file_path = os.path.join(assets_include_path, f'mmap_generate_{asset_name}.h')
|
||||
with open(file_path, 'w') as output_header:
|
||||
output_header.write('/*\n')
|
||||
output_header.write(' * SPDX-FileCopyrightText: 2022-{} Espressif Systems (Shanghai) CO LTD\n'.format(current_year))
|
||||
output_header.write(' *\n')
|
||||
output_header.write(' * SPDX-License-Identifier: Apache-2.0\n')
|
||||
output_header.write(' */\n\n')
|
||||
output_header.write('/**\n')
|
||||
output_header.write(' * @file\n')
|
||||
output_header.write(" * @brief This file was generated by esp_mmap_assets, don't modify it\n")
|
||||
output_header.write(' */\n\n')
|
||||
output_header.write('#pragma once\n\n')
|
||||
output_header.write("#include \"esp_mmap_assets.h\"\n\n")
|
||||
output_header.write(f'#define MMAP_{asset_name.upper()}_FILES {total_files}\n')
|
||||
output_header.write(f'#define MMAP_{asset_name.upper()}_CHECKSUM 0x{combined_checksum:04X}\n\n')
|
||||
output_header.write(f'enum MMAP_{asset_name.upper()}_LISTS {{\n')
|
||||
|
||||
for i, (file_name, _, _, _, _) in enumerate(file_info_list):
|
||||
enum_name = file_name.replace('.', '_')
|
||||
output_header.write(f' MMAP_{asset_name.upper()}_{enum_name.upper()} = {i}, /*!< {file_name} */\n')
|
||||
|
||||
output_header.write('};\n')
|
||||
|
||||
print(f'All bin files have been merged into {os.path.basename(out_file)}')
|
||||
|
||||
def copy_assets(config: AssetCopyConfig):
|
||||
"""
|
||||
Copy assets to target_path based on the provided configuration.
|
||||
"""
|
||||
format_tuple = tuple(config.support_format)
|
||||
assets_path = config.assets_path
|
||||
target_path = config.target_path
|
||||
|
||||
for filename in os.listdir(assets_path):
|
||||
if any(filename.endswith(suffix) for suffix in format_tuple):
|
||||
source_file = os.path.join(assets_path, filename)
|
||||
target_file = os.path.join(target_path, filename)
|
||||
shutil.copyfile(source_file, target_file)
|
||||
|
||||
conversion_map = {
|
||||
'.jpg': [
|
||||
(config.sjpg_enable, convert_image_to_simg),
|
||||
(config.qoi_enable, convert_image_to_qoi),
|
||||
],
|
||||
'.png': [
|
||||
(config.spng_enable, convert_image_to_simg),
|
||||
(config.qoi_enable, convert_image_to_qoi),
|
||||
],
|
||||
}
|
||||
|
||||
file_ext = os.path.splitext(filename)[1].lower()
|
||||
conversions = conversion_map.get(file_ext, [])
|
||||
converted = False
|
||||
|
||||
for enable_flag, convert_func in conversions:
|
||||
if enable_flag:
|
||||
convert_func(target_file, config.split_height)
|
||||
os.remove(target_file)
|
||||
converted = True
|
||||
break
|
||||
|
||||
if not converted and config.row_enable:
|
||||
convert_image_to_raw(target_file)
|
||||
os.remove(target_file)
|
||||
else:
|
||||
print(f'No match found for file: {filename}, format_tuple: {format_tuple}')
|
||||
|
||||
def process_assets_build(config_data):
|
||||
assets_path = config_data['assets_path']
|
||||
image_file = config_data['image_file']
|
||||
target_path = os.path.dirname(image_file)
|
||||
include_path = config_data['include_path']
|
||||
name_length = config_data['name_length']
|
||||
split_height = config_data['split_height']
|
||||
support_format = [fmt.strip() for fmt in config_data['support_format'].split(',')]
|
||||
|
||||
copy_config = AssetCopyConfig(
|
||||
assets_path=assets_path,
|
||||
target_path=target_path,
|
||||
spng_enable=config_data['support_spng'],
|
||||
sjpg_enable=config_data['support_sjpg'],
|
||||
qoi_enable=config_data['support_qoi'],
|
||||
sqoi_enable=config_data['support_sqoi'],
|
||||
row_enable=config_data['support_raw'],
|
||||
support_format=support_format,
|
||||
split_height=split_height
|
||||
)
|
||||
|
||||
pack_config = PackModelsConfig(
|
||||
target_path=target_path,
|
||||
include_path=include_path,
|
||||
image_file=image_file,
|
||||
assets_path=assets_path,
|
||||
name_length=name_length
|
||||
)
|
||||
|
||||
print('--support_format:', support_format)
|
||||
|
||||
if '.jpg' in support_format or '.png' in support_format:
|
||||
print('--support_spng:', copy_config.spng_enable)
|
||||
print('--support_sjpg:', copy_config.sjpg_enable)
|
||||
print('--support_qoi:', copy_config.qoi_enable)
|
||||
print('--support_raw:', copy_config.row_enable)
|
||||
|
||||
if copy_config.sqoi_enable:
|
||||
print('--support_sqoi:', copy_config.sqoi_enable)
|
||||
if copy_config.spng_enable or copy_config.sjpg_enable or copy_config.sqoi_enable:
|
||||
print('--split_height:', copy_config.split_height)
|
||||
if copy_config.row_enable:
|
||||
print('--lvgl_version:', config_data['lvgl_ver'])
|
||||
|
||||
if not os.path.exists(target_path):
|
||||
os.makedirs(target_path, exist_ok=True)
|
||||
for filename in os.listdir(target_path):
|
||||
file_path = os.path.join(target_path, filename)
|
||||
if os.path.isfile(file_path) or os.path.islink(file_path):
|
||||
os.unlink(file_path)
|
||||
elif os.path.isdir(file_path):
|
||||
shutil.rmtree(file_path)
|
||||
|
||||
copy_assets(copy_config)
|
||||
pack_assets(pack_config)
|
||||
|
||||
total_size = os.path.getsize(os.path.join(target_path, image_file))
|
||||
recommended_size = math.ceil(total_size / 1024)
|
||||
partition_size = math.ceil(int(config_data['assets_size'], 16))
|
||||
|
||||
print(f'{"Total size:":<30} {GREEN}{total_size / 1024:>8.2f}K ({total_size}){RESET}')
|
||||
print(f'{"Partition size:":<30} {GREEN}{partition_size / 1024:>8.2f}K ({partition_size}){RESET}')
|
||||
|
||||
if int(config_data['assets_size'], 16) <= total_size:
|
||||
print(f'Recommended partition size: {GREEN}{recommended_size}K{RESET}')
|
||||
print(f'{RED}Error:Binary size exceeds partition size.{RESET}')
|
||||
sys.exit(1)
|
||||
|
||||
def process_assets_merge(config_data):
|
||||
app_bin_path = config_data['app_bin_path']
|
||||
image_file = config_data['image_file']
|
||||
target_path = os.path.dirname(image_file)
|
||||
|
||||
combined_bin_path = os.path.join(target_path, 'combined.bin')
|
||||
append_bin_path = os.path.join(target_path, image_file)
|
||||
|
||||
app_size = os.path.getsize(app_bin_path)
|
||||
asset_size = os.path.getsize(append_bin_path)
|
||||
total_size = asset_size + app_size
|
||||
recommended_size = math.ceil(total_size / 1024)
|
||||
partition_size = math.ceil(int(config_data['assets_size'], 16))
|
||||
|
||||
print(f'{"Asset size:":<30} {GREEN}{asset_size / 1024:>8.2f}K ({asset_size}){RESET}')
|
||||
print(f'{"App size:":<30} {GREEN}{app_size / 1024:>8.2f}K ({app_size}){RESET}')
|
||||
print(f'{"Total size:":<30} {GREEN}{total_size / 1024:>8.2f}K ({total_size}){RESET}')
|
||||
print(f'{"Partition size:":<30} {GREEN}{partition_size / 1024:>8.2f}K ({partition_size}){RESET}')
|
||||
|
||||
if total_size > partition_size:
|
||||
print(f'Recommended partition size: {GREEN}{recommended_size}K{RESET}')
|
||||
print(f'{RED}Error:Binary size exceeds partition size.{RESET}')
|
||||
sys.exit(1)
|
||||
|
||||
with open(combined_bin_path, 'wb') as combined_bin:
|
||||
with open(app_bin_path, 'rb') as app_bin:
|
||||
combined_bin.write(app_bin.read())
|
||||
with open(append_bin_path, 'rb') as img_bin:
|
||||
combined_bin.write(img_bin.read())
|
||||
|
||||
shutil.move(combined_bin_path, app_bin_path)
|
||||
print(f'Append bin created: {os.path.basename(app_bin_path)}')
|
||||
|
||||
if __name__ == '__main__':
|
||||
parser = argparse.ArgumentParser(description='Move and Pack assets.')
|
||||
parser.add_argument('--config', required=True, help='Path to the configuration file')
|
||||
parser.add_argument('--merge', action='store_true', help='Merge assets with app binary')
|
||||
args = parser.parse_args()
|
||||
|
||||
with open(args.config, 'r') as f:
|
||||
config_data = json.load(f)
|
||||
|
||||
if args.merge:
|
||||
process_assets_merge(config_data)
|
||||
else:
|
||||
process_assets_build(config_data)
|
||||
@@ -0,0 +1,6 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/unit-test-app/components")
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(test_esp_map_assets)
|
||||
@@ -0,0 +1,31 @@
|
||||
set(IMPORT_PATH "${CMAKE_SOURCE_DIR}")
|
||||
|
||||
idf_component_register(
|
||||
SRC_DIRS "."
|
||||
INCLUDE_DIRS "." "${IMPORT_PATH}"
|
||||
)
|
||||
|
||||
set(DIR_SRC "${PROJECT_DIR}/spiffs_assets")
|
||||
set(DIR_INDEPEND "${CMAKE_BINARY_DIR}/assert_independ")
|
||||
set(DIR_APPEND "${CMAKE_BINARY_DIR}/assert_append")
|
||||
|
||||
file(GLOB_RECURSE ASSET_FILES "${DIR_SRC}/*")
|
||||
|
||||
file(INSTALL ${ASSET_FILES} DESTINATION ${DIR_INDEPEND} USE_SOURCE_PERMISSIONS)
|
||||
file(INSTALL ${ASSET_FILES} DESTINATION ${DIR_APPEND} USE_SOURCE_PERMISSIONS)
|
||||
|
||||
spiffs_create_partition_assets(
|
||||
assets
|
||||
${DIR_INDEPEND}
|
||||
FLASH_IN_PROJECT
|
||||
MMAP_FILE_SUPPORT_FORMAT ".jpg,.png,.ttf"
|
||||
)
|
||||
|
||||
spiffs_create_partition_assets(
|
||||
factory
|
||||
${DIR_APPEND}
|
||||
FLASH_IN_PROJECT
|
||||
FLASH_APPEND_APP
|
||||
MMAP_FILE_SUPPORT_FORMAT ".jpg,.png,.ttf"
|
||||
IMPORT_INC_PATH "${IMPORT_PATH}"
|
||||
)
|
||||
@@ -0,0 +1,6 @@
|
||||
## IDF Component Manager Manifest File
|
||||
dependencies:
|
||||
idf: ">=5.0"
|
||||
esp_mmap_assets:
|
||||
version: "*"
|
||||
override_path: "../../../esp_mmap_assets"
|
||||
@@ -0,0 +1,178 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "esp_err.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_check.h"
|
||||
#include "string.h"
|
||||
|
||||
#include "unity.h"
|
||||
#include "unity_test_runner.h"
|
||||
#include "unity_test_utils_memory.h"
|
||||
|
||||
#include "mmap_generate_assert_append.h"
|
||||
#include "mmap_generate_assert_independ.h"
|
||||
|
||||
static const char *TAG = "assets_test";
|
||||
|
||||
static int is_png(const uint8_t *raw_data, size_t len)
|
||||
{
|
||||
const uint8_t png_signature[] = {0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a};
|
||||
if (len < sizeof(png_signature)) {
|
||||
return false;
|
||||
}
|
||||
return memcmp(png_signature, raw_data, sizeof(png_signature)) == 0;
|
||||
}
|
||||
|
||||
static int is_jpg(const uint8_t *raw_data, size_t len)
|
||||
{
|
||||
const uint8_t jpg_signature[] = {0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46};
|
||||
if (len < sizeof(jpg_signature)) {
|
||||
return false;
|
||||
}
|
||||
return memcmp(jpg_signature, raw_data, sizeof(jpg_signature)) == 0;
|
||||
}
|
||||
|
||||
void print_file_list(mmap_assets_handle_t handle, int file_num)
|
||||
{
|
||||
for (int i = 0; i < MMAP_ASSERT_INDEPEND_FILES; i++) {
|
||||
const char *name = mmap_assets_get_name(handle, i);
|
||||
const uint8_t *mem = mmap_assets_get_mem(handle, i);
|
||||
int size = mmap_assets_get_size(handle, i);
|
||||
int width = mmap_assets_get_width(handle, i);
|
||||
int height = mmap_assets_get_height(handle, i);
|
||||
|
||||
ESP_LOGI(TAG, "name:[%s], mem:[%p], size:[%d bytes], w:[%d], h:[%d]", name, mem, size, width, height);
|
||||
|
||||
if (strstr(name, ".png")) {
|
||||
TEST_ASSERT_TRUE(is_png(mem, size));
|
||||
} else if (strstr(name, ".jpg")) {
|
||||
TEST_ASSERT_TRUE(is_jpg(mem, size));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
TEST_CASE("test assets mmap table", "[mmap_assets][mmap_enable][Independent partition]")
|
||||
{
|
||||
mmap_assets_handle_t asset_handle;
|
||||
|
||||
const mmap_assets_config_t config = {
|
||||
.partition_label = "assets",
|
||||
.max_files = MMAP_ASSERT_INDEPEND_FILES,
|
||||
.checksum = MMAP_ASSERT_INDEPEND_CHECKSUM,
|
||||
.flags = {
|
||||
.mmap_enable = true,
|
||||
.app_bin_check = false,
|
||||
.full_check = true,
|
||||
.metadata_check = true,
|
||||
},
|
||||
};
|
||||
|
||||
TEST_ESP_OK(mmap_assets_new(&config, &asset_handle));
|
||||
|
||||
int stored_files = mmap_assets_get_stored_files(asset_handle);
|
||||
ESP_LOGI(TAG, "stored_files:%d", stored_files);
|
||||
|
||||
print_file_list(asset_handle, MMAP_ASSERT_INDEPEND_FILES);
|
||||
|
||||
mmap_assets_del(asset_handle);
|
||||
}
|
||||
|
||||
TEST_CASE("test assets mmap table", "[mmap_assets][mmap_enable][Append Partition]")
|
||||
{
|
||||
mmap_assets_handle_t asset_handle;
|
||||
|
||||
const mmap_assets_config_t config = {
|
||||
.partition_label = "factory",
|
||||
.max_files = MMAP_ASSERT_APPEND_FILES,
|
||||
.checksum = MMAP_ASSERT_APPEND_CHECKSUM,
|
||||
.flags = {
|
||||
.mmap_enable = true,
|
||||
.full_check = true,
|
||||
},
|
||||
};
|
||||
|
||||
TEST_ESP_OK(mmap_assets_new(&config, &asset_handle));
|
||||
|
||||
int stored_files = mmap_assets_get_stored_files(asset_handle);
|
||||
ESP_LOGI(TAG, "stored_files:%d", stored_files);
|
||||
|
||||
print_file_list(asset_handle, MMAP_ASSERT_APPEND_FILES);
|
||||
|
||||
mmap_assets_del(asset_handle);
|
||||
}
|
||||
|
||||
TEST_CASE("test assets mmap table", "[mmap_assets][mmap_disable][Independent partition]")
|
||||
{
|
||||
mmap_assets_handle_t asset_handle;
|
||||
|
||||
const mmap_assets_config_t config = {
|
||||
.partition_label = "assets",
|
||||
.max_files = MMAP_ASSERT_INDEPEND_FILES,
|
||||
.checksum = MMAP_ASSERT_INDEPEND_CHECKSUM,
|
||||
.flags = {
|
||||
.mmap_enable = false,
|
||||
.app_bin_check = false,
|
||||
.full_check = true,
|
||||
.metadata_check = true,
|
||||
},
|
||||
};
|
||||
|
||||
TEST_ESP_OK(mmap_assets_new(&config, &asset_handle));
|
||||
|
||||
int stored_files = mmap_assets_get_stored_files(asset_handle);
|
||||
ESP_LOGI(TAG, "stored_files:%d", stored_files);
|
||||
|
||||
for (int i = 0; i < MMAP_ASSERT_INDEPEND_FILES; i++) {
|
||||
const char *name = mmap_assets_get_name(asset_handle, i);
|
||||
const uint8_t *mem = mmap_assets_get_mem(asset_handle, i);
|
||||
int size = mmap_assets_get_size(asset_handle, i);
|
||||
int width = mmap_assets_get_width(asset_handle, i);
|
||||
int height = mmap_assets_get_height(asset_handle, i);
|
||||
|
||||
ESP_LOGI(TAG, "name:[%s], mem:[%p], size:[%d bytes], w:[%d], h:[%d]", name, mem, size, width, height);
|
||||
|
||||
uint8_t load_data[20];
|
||||
mmap_assets_copy_mem(asset_handle, (size_t)mem, load_data, sizeof(load_data));
|
||||
|
||||
if (strstr(name, ".png")) {
|
||||
TEST_ASSERT_TRUE(is_png((const uint8_t *)load_data, size));
|
||||
} else if (strstr(name, ".jpg")) {
|
||||
TEST_ASSERT_TRUE(is_jpg((const uint8_t *)load_data, size));
|
||||
}
|
||||
}
|
||||
|
||||
mmap_assets_del(asset_handle);
|
||||
}
|
||||
|
||||
// Some resources are lazy allocated in the LCD driver, the threadhold is left for that case
|
||||
#define TEST_MEMORY_LEAK_THRESHOLD (500)
|
||||
|
||||
static size_t before_free_8bit;
|
||||
static size_t before_free_32bit;
|
||||
|
||||
void setUp(void)
|
||||
{
|
||||
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||
}
|
||||
|
||||
void tearDown(void)
|
||||
{
|
||||
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||
unity_utils_check_leak(before_free_8bit, after_free_8bit, "8BIT", TEST_MEMORY_LEAK_THRESHOLD);
|
||||
unity_utils_check_leak(before_free_32bit, after_free_32bit, "32BIT", TEST_MEMORY_LEAK_THRESHOLD);
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
printf("mmap assets TEST \n");
|
||||
unity_run_menu();
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
# Name, Type, SubType, Offset, Size, Flags
|
||||
# Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
|
||||
nvs, data, nvs, , 0x6000,
|
||||
phy_init, data, phy, , 0x1000,
|
||||
factory, app, factory, , 500K,
|
||||
assets, data, spiffs, , 1M,
|
||||
|
@@ -0,0 +1,18 @@
|
||||
# SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
|
||||
@pytest.mark.target('esp32')
|
||||
@pytest.mark.target('esp32c3')
|
||||
@pytest.mark.target('esp32s3')
|
||||
@pytest.mark.target('esp32p4')
|
||||
@pytest.mark.env('generic')
|
||||
@pytest.mark.parametrize(
|
||||
'config',
|
||||
[
|
||||
'defaults',
|
||||
],
|
||||
)
|
||||
def test_esp_mmap_assets(dut: Dut)-> None:
|
||||
dut.run_all_single_board_cases()
|
||||
@@ -0,0 +1,5 @@
|
||||
# For IDF 5.0
|
||||
CONFIG_ESP_TASK_WDT_EN=n
|
||||
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 829 B |
Reference in New Issue
Block a user