diff --git a/.gitignore b/.gitignore index 3bc4bed..7f1d692 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ /archives +/utils/assets +/utils/output diff --git a/docs/streaming_manual.md b/docs/streaming_manual.md index 5fe97c7..bc2eb55 100644 --- a/docs/streaming_manual.md +++ b/docs/streaming_manual.md @@ -72,7 +72,7 @@ display = st7789.ST7789( ) # 加载流式字体 -font = st7789.streaming_font_mpy.StreamingFont("font.bin") +font = st7789.streaming_font_mpy.StreamingFont("font.font") # 使用与普通字体相同的方式显示文字 display.write(font, "你好,世界!", 10, 50, st7789.WHITE) @@ -127,7 +127,7 @@ display = st7789.ST7789( ) # 加载流式图片 -image = st7789.streaming_image.StreamingImage("image.bin") +image = st7789.streaming_image.StreamingImage("image.img") # 显示图片 display.bitmap(image, 0, 0, 0) @@ -170,9 +170,9 @@ python font2bitmap.py NotoSansSC-Regular.otf 20 -s "你好世界0123456789" 2. **将 Python 字体模块转换为二进制文件**: ```bash -python font2bin.py 字体模块.py 输出文件.bin +python font2bin.py 字体模块.py 输出文件.font # 示例 -python font2bin.py noto_sans_sc_20.py ch_font.bin +python font2bin.py noto_sans_sc_20.py ch_font.font ``` ### 图片转换流程 @@ -186,9 +186,9 @@ python image2bitmap.py python-logo.png 4 2. **将 Python 图片模块转换为二进制文件**: ```bash -python image2bin.py 图片模块.py 输出文件.bin +python image2bin.py 图片模块.py 输出文件.img # 示例 -python image2bin.py python_logo.py logo.bin +python image2bin.py python_logo.py logo.img ``` ### 自动转换脚本 @@ -226,7 +226,7 @@ display = st7789.ST7789( ) # 加载中文字体 -font = st7789.streaming_font_mpy.StreamingFont("ch_font.bin") +font = st7789.streaming_font_mpy.StreamingFont("ch_font.font") # 显示文本 display.write(font, "温度: 25.3°C", 10, 10, st7789.WHITE) @@ -249,7 +249,7 @@ import time ... # 加载Logo图片 -logo = st7789.streaming_image.StreamingImage("logo.bin") +logo = st7789.streaming_image.StreamingImage("logo.img") display.bitmap(logo, 60, 20, 0) logo.close() @@ -285,8 +285,8 @@ WIFI_PASSWORD = "your_wifi_password" ... # 加载字体 -ch_font = st7789.streaming_font_mpy.StreamingFont("ch_font.bin") -en_font = st7789.streaming_font_mpy.StreamingFont("en_font.bin") +ch_font = st7789.streaming_font_mpy.StreamingFont("ch_font.font") +en_font = st7789.streaming_font_mpy.StreamingFont("en_font.font") # 加载天气图标 weather_icons = st7789.streaming_image.StreamingAnimation("weather_icon", 10) @@ -386,10 +386,10 @@ finally: **A**: 使用 try-except 块捕获异常: ```python try: - font = st7789.streaming_font_mpy.StreamingFont("font.bin") + font = st7789.streaming_font_mpy.StreamingFont("font.font") except OSError: print("字体文件不存在,使用备用字体") - font = st7789.streaming_font_mpy.StreamingFont("fallback_font.bin") + font = st7789.streaming_font_mpy.StreamingFont("fallback_font.font") ``` ### Q5: 流式加载支持哪些文件系统? @@ -1064,10 +1064,10 @@ if __name__ == "__main__": import st7789.streaming_image # 加载流式字体 - font = st7789.streaming_font_mpy.StreamingFont("ch_font_20.bin") + font = st7789.streaming_font_mpy.StreamingFont("ch_font_20.font") # 加载流式图片 - image = st7789.streaming_image.StreamingImage("python_logo.bin") + image = st7789.streaming_image.StreamingImage("python_logo.img") # 使用... ``` diff --git a/utils/convert_assets.sh b/utils/convert_assets.sh index 852a676..9b9755a 100755 --- a/utils/convert_assets.sh +++ b/utils/convert_assets.sh @@ -8,6 +8,8 @@ set -e # 遇到错误时退出 # 项目根目录 PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" ASSETS_DIR="$PROJECT_ROOT/utils/assets" +FONTS_DIR="$PROJECT_ROOT/utils/assets" +FONT_CONFIG_PATH="$ASSETS_DIR/fonts.json" OUTPUT_DIR="$PROJECT_ROOT/utils/output" # 创建输出目录 @@ -38,47 +40,16 @@ print_blue() { echo -e "${BLUE}[CONFIG]${NC} $1" } -# ============================================== -# 字体配置表 - 调整这里来自定义字体设置 -# 格式: "字体名称|字符集|字体文件|字号|输出名称" -# ============================================== - -# 小号字体配置 -SMALL_FONT_CHARS="你好世界温度湿度1234567890.,?!℃°%:" -SMALL_FONT_FILE="$ASSETS_DIR/NotoSansSC-Regular.otf" -SMALL_FONT_SIZE=14 -SMALL_FONT_OUTPUT="small_font" - -# 中号字体配置 -MEDIUM_FONT_CHARS="你好今天天气温度湿度空气质量良一般差零一二三四五六七八九十百千万亿点°%℃ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.-:" -MEDIUM_FONT_FILE="$ASSETS_DIR/NotoSansSC-Regular.otf" -MEDIUM_FONT_SIZE=18 -MEDIUM_FONT_OUTPUT="medium_font" - -# 大号字体配置 -LARGE_FONT_CHARS="你好今天温度湿度一二三四五六七八九十°%℃:" -LARGE_FONT_FILE="$ASSETS_DIR/NotoSansSC-Regular.otf" -LARGE_FONT_SIZE=24 -LARGE_FONT_OUTPUT="large_font" - -# 天气字体配置 -WEATHER_FONT_CHARS="晴多云阴雨雪雷风雾霾霜露冰雹" -WEATHER_FONT_FILE="$ASSETS_DIR/NotoSansSC-Regular.otf" -WEATHER_FONT_SIZE=28 -WEATHER_FONT_OUTPUT="weather_font" - # ============================================== # 图片转换配置表 - 调整这里来自定义图片设置 # ============================================== # 默认图片颜色深度 -DEFAULT_IMAGE_COLORS=8 +DEFAULT_IMAGE_COLORS=4 # 支持的图片文件扩展名 IMAGE_EXTENSIONS=("png" "jpg" "jpeg" "bmp" "gif") - - # ============================================== # 函数定义 # ============================================== @@ -108,42 +79,69 @@ check_dependencies() { print_info "依赖检查完成" } -# 显示字体配置 -show_font_configs() { +# 读取字体配置文件 +load_font_configs() { + local fonts_file="$FONT_CONFIG_PATH" + + if [ ! -f "$fonts_file" ]; then + print_warn "字体配置文件不存在: $fonts_file" + print_warn "将跳过字体转换" + return + fi + + print_info "加载字体配置文件: $fonts_file" print_blue "字体配置:" echo "----------------------------------------" - printf "%-12s %-8s %-15s %-30s %s\n" "字体名称" "字号" "输出名称" "描述" "字符集预览" + printf "%-12s %-30s %-8s %-15s %s\n" "字体名称" "字符集预览" "字号" "输出名称" "描述" echo "----------------------------------------" - # 小字体 - local preview="${SMALL_FONT_CHARS:0:25}" - if [[ ${#SMALL_FONT_CHARS} -gt 25 ]]; then - preview="$preview..." + # 检查是否有Python可用来解析JSON + if ! command -v python3 &> /dev/null; then + print_warn "Python3 不可用,无法解析JSON配置文件" + print_warn "将跳过字体转换" + return fi - printf "%-12s %-8s %-15s %-30s %s\n" "小字体" "$SMALL_FONT_SIZE" "$SMALL_FONT_OUTPUT" "基本UI元素" "$preview" - # 中字体 - preview="${MEDIUM_FONT_CHARS:0:25}" - if [[ ${#MEDIUM_FONT_CHARS} -gt 25 ]]; then - preview="$preview..." - fi - printf "%-12s %-8s %-15s %-30s %s\n" "中字体" "$MEDIUM_FONT_SIZE" "$MEDIUM_FONT_OUTPUT" "主要文本显示" "$preview" + # 使用Python解析JSON并生成临时配置文件 + python3 -c " +import json +import sys - # 大字体 - preview="${LARGE_FONT_CHARS:0:25}" - if [[ ${#LARGE_FONT_CHARS} -gt 25 ]]; then - preview="$preview..." - fi - printf "%-12s %-8s %-15s %-30s %s\n" "大字体" "$LARGE_FONT_SIZE" "$LARGE_FONT_OUTPUT" "标题和重要信息" "$preview" +try: + with open('$fonts_file', 'r', encoding='utf-8') as f: + config = json.load(f) - # 天气字体 - preview="${WEATHER_FONT_CHARS:0:25}" - if [[ ${#WEATHER_FONT_CHARS} -gt 25 ]]; then - preview="$preview..." - fi - printf "%-12s %-30s %-8s %-15s %s\n" "天气字体" "$WEATHER_FONT_SIZE" "$WEATHER_FONT_OUTPUT" "天气图标" "$preview" + line_num = 0 + for font in config.get('fonts', []): + name = font.get('name', '') + file = font.get('file', '') + size = font.get('size', '') + chars = font.get('chars', '') + output = font.get('output', '') + description = font.get('description', '') - echo + if not name or not file or not size or not output: + continue + + # 如果是相对路径,加上assets目录 + if not file.startswith('/'): + file = '$ASSETS_DIR/' + file + + # 显示配置信息 + preview = chars[:25] + ('...' if len(chars) > 25 else '') + print(f'{name:<12} {preview:<30} {size:<8} {output:<15} {description}') + + # 存储配置到临时文件 + with open('$TEMP_FONT_CONFIG_FILE', 'a') as f_out: + f_out.write(f'{name}|{chars}|{file}|{size}|{output}|{description}\n') + + line_num += 1 + + print(f'\n加载了 {line_num} 个字体配置') +except Exception as e: + print(f'解析JSON配置文件时出错: {e}', file=sys.stderr) + sys.exit(1) +" } # 显示图片配置 @@ -152,8 +150,7 @@ show_image_configs() { echo "----------------------------------------" echo "支持格式: ${IMAGE_EXTENSIONS[*]}" echo "默认颜色深度: $DEFAULT_IMAGE_COLORS" - echo - echo "所有图片将使用默认颜色深度: $DEFAULT_IMAGE_COLORS" + echo "特殊配置: bx_开头的图片文件名中的x值将覆盖默认颜色深度" echo } @@ -172,7 +169,18 @@ is_image_file() { # 获取图片颜色深度 get_image_colors() { - # 统一使用默认颜色深度 + local file_name="$1" + + # 检查是否是b数字_或bx数字_开头的文件 + if [[ "$file_name" =~ ^b[0-9]+_ ]] || [[ "$file_name" =~ ^bx[0-9]+_ ]]; then + # 提取数字部分 + local colors=$(echo "$file_name" | sed -E 's/^b(x?)([0-9]+)_.*/\2/') + print_info "图片 $file_name 使用特殊颜色深度: $colors" >&2 + echo "$colors" + return + fi + + # 使用默认颜色深度 echo "$DEFAULT_IMAGE_COLORS" } @@ -189,7 +197,7 @@ convert_font() { local font_name=$(basename "$font_file" | cut -d'.' -f1) local temp_py="$output_py_dir/${output_name}.py" - local bin_file="$output_bin_dir/${output_name}.bin" + local bin_file="$output_bin_dir/${output_name}.font" # 步骤1: 转换为Python模块 print_info " 步骤1: 将字体转换为Python模块..." @@ -213,7 +221,7 @@ convert_image() { print_info "转换图片: $(basename "$image_file") (颜色深度: $colors)" local temp_py="$output_py_dir/${output_name}.py" - local bin_file="$output_bin_dir/${output_name}.bin" + local bin_file="$output_bin_dir/${output_name}.img" # 检查imgtobitmap.py是否存在 if [ ! -f "$PROJECT_ROOT/utils/imgtobitmap.py" ]; then @@ -237,53 +245,27 @@ main() { print_info "开始转换资源..." print_info "输出目录: $OUTPUT_DIR" - # 显示配置 - show_font_configs + # 初始化字体配置文件列表 + TEMP_FONT_CONFIG_FILE="/tmp/font_configs.$$" + + # 加载字体配置 + load_font_configs + + # 显示图片配置 show_image_configs - # 确认字体文件存在 - local font_files_exist=false - - if [ -f "$SMALL_FONT_FILE" ]; then - font_files_exist=true - elif [ -f "$MEDIUM_FONT_FILE" ]; then - font_files_exist=true - elif [ -f "$LARGE_FONT_FILE" ]; then - font_files_exist=true - elif [ -f "$WEATHER_FONT_FILE" ]; then - font_files_exist=true - fi - - if [ "$font_files_exist" = false ]; then - print_warn "没有找到任何配置的字体文件,跳过字体转换" + # 转换字体 + if [ ! -f "$TEMP_FONT_CONFIG_FILE" ] || [ ! -s "$TEMP_FONT_CONFIG_FILE" ]; then + print_warn "没有找到任何字体配置,跳过字体转换" else - # 转换小字体 - if [ -f "$SMALL_FONT_FILE" ]; then - convert_font "$SMALL_FONT_FILE" "$SMALL_FONT_SIZE" "$SMALL_FONT_CHARS" "$SMALL_FONT_OUTPUT" "$OUTPUT_DIR/py" "$OUTPUT_DIR/bin" - else - print_warn "字体文件不存在: $SMALL_FONT_FILE" - fi - - # 转换中字体 - if [ -f "$MEDIUM_FONT_FILE" ]; then - convert_font "$MEDIUM_FONT_FILE" "$MEDIUM_FONT_SIZE" "$MEDIUM_FONT_CHARS" "$MEDIUM_FONT_OUTPUT" "$OUTPUT_DIR/py" "$OUTPUT_DIR/bin" - else - print_warn "字体文件不存在: $MEDIUM_FONT_FILE" - fi - - # 转换大字体 - if [ -f "$LARGE_FONT_FILE" ]; then - convert_font "$LARGE_FONT_FILE" "$LARGE_FONT_SIZE" "$LARGE_FONT_CHARS" "$LARGE_FONT_OUTPUT" "$OUTPUT_DIR/py" "$OUTPUT_DIR/bin" - else - print_warn "字体文件不存在: $LARGE_FONT_FILE" - fi - - # 转换天气字体 - if [ -f "$WEATHER_FONT_FILE" ]; then - convert_font "$WEATHER_FONT_FILE" "$WEATHER_FONT_SIZE" "$WEATHER_FONT_CHARS" "$WEATHER_FONT_OUTPUT" "$OUTPUT_DIR/py" "$OUTPUT_DIR/bin" - else - print_warn "字体文件不存在: $WEATHER_FONT_FILE" - fi + while IFS='|' read -r font_name characters font_file font_size output_name description; do + # 只在字体文件存在时转换 + if [ -f "$font_file" ]; then + convert_font "$font_file" "$font_size" "$characters" "$output_name" "$OUTPUT_DIR/py" "$OUTPUT_DIR/bin" + else + print_warn "字体文件不存在: $font_file" + fi + done < "$TEMP_FONT_CONFIG_FILE" fi # 检查并转换图片 @@ -325,10 +307,10 @@ main() { print_info "转换完成! 生成的文件:" echo -e "\n${GREEN}=== 二进制文件 ===${NC}" - ls -lh "$OUTPUT_DIR/bin"/*.bin 2>/dev/null || print_warn "没有生成二进制文件" + ls -lh "$OUTPUT_DIR/bin"/*.img "$OUTPUT_DIR/bin"/*.font 2>/dev/null || print_warn "没有生成二进制文件" echo -e "\n${GREEN}=== 信息文件 ===${NC}" - ls -lh "$OUTPUT_DIR/bin"/*.info 2>/dev/null || print_warn "没有生成信息文件" + ls -lh "$OUTPUT_DIR/py"/*.info 2>/dev/null || print_warn "没有生成信息文件" echo -e "\n${GREEN}=== Python模块 ===${NC}" ls -lh "$OUTPUT_DIR/py"/*.py 2>/dev/null || print_warn "没有生成Python模块" @@ -344,15 +326,41 @@ show_help() { echo " -h, --help 显示此帮助信息" echo "" echo "此脚本将转换以下资源:" - echo " 1. 字体 - 根据配置表转换为不同大小的二进制字体文件" + echo " 1. 字体 - 根据assets/fonts/fonts.txt配置转换为二进制字体文件" echo " 2. 图片 - 遍历assets目录下所有支持的图片文件,转换为二进制图片文件" echo "" echo "输入文件应位于: $ASSETS_DIR" + echo "字体配置文件应位于: $FONTS_DIR/fonts.txt" echo "输出文件将保存到:" echo " - Python模块: $OUTPUT_DIR/py" echo " - 二进制文件: $OUTPUT_DIR/bin" echo "" - echo "字体和图片配置可在脚本顶部的配置表中调整" + echo "图片特殊命名规则:" + echo " - 默认使用 $DEFAULT_IMAGE_COLORS 色深度" + echo " - 如果图片名以 bx_N_ 开头,则使用 N 色深度" + echo " 例如: bx_2_icon.png 将使用 2 色深度" + echo "" + echo "fonts.json 配置格式 (JSON):" + echo " {" + echo " \"fonts\": [" + echo " {" + echo " \"name\": \"小字体\"," + echo " \"file\": \"NotoSansSC-Regular.otf\"," + echo " \"size\": 14," + echo " \"chars\": \"你好世界...ABCD...\"," + echo " \"output\": \"small_font\"," + echo " \"description\": \"用于基本UI元素的小号字体\"" + echo " }," + echo " {" + echo " \"name\": \"中字体\"," + echo " \"file\": \"NotoSansSC-Regular.otf\"," + echo " \"size\": 18," + echo " \"chars\": \"你好今天天气...ABCD...\"," + echo " \"output\": \"medium_font\"," + echo " \"description\": \"用于主要文本显示的中号字体\"" + echo " }" + echo " ]" + echo " }" } # 解析命令行参数 @@ -371,6 +379,16 @@ while [[ $# -gt 0 ]]; do shift done +# 清理临时文件 +cleanup() { + if [ -f "$TEMP_FONT_CONFIG_FILE" ]; then + rm -f "$TEMP_FONT_CONFIG_FILE" + fi +} + +# 注册清理函数 +trap cleanup EXIT + # 检查依赖并执行转换 check_dependencies main diff --git a/utils/font2bin.py b/utils/font2bin.py index cdce4f0..7d147ee 100644 --- a/utils/font2bin.py +++ b/utils/font2bin.py @@ -11,7 +11,7 @@ Usage: python font2bin.py Example: - python font2bin.py proverbs_font font_data.bin + python font2bin.py proverbs_font font_data.font """ import importlib.util @@ -87,7 +87,13 @@ def write_binary_file(font_data, output_path): def create_info_file(font_data, output_path): """Create a text file with font information""" - info_path = output_path.replace(".bin", ".info") + # Extract the base filename without extension + base_name = os.path.splitext(os.path.basename(output_path))[0] + # Get the py directory path (assuming output_path is in bin directory) + bin_dir = os.path.dirname(output_path) + py_dir = os.path.join(os.path.dirname(bin_dir), "py") + info_path = os.path.join(py_dir, base_name + ".info") + try: with open(info_path, "w") as f: f.write(f"Font Information\n") @@ -114,7 +120,7 @@ def create_info_file(font_data, output_path): def main(): if len(sys.argv) != 3: print("Usage: python font2bin.py ") - print("Example: python font2bin.py proverbs_font font_data.bin") + print("Example: python font2bin.py proverbs_font font_data.font") return 1 font_module_path = sys.argv[1] diff --git a/utils/image2bin.py b/utils/image2bin.py index b0e6ee1..c6c6db4 100644 --- a/utils/image2bin.py +++ b/utils/image2bin.py @@ -11,7 +11,7 @@ Usage: python image2bin.py Example: - python image2bin.py logo.py logo.bin + python image2bin.py logo.py logo.img """ import importlib.util @@ -92,7 +92,12 @@ def write_binary_file(image_data, output_path): def create_info_file(image_data, output_path): """Create a text file with image information""" - info_path = output_path.replace(".bin", ".info") + # Extract the base filename without extension + base_name = os.path.splitext(os.path.basename(output_path))[0] + # Get the py directory path (assuming output_path is in bin directory) + bin_dir = os.path.dirname(output_path) + py_dir = os.path.join(os.path.dirname(bin_dir), "py") + info_path = os.path.join(py_dir, base_name + ".info") try: with open(info_path, "w") as f: f.write(f"Image Information\n") @@ -107,7 +112,7 @@ def create_info_file(image_data, output_path): f.write(f"Palette: None\n") f.write(f"Bitmap Size: {len(image_data['bitmap'])} bytes\n") f.write( - f"Total File Size: {os.path.getsize(output_path.replace('.info', '.bin'))} bytes\n" + f"Total File Size: {os.path.getsize(output_path.replace('.info', '.img'))} bytes\n" ) print(f"Image information written to {info_path}") @@ -120,7 +125,7 @@ def create_info_file(image_data, output_path): def main(): if len(sys.argv) != 3: print("Usage: python image2bin.py ") - print("Example: python image2bin.py logo.py logo.bin") + print("Example: python image2bin.py logo.py logo.img") print( "\nThis converts Python image modules (from imgtobitmap.py) to streaming format." )