一、字符的编码方式
我们在显示字体时,需要关注两方面的内容,一显示我们要什么字符,二这些字符是要显示什么格式
ASCII编码,一个字符使用到一个字节来表示,2的7次方可以表示128个字符,最高位bit7为永远为0。
ANSI编码,是一个扩展的ASCII表,最高位bit7为1则是非ASCII码,要用两个字节来表示;ANSI对于更多的字符使用到两个字节来表示;ANSI编码在不同的字符集(ISO8859,GB2312,BIG5)下面会显示出不同的字符,在ANSI下面数值和字符是一对多的关系。
unicode统一编码,也包含了ascii码一个数值对应一个字符,unicode的数值范围是0x0000至0x10FFFF,有100多万个字符。
编码实现
使用三个字节来表示,这样过于浪费。使用两个字节,这样只能算将就使用,2的16次方可以表示65536个字符,也足够使用了。
对于A字符使用0x41 0x00或者0x00 0x41两种不同的字节序来表示,称为大字节序和小字节序。UTF-16 LE小字节序,使用小字节序保存文件时,文件开头会保存ff,fe来表示这是一个小字节序的数据,A保存为41 00 权重小的放在前面,权重大的放在后面,组合起来就是0041;UTF-16 BE大字节序 ,文件开头有fe ff来表示这是一个大字节序的数据,A保存为00 41组合起来就是0x0041。
缺点是容错率比较差,1、如果丢失一个字节,就会导致全文乱码;2、ASCII字符有空间浪费;3、无法表示出全部的字符数据。
UTF8编码
使用UTF8可以解决上面的问题;有两种表示,一个有头部,一个没有头部,头部内容是ef bb bf。
二、ASCII码的点阵显示
我们要在lcd上面显示一个ASCII字符,要找到字符对应的点阵,在linux内核源码中已经包括了点阵文件例如lib\fonts\font_8*16.c,这里以数组的形式保存各个字符的点阵。下图的左边使用一个字节代表一行的信息,bit0到bit7依次对应了右边的每一个像素,如果bit的值是1则显示为白色,0显示为黑色。
假如我们要显示一个字符A,根据它的ASCII码在fontdata_8x16数组中找到它的点阵,然后取出这16个字节去描画16行像素,字符A的ASCII值是0x41,那么从fontdata_8x16[0x41*16]开始取其点阵数据。那么我们需要循环16*8次,在每一个对应的X,Y坐标上面选择白色或者是黑色。
实验成功,我们将看到屏幕中间会显示出一个白色的字母‘A’。
三、中文字符的点阵显示
对于同一套程序,在保存时使用不同的编码格式,存储在内存中的数值是不一样的,程序执行的效果也就是不一样的。当然,可以指定源文件的保存格式,也可以指定编译出来的可执行程序里面的字符以什么方式编码。
gcc -finput-charset=GB2312 -fexec-charset=UTF-8 -o test_charset_ansi test_charset_ansi.c
(把源文件里面的gb2312转换为可执行文件的utf-8)
hzk16库,它包括了常用汉字的16*16点阵字库,使用了32个字节表示一个汉字,它是以GB2312的编码值来查找点阵的。比如汉字‘中’,他的gb2312编码为0xd6,0xd0,这里的0xd6表示区码,表示在哪一个区:第0xd0 - 0xa1区;0xd0表示位码,表示它是这个区里的那个字符:第0xd0 - 0xa1个。每一个区有94个汉字。区位码从0xa1而不是从0开始,是为了兼容ASCII码。所以汉字‘中’是HZK16里第“(0xd6-0xa1)*94+(0xd0-0xa1)”个字符,最后在乘以32就可以得到‘中’在库中的那个位置。
四、交叉编译程序-freetype
freetype库是开源的字体引擎库,它提供统一的接口来访问多种字体格式文件,从而实现矢量字体显示,我们使用手工交叉编译freetype,这种方法在编译、安装一些小程序时很有用。
1、编译程序时去哪找头文件?
系统目录:就是交叉编译工具链里的某个include目录;自己指定:链接时用“-I(大写i) dir”选项指定。
2、链接时去哪找库文件?
系统目录:就是交叉编译工具链里的某个lib目录;自己指定:链接时用“-L dir”选项指定。
3、运行时去哪找库文件?
系统目录:就是板子上的/lib、/usr/lib目录;自己指定:运行程序用环境变量LD_LIBRARY_PATH指定。
export LD_LIBRARY_PATH=/xxx_dir ; ./test
或
LD_LIBRARY_PATH=/xxx_dir ./test
4、运行时不需要头文件,所以头文件不用放到开发板上。
5、编译时怎么确定系统目录?
echo 'main(){}'| arm-buildroot-linux-gnueabihf-gcc -E -v - (gcc的前缀可以更改成别的)
它会列出头文件目录、库目录(LIBRARY_PATH)。我们只需要在头文件目录中确定有没有这个文件,或是自己指定头文件目录。
6、万能编译命令?
如果交叉编辑工具链的前缀是arm-buildroot-linux-gnueabihf-,比如arm-buildroot-linux-gnueabihf-gcc,交叉编译开源软件时,文件目录里面有configure,就可以使用万能命令进行编译,如下:
./configure --host=arm-buildroot-linux-gnueabihf --prefix=$PWD/tmp
make
make install
五、使用freetype显示一个文字
矢量字体首先需要确定关键点,确定好关键点之后,使用数学曲线将他们连接起来,然后在空隙里面填充各种颜色,也就是填充闭合曲线内部空间,使用矢量字体避免了显示出现锯齿的情况。
一个文字的显示过程可以概括如下:
1、 给定一个字符可以确定它的编码值(ASCII、UNICODE、GB2312);
2、 设置字体大小;
3、根据编码值,从文件头部中通过charmap找到对应的关键点(glyph),它会根据字体大小调整关键点;
4、 把关键点转换为位图点阵;
5、在LCD上显示出来
程序code不贴出。
使用FT_Load_Char函数加载某个字符的位图,然后把这个位图在屏幕上面显示出来
备注:对于GB2312编译的c文件,编译时不指定 -finput-charset,就会使用默认的utf-8来解析c文件,并试图转换为wchar_t,就会出错,对于这样的c文件使用这个命令进行编译,gcc -finput-charset=GB2312 -o test_wchar test_wchar.c 。
最后执行命令,指定字体文件,指定字体大小 ./freetype_show_font ./simsun.ttc 300,就可以在屏幕上面看见一个“繁”字。
六、使用freetype显示一行文字
在LCD的坐标系中,原点在屏幕的左上角。对于笛卡尔坐标系,原点在左下角。freetype使用笛卡尔坐标系,在显示时需要转换为LCD坐标系。
从下图可知,X方向坐标值是一样的。
假设LCD的高度是V,在LCD坐标系中坐标是(x, y),那么它在笛卡尔坐标系中的坐标值为(x, V-y)。
反过来,在笛卡尔坐标系中坐标是(x, y),那么它在LCD坐标系中坐标值为(x, V-y)。
使用矢量字体进行显示时,字符之间的位置彼此依赖,我们需要指定原点origin,有了原点之后加上当前字符的advance就可以得到下一个字符的原点,原点和原点相连就可以组成一条基线baseline, 通过确定每个字符的外框就可以确定整行字符的外框了。我们可以假设原点为(0,0)坐标,整行字符左上角坐标是(x1,y1)这个可以计算得到,如果我们想指定这行字的显示位置(x,y)那么新的原点位置就是(x-x1,y-y1)。
freetype的几个数据结构
FT_Library library; /* 对应freetype库 */
error = FT_Init_FreeType( &library ); /* 初始化freetype库 */
error = FT_New_Face(library, font_file, 0, &face ); /* 加载字体文件 */
FT_GlyphSlot slot = face->glyph; /* 插槽: 字体的处理结果保存在这里 */
error = FT_Get_Glyph(slot , &glyph);/* 取出glyph */
FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_TRUNCATE, &bbox ); /* 从glyph得到外框: bbox */
备注:外框的左上角坐标是(xMin,yMax);所以新origin坐标是(x-xMin,y-yMax);之所以乘以64,是因为FT_Set_Transform要求坐标的单位是1/64像素
=文档信息=
本学习笔记由博主整理编辑,仅供非商用学习交流使用
由于水平有限,错误和纰漏之处在所难免,欢迎大家交流指正
如本文涉及侵权,请随时留言博主,必妥善处置
版权声明:非商用自由转载-保持署名-注明出处