linux应用开发基础6-文字显示

本文详细介绍了字符编码的方式,从ASCII、ANSI到Unicode,探讨了UTF-8编码的优势。接着讲解了ASCII码如何在LCD上以点阵形式显示,并延伸到中文字符的点阵显示,特别是GB2312编码。最后,文章深入讨论了Freetype库在显示矢量字体上的应用,包括字符定位、位图转换及一行文字的显示方法,展示了如何使用Freetype显示单个和一行文字的步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、字符的编码方式

我们在显示字体时,需要关注两方面的内容,一显示我们要什么字符,二这些字符是要显示什么格式

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像素

=文档信息=
本学习笔记由博主整理编辑,仅供非商用学习交流使用
由于水平有限,错误和纰漏之处在所难免,欢迎大家交流指正
如本文涉及侵权,请随时留言博主,必妥善处置
版权声明:非商用自由转载-保持署名-注明出处

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值