1 字符编码#
1.1 ASCII编码#
ascii是“American Standard Code for Information Interchange”的缩写, 美国信息交换标准代码。
电脑毕竟是西方人发明的,他们常用字母就 26 个,区分大小写、加上标点符号也没超过 127 个,每个字符用一个字节来表示就足够了。一个字节的 7 位就可以表示 128 个数值,在 ASCII 码中最高位永远是 0。
linux-4.18.16/lib/fonts这个目录下就有对应文件。在这里我挑选font_8x16.c
1.2 ANSI#
ASNI 是 ASCII 的扩展,向下包含 ASCII。对于 ASCII 字符仍以一个字节来表示。
对于非 ASCII 字符则使用 2 字节来表示。并没有固定的 ASNI 编码。
比如在中国大陆地区, ANSI 的默认编码是 GB2312;
在港澳台地区默认编码是 BIG5。以数值“ 0xd0d6”为例,对于 GB2312 编码它表示“中”;对于 BIG5 编码它表示“ 笢”。
用ANSI编码字符’aa中’的16进制数据
1.3 UNICODE#
在 ANSI 标准中,很多种文字都有自己的编码标准,汉字简体字有 GB2312、繁体字有 BIG5,这难免同一个数值对应不同字符。比如数值“ 0xd0d6”,对于GB2312 编码它表示“中”;对于 BIG5 编码它表示“ 笢”。这造成了使用 ANSI 编码保存的文件,不适合跨地区交流。
UNICODE 编码就是解决这类问题:对于地球上任意一个字符,都给它一个唯一的数值。
- ASCII 编码中使用一个字节来表示一个字符,只用到其中的 7 位,最高位恒为 0;
- ANSI 编码中,对于 ASCII 字符仍使用一个字节来表示(BIT7 是 0),对于非ASCII 字符一般使用 2 个字节来表示,非 ASCII 字符的数值 BIT7 都是 1
1.3.1 UTF-16 LE#
每个 UNICODE 值用 3 字节来表示有点浪费,那只用 2 字节呢?它可以表示2^16=65536 个字符,全世界常用的字符都可以表示了。Little endian 表示小字节序,数值中权重低的字节放在前面,比如字符“ A 中”在 TXT 文件中的数值如下,其中的“ A”使用“0x41 0x00”两字节表示;“中”使用“ 0x2d 0x4e”两字节表示。文件开头的“ 0xff 0xfe”表示“UTF-16 LE”。
1.3.2 UTF-16 BE#
Big endian 表示大字节序,数值中权重低的字节放在后面,比如字符“ ab中”在 TXT 文件中的数值如下,其中的“ A”使用“ 0x00 0x41”两字节表示;“中”使用“ 0x4e 0x2d”两字节表示。文件开头的“ 0xfe 0xff”表示“UTF-16 BE”。
1.4 UTF8#
UTF8 是一种变长的编码方法,有 2 种 UTF8格式的文件:带有头部、不带头部。
对于 ASCII 字符用UTF-16有空间浪费、而且文件中有某个字节丢失,这会使得后面所有字符都因为错位而无法显示。UTF8则不会有这样的问题。0x41表示大写字母’A’,只用了一个字节。上图中的 3 个字节“ 0xe4 0xb8 0xad”表示的数值是 0x4e2d,对应“中”的 UNICODE 码.
上图中, 0xe4 的二进制是“ 11100100”,高位有 3 个 1,表示从当前字节起有 3 字节参与表示 UNICODE;
0xb8 的二进制是“10111000”,高位有 1 个 1,表示从当前字节起有 1 字节参与表示 UNICODE;
0xad 的二进制是“10101101”,高位有 1 个 1,表示从当前字节起有 1 字节参与表示 UNICODE;
除去高位的“ 1110”、“ 10”、“ 10”后,剩下的二进制数组合起来得到“ 01001110001101”,它就是 0x4e2d,即“中”的 UNICODE 值。
使用 UTF8 编码时,即使 TXT 文件中丢失了某些数据,也只会影响到当前字符的显示,后面的字符不受影响。
2 中文字库移植#
2.1 -finput-charset -fexec-charset编译选项#
我们编写 C 程序时,可以使用 ANSI 编码,或是 UTF-8 编码;在编译程序时,可以使用以下的选项告诉编译器用什么方式编码:
1 | -finput-charset=GB2312 |
如果不指定-finput-charset
, GCC 就会默认 C 程序的编码方式为 UTF8。
用ANSI格式编写编码,vim浏览显示会乱码,用notepad++采用ANSI编码格式浏览。
由于编译器默认用utf8编码,所以看到最终打印是乱码的。可以看到“中”的ANSI码是d6 d0。
用utf8编写代码
最终打印是OK的。可以看到“中”的utf8码是e4 b8 ad。
2.2 GB2312 转为 UTF-8#
从上面的输出信息可以看出来, GB2312 的”0xd6 0xd0”可以转换为 UTF-8的“ 0xe4 0xb8 0xad”。而如果把原本就是 UTF-8 格式的 test_charset_utf8.c当作 GB2312 格式,会引起错误.
2.3 UTF-8 转为 GB2312#
从 上 面 的 输 出 信 息 可 以 看 出 来 , 如 果 把 原 本 就 是 GB2312 格 式 test_charset_ansi.c 当成UTF-8 格式,会引起错误。而 UTF-8 格式的“中”编码值为“ 0xe4 0xb8 0xad”,可以转换为 GB2312 的“0xd6 0xd0”
2.4 HZK16中文字库#
HZK16字库里的16×16汉字一共需要256个点来显示,也就是说需要32个字节才能达到显示一个普通汉字的目的。符合GB2312标准。
一个GB2312汉字是由两个字节编码的,范围为A1A1~FEFE。A1-A9为符号区,B0到F7为汉字区。每一个区有94个字符。
HZK16 中是以 GB2312 编码值来查找点阵的,以“中”字为例,它的编码值是“ 0xd6 0xd0”,其中的 0xd6 表示“区码”,表示在哪一个区;其中的 0xd0 表示“位码”,表示它是这个区里的哪一个字符。每一个区有 94 个汉字。区位码从 0xa1 而不是从 0 开始,是为了兼容 ASCII码。
要显示“中”字, 它的 GB2312 编码是 d6d0,它是 HZK16 里第“ (0xd6-0xa1)*94+(0xd0-0xa1)”个字符。(0xd6-0xa1)表示是哪个区,(0xd0-0xa1)表示是哪个位。
如何获取和显示汉字”中“?
1 | fd_hzk16 = open("HZK16", O_RDONLY); |
下载HZK16字库,读取并且mmap字库:
1 | 该函数在 LCD 的(x,y)位置处显示汉字字符 str, str[0]中保存区码、 str[1]中保存位码。 |
根据下图来理解字库中每个像素点是如何显示的:
总共有十六行,因此需要一个循环 16次的大循环(第 4740 行)。
考虑到一行有两个字节, 在大循环中加入一个 2 次的循环用于区分是哪个字节(第 4741 行)。
最后使用第 3 个循环来处理一个字节中的 8 位(第 4744 行)。对于每一位,它等于 1 时对应的像素被设置为白色,它等于 0 时对应的像素被设置为黑色。需要注意的是根据 x、 y、 i、 j、 b 来计算像素坐标。
测试:
注意:使用上述命令时 show_chinese.c 的编码格式必须是 ANSI(GB2312),因为HZK16字库是按照GB2312编码的,否则编译时需要指定“ -fexec-charset=GB2312”。
3 freetype移植#
FreeType库是一个完全免费(开源)的、高质量的且可移植的字体引擎。Freetype 是开源的字体引擎库, 它提供统一的接口来访问多种字体格式文件,从而实现矢量字体显示。我们只需要移植这个字体引擎,调用对应的 API 接口,提供字体文件,就可以让 freetype 库帮我们取出关键点、实现闭合曲线, 填充颜色, 达到显示矢量字体的目的。
这里仅移植freetype库,freetype的使用不做具体展开。可以从 https://www.freetype.org/ 可 以 下 载 到 “ freetype-doc-2.10.2.tar.xz”。
3.1 矢量字体#
使用点阵字库显示英文字母、汉字时, 大小固定, 如果放大缩小则会模糊甚至有锯齿出现,为了解决这个问题,引用矢量字体。
1 | 第1步 确定关键点, |
什么是关键点?以字母“ A”为例:
再用数学曲线(比如贝塞尔曲线)将关键点都连接起来, 得到一系列的封闭的曲线:
最后把封闭空间填满颜色,就显示出一个 A 字母:
如果需要放大或者缩小字体,关键点的相对位置是不变的, 只要数学曲线平滑,字体就不会变形。
3.2 下载freetype#
https://freetype.org/download.html
https://download.savannah.gnu.org/releases/freetype/
freetype 依赖于 libpng, libpng 又依赖于 zlib,所以我们应该:先编译安装 zlib,再编译安装 libpng,最后编译安装 freetype。 但是,有些工具链里有 zlib, 那就不用编译安装 zlib.
下载安装libpng: https://www.linuxfromscratch.org/blfs/view/svn/general/libpng.html
下载安装zlib: https://www.zlib.net/fossils/
3.3 编译freetype#
3.3.1 设置工具链#
1 | export ARCH=arm |
得到头文件的系统目录为:
1 | /media/cvitek/robin.lee/my_test/study/weidongshan/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../lib/gcc/arm-buildroot-linux-gnueabihf/7.5.0/include |
库文件系统目录为:
1 | COMPILER_PATH=/media/cvitek/robin.lee/my_test/study/weidongshan/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../libexec/gcc/arm-buildroot-linux-gnueabihf/7.5.0/:/media/cvitek/robin.lee/my_test/study/weidongshan/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../libexec/gcc/:/media/cvitek/robin.lee/my_test/study/weidongshan/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../lib/gcc/arm-buildroot-linux-gnueabihf/7.5.0/../../../../arm-buildroot-linux-gnueabihf/bin/ |
3.3.2 编译zlib#
编译zlib库时,./configure不允许传入–host参数;不支持的话需要export CC设置为你的arm工具链
1 | export CC=arm-buildroot-linux-gnueabihf-gcc |
将lib和头文件拷贝到工具链目录(或者不拷贝,到时候编译用-L, -I指定即可,运行时指定LIBRARY_PATH)
1 | cp include/* -rf /home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../lib/gcc/arm-buildroot-linux-gnueabihf/7.5.0/include |
3.3.3 编译libpng#
1 | ./configure --host= arm-buildroot-linux-gnueabihf --prefix=$PWD/tmp |
将lib和头文件拷贝到工具链目录。
3.3.4 编译freetype#
1 | ./configure --host= arm-buildroot-linux-gnueabihf --prefix=$PWD/tmp |
注意:如果你的工具链路径不是 /home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot
,那么make时会出现类似如下错误。
修改自己工具链下的$(TOOLCHAIN)/arm-buildroot-linux-gnueabihf/sysroot/usr/lib目录下编辑libfreetype.la, 替换dependency_libs和libdir的路径。来自 < http://bbs.100ask.net/question/15908>
libfreetype库如下:
将lib和头文件拷贝到工具链目录。
3.4 测试#
1 | gcc freetype_show_font.c -I /media/cvitek/robin.lee/my_test/study/weidongshan/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/arm-buildroot-linux-gnueabihf/sysroot/usr/include/freetype2/ -L /media/cvitek/robin.lee/my_test/study/weidongshan/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/arm-buildroot-linux-gnueabihf/sysroot/usr/lib -lfreetype |
1 |
|