尤其是在一些商业场合上,在中国做中国的生意,显示中文是多么的基本鸭。
刚好在 MaixPy 的代码里做完了这个功能,做得七七八八吧,不算完美,但也够大部分场合使用了。
那么要如何在一款屏幕上显示中文呢?
先说点历史进程的事情,最早计算机由美国发展兴起,所以当时设计出了 ASCII ((American Standard Code for Information Interchange): 美国信息交换标准代码),而这个事情应该是在美苏争霸争霸期间,当然我讲的这点故事和编码没关系,主要是当时的美国在航空业上输了苏联后就趁早开了新赛道走起了半导体路线,所以今天我们看到的很多计算机的定义和标准都是来源于美国国家标准学会,所以我们都知道 ASCII 编码表指是下面这张表。
很快计算机兴起了 ASCII 根本不够其他国家表示字符来使用,就在原来的 ASCII 基础上拓展出了 Unicode 万国码,所以说 Unicode 是兼容万国码的,它定义很简单,就两个字节的二进制数据继续拓展编码定义,所以在编码的开头里还是 ASCII 码。
至此 Unicode 够多了,2^16=65536 个编码的可能性,足够将大部分人类的文字涵盖其中,可随着 Web 的兴起 Unicode 码并不适合字符编码的传输,例如两个字节中,开头就为 '\0' 这就会导致 socket 传输的字符串还没开始传就结束了,为了克服这个问题,就引出了 utf-8 的编码设计,类似于上了一层 base64 编码便于传输,也就是后来 unicode 互转 utf-8 的原理,关于这个编码的发展历程可以多看一些知乎的故事。
其实讲到这些,多少都会有些片面,因为事物发展是多种可能性的,我们知道了这些事实,就有利于我们理解为什么会演变成今天这样,而非重新设计一套方案,开历史的倒车。
就像我曾经升级 base64 到 base128 的编码方式,然而我当时对自己的这个设计沾沾自喜,以为创造了新的编码算法,后来才知道,这个叫 base128 编码规则,所以咱们学习要全面,不要经验主义。
但到这些也只是指文字的编码,并不等于文字的显示内容,从这里开始就引出了字模的定义,字模就像下面这款软件产生的结果一样。
我们可以看到它实际上就是对屏幕的像素点依次打印出来,打印的方式可以称为扫描方式,如我所选的上下到左右,又或是其他,这个关于到字模的存储格式和显示方式,然后我们将这字体中一系列的文字导出来就变成了字库。
所以字库不一定具备兼容性的,相比 ttf 字体,这种字体的手段更为原始一些,因为它通过二进制数据控制打印的数据内容,将字模打印的逻辑也很简单,只需要依次判断所取的行或列的位数据是否存在为 1 即可,若是则打印该像素点。
关于字模的中文资料很多,我想也不是本文的重点了,这些基础的理论知识就留给好奇的你自行去查阅资料吧。
那么如何实现呢?在实现前的准备工作
我们是要在 MaixPy 的环境下实现该功能,在 MaixPy 中存在两类字模的 C 实现,一类为 lcd.draw_string ,另一类为 openmv 的 image.draw_string ,事实上 lcd 不应该参与绘图函数的功能,反而应该放弃这部分实现,转而到 image 的绘图后统一显示(lcd.display)到屏幕上。
但由于 openmv 的字体相当难看,我们若是想全部统一,则要提升 openmv 的字体效果。
关于 openmv 的 字模 定义 在这里。
那么它是如何实现的呢?
我们在 image 的 draw_string 处理过程中可以看到 const glyph_t *g = &font[ch - ' ']; 在和 ' '
做差,这表示它只能兼容到 ASCII 的可视化字符 ' '
部分,这样就能最大程度的压缩体积和保证基本功能。
但事实上这样的做法都暴露了一个问题,字体的定义方式浪费了空间,没有必要为此带入 宽度 和 高度 的变量存储,因为本身就没有解耦接口的实现,就算带入了也都是一堆重复的脏数据。
出现这样的代码都是说明 openmv 的开发者们在思考要不要做兼容,要不要预留字模的控制方法,但事实上这个存储结构和存储逻辑也有密切的关系,例如 8 位 和 16 位的字模在扫描方式上又要分离逻辑,并不能直接使用。
为复杂项目添加代码,应当保证实现的代码尽量都是最小化侵入架构,确保彼此功能独立。
这时候我们就要思考了,现成的代码很多很乱,如何让它们在敏捷开发的思路下被迭代掉,这个思路得提前确定好,为此先梳理一遍整体的逻辑。
想要打印中文字体 < 能够将画布显示到屏幕 < 能够将字模打印到画布上 < 能够从字库中获取字模 < 能够从存储介质中获取字库 < 最终用户能够通过 API 实现打印中文字体。
接下来将顺着这个流程依次实现各组件的功能,最后将其链接起来即可大功告成。
实现字模的打印
我们已经能够在 MaixPy 的基础上实现了将字模打印到画布上,并显示的功能,如下 Python 代码。
import lcd
import image
lcd.init()
img = image.Image(size=(240, 240))
img.draw_rectangle((0,0,240,240), fill=True, color=(150,150,150))
img.draw_string(60, 100, "hello maixpy", scale=4)
lcd.display(img)
我们不难看到 draw_string 就已经实现了该功能,它是如何实现的呢?看如下代码
STATIC mp_obj_t py_image_draw_string(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args)
{
image_t *arg_img = py_helper_arg_to_image_mutable(args[0]);
const mp_obj_t *arg_vec;
uint offset = py_helper_consume_array(n_args, args, 1, 3, &a