/MicroPython-New-FontLib

使用 MicroPython 读取自定义中文字库并显示

Primary LanguagePythonMIT LicenseMIT

MicroPython New FontLib

项目介绍

使用MicroPython 开发板读取自定义字库并显示

获取完整项目

因为项目中使用了子模块 FontMaker Clientbinary分支 和 OLED Research,所以要获取完整项目代码需要如下操作

克隆方式

$ git clone --recursive https://gitee.com/walkline/micropython-new-fontlib.git

下载压缩文件方式

或者克隆时未使用--recursive参数的,使用如下代码更新子模块

$ cd micropython-new-fontlib
$ git submodule update --init --recursive

如何使用和测试

生成字库文件

生成字库文件详细说明参考 FontMaker Client 项目文档

直接运行

$ client/youyuan_16.cmd

会生成一个combined.bin文件,字库文件信息如下

参数 数值 说明
字体 幼圆
字号 16 像素
字重 普通 不加粗、不斜体、无下划线
字符宽度 固定
宽度 16 像素
高度 16 像素
水平偏移 0 像素
垂直偏移 0 像素
扫描方式 垂直扫描
字节顺序 低位在前

使用电脑测试

直接运行

$ python fontlib.py

会显示相关信息,包括:

  • 字库信息
  • 获取的字模数据
  • 字模打印预览

完整输出内容如下

HZK Info: .//client/combined.bin
    file size : 261972
  font height : 16
    data size : 32
    scan mode : Vertical
   byte order : LSB
   characters : 7710

!: b'\x00\x00\x00\x00\x00\x00\x08\x00\x0c\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x08\x00\x00\x00\x08\x00\x08\x00\x00\x00'

☆: b'\x00\x00\x00\x00\x00\x80\x01\x80\x01\x80\x01@\x02@~? \x04\x10\x08\x0c\x10\x08\x10\x08\x90\x0bH\x148\x18\x08'

⒉: b'\x00\x00\x00\x00\x00\x00\x07\xc0\x08@\x00@\x00@\x00@\x00\x80\x01\x00\x01\x00\x02\x00\x04\x00\x0c\x08\x0f\xcc\x00\x00'

,: b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00p\x000\x00 \x00'

我: b'\x00\x00\x00@\x07PxH\x08D\x08D\x7f\xfe\x08D\x08D\t(\x0e0x0\x080\x08R\t\x8ax\x06'

ㄘ: b'\x00\x00\x00\x00\x00\x00\x01\x00\x01\x80\x00\x80\x01\x00\x03\xf0\x1d\x00\x01\x00\x02\xe0\x03`\x00@\x00\x80\x00\x00\x00\x00'

■: b'\x00\x00\x00\x00\x7f\xff\x7f\xff\x7f\xff\x7f\xff\x7f\xff\x7f\xff\x7f\xff\x7f\xff\x7f\xff\x7f\xff\x7f\xff\x7f\xff\x7f\xff\x7f\xff'

B: b'\x00\x00\x00\x00\x00\x00\x00\x00\x07\xf0\x060\x068\x060\x06p\x07\xf0\x06\x18\x06\x18\x06\x18\x07\xf0\x00\x00\x00\x00'

中: b'\x00\x00\x01\x00\x01\x00\x01\x00?\xfc!\x04!\x04!\x04!\x04!\x04!\x04?\xfc\x01\x00\x01\x00\x01\x00\x01\x00'

爱: b'\x00\x00\x00\x00?\xf8\x11\x10\t\x10?\xfcD\x02B\x02\x1f\xf8\x04\x00\x07\xf8\x0e\x08\x13\x10 \xe0\x01\xe0\x1e\x1e'

β: b'\x00\x00\x00\x00\x00\x00\x01\xe0\x01\x10\x030\x02 \x02\xc0\x02`\x06 \x04 \x04`\x04`\x07\xc0\x0c\x00\x08\x00'

あ: b"\x00\x00\x00\x00\x03\x00\x03\x00\x02\xc0\x1f\x80\x02\x00\x06\x80\x07\xf0\x0c\x98\x15\x0c'\x0c&\x0c/\x088\x10\x01\xe0"

H: b'\x00\x00\x00\x00\x00\x00\xe7\x00B\x00B\x00B\x00B\x00~\x00B\x00B\x00B\x00B\x00\xe7\x00\x00\x00\x00\x00'

华: b'\x00\x00\x00\x00\x08@\x08H\x18p(\xc0+BHB\x08~\x01\x00\x01\x00\x7f\xfe\x01\x00\x01\x00\x01\x00\x01\x00'

ǚ: b"\x00\x00\x00\x00\x00\x00\x00\x00\x14\x00\x08\x004\x00C\x00C\x00C\x00C\x00C\x00C\x00'\x00\x18\x00\x00\x00"

e: b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00<\x00B\x00~\x00@\x00@\x00B\x00<\x00\x00\x00\x00\x00'

l: b'\x00\x00\x00\x00\x00\x00p\x00\x10\x00\x10\x00\x10\x00\x10\x00\x10\x00\x10\x00\x10\x00\x10\x00\x10\x00|\x00\x00\x00\x00\x00'

o: b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00<\x00B\x00B\x00B\x00B\x00B\x00<\x00\x00\x00\x00\x00'

⑴: b'\x00\x00\x00\x00\x08\x00\x10\x08!\x84 \x82@\x82@\x83@\x81@\x81@\x81@\x82 \x82 \x84\x13\xc4\x10\x08'

................ ................ ................ ................ ................ ................
................ ................ ................ ................ .........@...... ................
................ ........@....... ................ ................ .....@@@.@.@.... ................ 
....@........... .......@@....... .....@@@@@...... ................ .@@@@....@..@... .......@........
....@@.......... .......@@....... ....@....@...... ................ ....@....@...@.. .......@@.......
....@........... .......@.@...... .........@...... ................ ....@....@...@.. ........@.......
....@........... ......@..@...... .........@...... ................ .@@@@@@@@@@@@@@. .......@........
....@........... .@@@@@@...@@@@@@ .........@...... ................ ....@....@...@.. ......@@@@@@....
....@........... ..@..........@.. ........@....... ................ ....@....@...@.. ...@@@.@........
....@........... ...@........@... .......@........ ................ ....@..@..@.@... .......@........
....@........... ....@@.....@.... .......@........ ................ ....@@@...@@.... ......@.@@@.....
....@........... ....@......@.... ......@......... ................ .@@@@.....@@.... ......@@.@@.....
................ ....@...@..@.... .....@.......... ................ ....@.....@@.... .........@......
....@........... ....@.@@.@..@... ....@@......@... .@@@............ ....@....@.@..@. ........@.......
....@........... ...@.@....@@@... ....@@@@@@..@@.. ..@@............ ....@..@@...@.@. ................
................ ...@@.......@... ................ ..@............. .@@@@........@@. ................

................ ................ ................ ................ ................ ................
................ ................ .......@........ ................ ................ ................
.@@@@@@@@@@@@@@@ ................ .......@........ ..@@@@@@@@@@@... ................ ......@@........
.@@@@@@@@@@@@@@@ ................ .......@........ ...@...@...@.... .......@@@@..... ......@@........
.@@@@@@@@@@@@@@@ .....@@@@@@@.... ..@@@@@@@@@@@@.. ....@..@...@.... .......@...@.... ......@.@@...... 
.@@@@@@@@@@@@@@@ .....@@...@@.... ..@....@.....@.. ..@@@@@@@@@@@@.. ......@@..@@.... ...@@@@@@.......
.@@@@@@@@@@@@@@@ .....@@...@@@... ..@....@.....@.. .@...@........@. ......@...@..... ......@.........
.@@@@@@@@@@@@@@@ .....@@...@@.... ..@....@.....@.. .@....@.......@. ......@.@@...... .....@@.@.......
.@@@@@@@@@@@@@@@ .....@@..@@@.... ..@....@.....@.. ...@@@@@@@@@@... ......@..@@..... .....@@@@@@@....
.@@@@@@@@@@@@@@@ .....@@@@@@@.... ..@....@.....@.. .....@.......... .....@@...@..... ....@@..@..@@...
.@@@@@@@@@@@@@@@ .....@@....@@... ..@....@.....@.. .....@@@@@@@@... .....@....@..... ...@.@.@....@@..
.@@@@@@@@@@@@@@@ .....@@....@@... ..@@@@@@@@@@@@.. ....@@@.....@... .....@...@@..... ..@..@@@....@@..
.@@@@@@@@@@@@@@@ .....@@....@@... .......@........ ...@..@@...@.... .....@...@@..... ..@..@@.....@@..
.@@@@@@@@@@@@@@@ .....@@@@@@@.... .......@........ ..@.....@@@..... .....@@@@@...... ..@.@@@@....@...
.@@@@@@@@@@@@@@@ ................ .......@........ .......@@@@..... ....@@.......... ..@@@......@....
.@@@@@@@@@@@@@@@ ................ .......@........ ...@@@@....@@@@. ....@........... .......@@@@.....

................ ................ ................ ................ ................ ................
................ ................ ................ ................ ................ ................
................ ....@....@...... ................ ................ ................ ................
@@@..@@@........ ....@....@..@... ................ ................ .@@@............ ................
.@....@......... ...@@....@@@.... ...@.@.......... ................ ...@............ ................ 
.@....@......... ..@.@...@@...... ....@........... ................ ...@............ ................
.@....@......... ..@.@.@@.@....@. ..@@.@.......... ................ ...@............ ................
.@....@......... .@..@....@....@. .@....@@........ ..@@@@.......... ...@............ ..@@@@..........
.@@@@@@......... ....@....@@@@@@. .@....@@........ .@....@......... ...@............ .@....@.........
.@....@......... .......@........ .@....@@........ .@@@@@@......... ...@............ .@....@.........
.@....@......... .......@........ .@....@@........ .@.............. ...@............ .@....@.........
.@....@......... .@@@@@@@@@@@@@@. .@....@@........ .@.............. ...@............ .@....@.........
.@....@......... .......@........ .@....@@........ .@....@......... ...@............ .@....@.........
@@@..@@@........ .......@........ ..@..@@@........ ..@@@@.......... .@@@@@.......... ..@@@@..........
................ .......@........ ...@@........... ................ ................ ................
................ .......@........ ................ ................ ................ ................

................
................
....@...........
...@........@...
..@....@@....@..
..@.....@.....@.
.@......@.....@.
.@......@.....@@
.@......@......@
.@......@......@
.@......@......@
.@......@.....@.
..@.....@.....@.
..@.....@....@..
...@..@@@@...@..
...@........@...

使用开发板测试 1

推荐使用 AMPY Batch Tool (ab工具) 进行文件上传和调试操作

# 上传文件
$ ab

# 进入 repl 模式
$ ab --repl

# 运行开发板上的文件
# 使用快捷键 Ctrl-T
>>>
Run onboard file
    [1] /boot.py
    [2] /drivers/ssd1306.py
    [3] /fontlib.py
Choose a file: 3
>>>

# 在开发板上运行本地文件
# 使用快捷键 Ctrl-R
>>> Run local file
    [1] fontlib.py
    [2] drivers\ssd1306.py
Choose a file: 1
>>>

使用开发板测试 2

这是把fontlib.py文件中非MicroPython代码精简掉,并编译为字节码再运行测试的方法

# 上传文件
$ ab abconfig-mpy

因为上传文件中已经包含了main.py,所以直接复位根据提示进行选择,就可以看到效果了

使用图标字体生成字库

方法参见 使用图标字体生成字库

选取其它语种字符的方法

GB2312收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的 682 个全角字符

可以用键盘输入它们,但是并不一定简单,好在我们还可以从 GB2312简体中文编码表 页面中直接查看复制它们

关于速度

以当前字库文件举例:

  • 文件名:combined.bin
  • 文件大小:261,972字节
  • GB2312索引表大小:17,672字节
  • 点阵大小:16x16像素
  • 字符数据大小:32字节
  • 字符总数:7710个(包含ASCIIGB2312
  • 实例化FontLib时打开字库文件,读取文件头信息,大约 12 ms
  • 同时读取?的字符数据作为占位符,大约 5 ms
  • 打印字库信息,大约 39 ms(简直无语,建议实际使用时不要打印)
  • 检索字符数据前再次打开字库文件,大约 12 ms
  • 检索字符数据,每字符大约 12 ms
  • 字符数据使用Dict存储,字符Unicode值作为关键字,取值耗时可以忽略
  • 显示字符耗时未统计

关于提速

决定显示速度的因素有两方面:

  • 从字库中读取指定字符的字模数据
  • 在 oled 中显示

显示部分,MicroPython已经提供了SSD1306驱动,使用FrameBuffer进行数据管理,我相信官方的实力,所以显示部分的速度假设已经没有提升空间了

数据部分,最开始的思路就是“完成功能”,所以在读取的时候是这个流程:

  1. 打开字库文件
  2. 取一个字符
  3. 分段查找字符在索引表中的偏移量
  4. 定位到偏移量读取字模数据
  5. 从第2步开始循环,直到读取所有字符数据

分段的意思是:在Python中打开文件,它不提供查找功能,需要先读取文件内容再进行查找,对于开发板来说,一次性读取全部文件肯定会导致内存溢出,所以需要分段读取一段合理长度的数据再进行查找,在查找到之前就需要不停的分段读取

这个方法的问题在于,每个字符都要从索引表的开始位置进行查找,比较浪费

提速的思路就是,在每次分段读取数据的时候查找所有字符,减少分段次数

继续查找资料,有人说使用read代替seek可以提高定位的效率,在电脑上进行了简单测试,300M 的文件使用readseek快大约0.2 ms,但是这个方法有个致命问题,虽然read读取到的数据并不使用,但也会分配内存空间,在开发板上运行的结果就是,更慢了

对比一下三种方式的读取速度(毫秒)

字库文件信息:

  • 文件大小:500024 字节
  • 字符总数:8932 个
  • 数据大小:54 字节
原方法 新方法(seek) 新方法(read)
打开字库 27.51 26.22 61.21
字符数 原方法 新方法(seek) 新方法(read)
240 13.09 6.50 159.28
227 16.05 8.22 /
767 内存溢出 内存溢出 /

注:统计结果为平均时间

这里的字符数是包含了重复字符的总数,实际获取数据时是要去重的

读取速度还与字符在索引表中的相对位置有关,相对位置靠前的字符当然能更快被找到,反之则更慢

一次性读取太多的数据也会导致内存溢出,所以在实际使用时不建议一次性读取太多

手动调用垃圾回收(gc.collect())的时机一定要掌握好,并不是任何时候手动调用都能起到预期的作用,有时还会起到反作用

对于能够自动回收的,如函数内部的变量,可以省去手动调用的操作

统计运行时间使用的装饰器

from utime import ticks_us, ticks_diff

def cal_time(fn):
    def wrapper(*args,**kwargs):
        starTime = ticks_us()
        f = fn(*args,**kwargs)
        print('%s() runtime: %s ms' % (fn.__name__, ticks_diff(ticks_us(), starTime) / 1000))
        return f
    return wrapper

关于测速

又增加了一个单独测速的文件fontlib_test.py,发现一个现象,虽然知道和缓存有关,但是具体怎么实现的并不了解

这里测速使用了两个方法

  • 一次获取所有字符的字模数据
  • 分段获取所有字符的字模数据

单独测试这两种方法,得到的结果如下

### method: load all
### 240 chars, get 111 chars: 1145.765 ms, avg: 10.32221 ms
### method separated
### 0 get 18 chars: 201.403 ms, avg: 11.18906 ms
...
### 9 get 22 chars: 169.531 ms, avg: 7.705955 ms
### 240 chars, get 197 chars: 1760.472 ms, avg: 9.978033 ms

但是同时测试两种方法的结果却是这样的

### method: load all
### 240 chars, get 111 chars: 1145.765 ms, avg: 10.32221 ms

### method separated
### 0 get 18 chars: 110.468 ms, avg: 6.137111 ms
...
### 9 get 22 chars: 125.306 ms, avg: 5.695727 ms
### 240 chars, get 197 chars: 1170.494 ms, avg: 6.635651 ms

调换顺序的结果也是类似

### method separated
### 0 get 18 chars: 201.403 ms, avg: 11.18906 ms
...
### 9 get 22 chars: 169.531 ms, avg: 7.705955 ms
### 240 chars, get 197 chars: 1760.472 ms, avg: 9.978033 ms

### method: load all
### 240 chars, get 111 chars: 565.726 ms, avg: 5.096631 ms

结论就是,不管是一次性还是分段,只要提前获取了所有字符的字模数据,下次再获取的时候速度就会快很多,不过显然这种提速并没有实际意义

合作交流

  • 联系邮箱:walkline@163.com
  • QQ 交流群:
    • 走线物联:163271910
    • 扇贝物联:31324057

走线物联扇贝物联