嵌入式linux/鸿蒙开发板(IMX6ULL)开发(十四)文字显示(中)

简介: 嵌入式linux/鸿蒙开发板(IMX6ULL)开发(十四)文字显示

1.3.3 汉字区位码


我们从网上搜到HZK16这个文件,它是常用汉字的16*16点阵字库。HZK16里每个汉字使用32字节来描述,如下图所示:

1670898641281.jpg

跟ASCII字库一样,每个字节中每一位用来表示一个像素,位值等于1时表示对应像素被点亮,位值等于0时表示对应像素被熄灭。


HZK16中是以GB2312编码值来查找点阵的,以“中”字为例,它的编码值是“0xd6 0xd0”,其中的0xd6表示“区码”,表示在哪一个区:第“0xd6 - 0xa1”区;其中的0xd0表示“位码”,表示它是这个区里的哪一个字符:第“0xd0 - 0xa1”个。每一个区有94个汉字。区位码从0xa1而不是从0开始,是为了兼容ASCII码。

所以,我们要显示的“中”字,它的GB2312编码是d6d0,它是HZK16里第“(0xd6-0xa1)*94+(0xd0-0xa1)”个字符。


1.3.4 汉字点阵显示实验


打开汉字库文件

4787    fd_hzk16 = open("HZK16", O_RDONLY);
4788    if (fd_hzk16 < 0)
4789    {
4790            printf("can't open HZK16\n");
4791            return -1;
4792    }
4793    if(fstat(fd_hzk16, &hzk_stat))
4794    {
4795            printf("can't get fstat\n");
4796            return -1;
4797    }
4798    hzkmem = (unsigned char *)mmap(NULL , hzk_stat.st_size, PROT_READ, MAP_SHARED, fd_hzk16, 0);
4799    if (hzkmem == (unsigned char *)-1)
4800    {
4801            printf("can't mmap for hzk16\n");
4802            return -1;
4803    }


第4787行打开当前目录的字库文件:HZK16。

第4793行获得文件的状态信息,里面含有文件长度,这在后面的mmap中用到。

第4798行使用mmap映射文件,以后就可以像访问内存一样读取文件内容;mmap的返回结果保存在hzkmem中,它将作为字库的基地址。


2.编写显示汉字的函数

核心函数是void lcd_put_chinese(int x, int y, unsigned char *str),它在LCD的(x,y)位置处显示汉字字符str,str[0]中保存区码、str[1]中保存位码。

代码如下图所示:

1670898673877.jpg

代码分解如下:

第4734行确定该汉字属于哪个区;第4735行确实它是该区中哪一个汉字。

第4736行确实它的字库地址:每个区中有94个汉字,每个汉字在字库中占据32字节。


需要根据下面的图来理解第4740行开始的循环:

1670898684107.jpg

上图是汉字点阵排布的示意图,总共有十六行,因此需要一个循环16次的大循环(第4740行)。

考虑到一行有两个字节,在大循环中加入一个2次的循环用于区分是哪个字节(第4741行)。

最后使用第3个循环来处理一个字节中的8位(第4744行)。对于每一位,它等于1时对应的像素被设置为白色,它等于0时对应的像素被设置为黑色。需要注意的是根据x、y、i、j、b来计算像素坐标。


3.使用lcd_put_chinese函数

程序文件:show_font.c

4762    unsigned char str[] = "中";
……
4810    printf("chinese code: %02x %02x\n", str[0], str[1]);
4811    lcd_put_chinese(var.xres/2 + 8,  var.yres/2, str);


4.编译程序

编译命令:

arm-buildroot-linux-gnueabihf-gcc  -o  show_chinese  show_chinese.c

注意:不同的板子,编译工具的前缀可能不一样。

注意:使用上述命令时show_chinese.c的编码格式必须是ANSI(GB2312),否则编译时需要指定“-fexec-charset=GB2312”。


5.上机实验

把show_chinese程序放到板子上,执行命令:

./show_chinese

如果实验成功,我们将看到屏幕中间会显示出一个白色的字母“A”和“中”。


1.4 交叉编译程序:以freetype为例(这部分很有用)


使用buildroot来给ARM板编译程序、编译库会很简单,以后系统讲解buildroot时再使用buildroot。现在我们还是手工交叉编译freetype,这种方法在编译、安装一些小程序时很有用。

以后肯定会遇到编译运行一些网络下载的库文件,这时候如何手动编译这些库给自己需要的程序使用,这就很关键。


1.4.1 程序运行的一些基础知识


编译程序时去哪找头文件?

系统目录:就是交叉编译工具链里的某个include目录;也可以自己指定:编译时用 “ -I dir ”选项指定。


链接时去哪找库文件?

系统目录:就是交叉编译工具链里的某个lib目录;也可以自己指定:链接时用 “ -L dir ”选项指定。


运行时去哪找库文件?

系统目录:就是板子上的/lib、/usr/lib目录;也可以自己指定:运行程序用环境变量LD_LIBRARY_PATH指定。


1.4.2 常见错误的解决方法


头文件问题

编译时找不到头文件。在程序中这样包含头文件:#include <xxx.h> 对于尖括号里的头文件,去哪里找它?

系统目录:就是交叉编译工具链里的某个include目录;也可以自己指定:编译时用 “ -I dir ”选项指定。

怎么确定“系统目录”?

执行下面命令确定目录:

echo 'main(){}'| arm-buildroot-linux-gnueabihf-gcc -E -v -

它会列出头文件目录、库目录(LIBRARY_PATH)。你需要在头文件目录中确定有没有这个文件,或是自己指定头文件目录。


库文件问题

链接程序时如果有这样的提示:undefined reference toxxx’`,它表示xxx函数未定义。

那么解决方法有2:

① 去写出这个函数

② 或是使用库函数,那需要在链接时指定库


怎么指定库?想链接libabc.so,那链接时加上:-labc。库在哪里?

① 系统目录:就是交叉编译工具链里的某个lib目录

② 也可以自己指定:链接时用 “ -L dir ”选项指定


怎么确定“系统目录”?执行下面命令确定目录:

echo 'main(){}'| arm-buildroot-linux-gnueabihf-gcc -E -v –

它会列出头文件目录、库目录(LIBRARY_PATH),你编译出库文件时,可以把它放入系统库目录。


运行问题

运行程序时找不到库:

error while loading shared libraries: libxxx.so: 
cannot open shared object file: No such file or directory

找不到库,库在哪?

① 系统目录:就是板子上的/lib、/usr/lib目录

② 也可以自己指定:运行程序用环境变量LD_LIBRARY_PATH指定,执行以下的命令:

export  LD_LIBRARY_PATH=/xxx_dir  
./test


LD_LIBRARY_PATH=/xxx_dir   
./test


1.4.3 交叉编译程序的万能命令


如果交叉编辑工具链的前缀是arm-buildroot-linux-gnueabihf-,比如arm-buildroot-linux-gnueabihf-gcc,交叉编译开源软件时,如果它里面有configure,万能命令如下:

./configure  --host=arm-buildroot-linux-gnueabihf   --prefix=$PWD/tmp
make
make install


就可以在当前目录的tmp目录下看见bin, lib, include等目录,里面存有可执行程序、库、头文件。


把头文件、库文件放到工具链目录里

如果你编译的是一个库,请把得到的头文件、库文件放入工具链的include、lib目录里。别的程序要使用这些头文件、库时,会很方便。

工具链里可能有多个include、lib目录,放到哪里去?

执行下面命令来确定目录:

echo 'main(){}'| arm-buildroot-linux-gnueabihf-gcc -E -v –

它会列出头文件目录、库目录(LIBRARY_PATH)。


把库文件放到板子上的/lib或/usr/lib目录里

程序在板子上运行时,需要用到板子上/lib或/usr/lib下的库文件;程序运行时不需要头文件。


1.4.4 给IMX6ULL交叉编译freetype


使用GIT下载所有源码后,本节源码位于如下目录:

01_all_series_quickstart\04_嵌入式Linux应用开发基础知识\source\10_freetype\
freetype-2.10.2.tar.xz
libpng-1.6.37.tar.xz
zlib-1.2.11.tar.gz


freetype依赖于libpng,libpng又依赖于zlib,所以我们应该:先编译安装zlib,再编译安装libpng,最后编译安装freetype。


但是,有些工具链里有zlib, 那就不用编译安装zlib,比如STM32MP157。

对于IMX6ULL,由于版本原因,使用过两套工具链:


比较精简的

export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
export PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin


它里面没有zlib,需要参考下面的文档先编译安装zlib。


比较完善的(基于buildroot)

export ARCH=arm
export CROSS_COMPILE=arm-buildroot-linux-gnueabihf-
export PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin


它里面有zlib,跟着视频操作即可。


本节文档以IMX6ULL开发板中arm-linux-gnueabihf-gcc工具链为例,对于其他开发板:工具链可能不一样,请灵活变通。


确定头文件、库文件在工具链中的目录

先设置交叉编译工具链:

export ARCH=arm
export CROSS_COMPILE=arm-linux-gnueabihf-
export PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin


以IMX6ULL开发板为例,它的工具链是arm-linux-gnueabihf-gcc,可以执行以下命令:

echo 'main(){}'| arm-linux-gnueabihf-gcc -E -v - 

可以确定头文件的系统目录为:

/home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin/../arm-linux-gnueabihf/libc/usr/include

库文件的系统目录为:

/home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin/../arm-linux-gnueabihf/libc/usr/lib/

交叉编译、安装zlib

libpng依赖于zlib,所以需要先编译、安装zlib。命令如下:

book@PC$ tar xzf zlib-1.2.11.tar.gz
book@PC$ cd zlib-1.2.11
book@PC$ export CC=arm-linux-gnueabihf-gcc  
book@PC$ ./configure --prefix=$PWD/tmp
book@PC$ make
book@PC$ make install
book@PC$ cd  tmp
book@PC$ cp  include/*  -rf  /home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin/../arm-linux-gnueabihf/libc/usr/include
book@PC$ cp  lib/*  -rfd   /home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin/../arm-linux-gnueabihf/libc/usr/lib/


交叉编译、安装libpng

freetype依赖于libpng,所以需要先编译、安装libpng。命令如下:

book@PC$ tar  xJf  libpng-1.6.37.tar.xz
book@PC$ cd  libpng-1.6.37
book@PC$ ./configure  --host=arm-linux-gnueabihf   --prefix=$PWD/tmp
book@PC$ make
book@PC$ make install
book@PC$ cd  tmp
book@PC$ cp  include/*  -rf  /home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin/../arm-linux-gnueabihf/libc/usr/include
book@PC$ cp  lib/*  -rfd   /home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin/../arm-linux-gnueabihf/libc/usr/lib/


交叉编译、安装freetype

命令如下:

book@PC$ tar  xJf  freetype-2.10.2.tar.xz
book@PC$ cd  freetype-2.10.2
book@PC$ ./configure  --host=arm-linux-gnueabihf   --prefix=$PWD/tmp
book@PC$ make
book@PC$ make install
book@PC$ cd  tmp
book@PC$ cp  include/*  -rf  /home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin/../arm-linux-gnueabihf/libc/usr/include
book@PC$ cp  lib/*  -rfd   /home/book/100ask_imx6ull-sdk/ToolChain/gcc-linaro-6.2.1-2016.11-x86_64_arm-linux-gnueabihf/bin/../arm-linux-gnueabihf/libc/usr/lib/


1.5 使用freetype显示单个文字


使用GIT下载所有源码后,本节源码位于如下目录:

01_all_series_quickstart\04_嵌入式Linux应用开发基础知识\source\10_freetype\
01_wchar\test_wchar.c
02_freetype_show_font\freetype_show_font.c
03_freetype_show_font_angle\freetype_show_font_angle.c


1.5.1 矢量字体引入


使用点阵字库显示英文字母、汉字时,大小固定,如果放大缩小则会模糊甚至有锯齿出现,为了解决这个问题,引用矢量字体。

矢量字体形成分三步:


① 确定关键点,

② 使用数学曲线(贝塞尔曲线)连接头键点,

③ 填充闭合区线内部空间。


什么是关键点?以字母“A”为例,它的的关键点如下图中的黄色所示。

1670898983346.jpg

再用数学曲线(比如贝塞尔曲线)将关键点都连接起来,得到一系列的封闭的曲线,如下图所示:

1670898992703.jpg


最后把封闭空间填满颜色,就显示出一个A字母,如下图所示:

1670899001206.jpg


如果需要放大或者缩小字体,关键点的相对位置是不变的,只要数学曲线平滑,字体就不会变形。


1.5.2 Freetype介绍


前面我们交叉编译了freetype这个程序。现在介绍一下我们编译这个程序是干什么?


Freetype是开源的字体引擎库,它提供统一的接口来访问多种字体格式文件,从而实现矢量字体显示。我们只需要移植这个字体引擎,调用对应的API接口,提供字体文件,就可以让freetype库帮我们取出关键点、实现闭合曲线,填充颜色,达到显示矢量字体的目的。

关键点(glyph)存在字体文件中,Windows使用的字体文件在c:\Windows\Fonts目录下,扩展名为TTF的都是矢量字库,本次使用实验使用的是新宋字体simsun.ttc。

给定一个字符,怎么在字体文件中找到它的关键点?

首先要确定该字符的编码值:比如ASCII码、GB2312码、UNICODE码。如果字体文件支持某种编码格式(charset),就可以使用这类编码值去找到该字符的关键点(glyph)。有些字体文件支持多种编码格式(charset),这在文件中被称为charmaps(注意:这个单词是复数,意味着可能支持多种charset)。

以simsun.ttc为值,该字体文件的格如下:头部含有charmaps,可以使用某种编码值去charmaps中找到它对应的关键点。下图中的“A、B、中、国、韦”等只是glyph的示意图,表示关键点。

1670899012137.jpg

Charmaps表示字符映射表,字体文件可能支持哪一些编码,GB2312、UNICODE、BIG5或其他。如果字体文件支持该编码,使用编码值通过charmap就可以找到对应的glyph,一般而言都支持UNICODE码。


有了以上基础,一个文字的显示过程可以概括如下:

① 给定一个字符可以确定它的编码值(ASCII、UNICODE、GB2312);

② 设置字体大小;

③ 根据编码值,从文件头部中通过charmap找到对应的关键点(glyph),它会根据字体大小调整关键点;

④ 把关键点转换为位图点阵;

⑤ 在LCD上显示出来


这里可以下载到“freetype-doc-2.10.2.tar.xz”,下图中的文件就是官方文档:

1670899031416.jpg

参照上图中step1,step2,step3里的内容,可以学习如何使用freetype库,总结出下列步骤:

① 初始化:FT_InitFreetype
② 加载(打开)字体Face:FT_New_Face
③ 设置字体大小:FT_Set_Char_Sizes 或 FT_Set_Pixel_Sizes
④ 选择charmap:FT_Select_Charmap
⑤ 根据编码值charcode找到glyph_index:glyph_index = FT_Get_Char_Index(face,charcode)
⑥ 根据glyph_index取出glyph:FT_Load_Glyph(face,glyph_index)
⑦ 转为位图:FT_Render_Glyph
⑧ 移动或旋转:FT_Set_Transform
⑨ 最后显示出来。


上面的⑤⑥⑦可以使用一个函数代替:FT_Load_Char(face, charcode, FT_LOAD_RENDER),它就可以得到位图。

相关文章
|
1天前
|
API 数据安全/隐私保护 UED
探索鸿蒙的蓝牙A2DP与访问API:从学习到实现的开发之旅
在掌握了鸿蒙系统的开发基础后,我挑战了蓝牙功能的开发。通过Bluetooth A2DP和Access API,实现了蓝牙音频流传输、设备连接和权限管理。具体步骤包括:理解API作用、配置环境与权限、扫描并连接设备、实现音频流控制及动态切换设备。最终,我构建了一个简单的蓝牙音频播放器,具备设备扫描、连接、音频播放与停止、切换输出设备等功能。这次开发让我对蓝牙技术有了更深的理解,也为未来的复杂项目打下了坚实的基础。
77 58
探索鸿蒙的蓝牙A2DP与访问API:从学习到实现的开发之旅
|
2天前
【HarmonyOS Next开发】:ListItemGroup使用
通过使用ListItemGroup和AlphabetIndexer两种类型组件,实现带标题分类和右侧导航栏的页面
83 61
|
3天前
|
人工智能 文字识别 算法
|
3天前
|
安全 Java 开发者
|
3天前
|
存储 开发者 容器
|
2天前
|
安全 数据安全/隐私保护
鸿蒙开发:一文了解软键盘相关
软键盘最主要的就是合理的进行避让,不能遮挡可输入组件,再有多个输入框的时候,需要动态的进行设置高度,这一点需要注意。
鸿蒙开发:一文了解软键盘相关
|
2天前
鸿蒙开发:一个轻盈的上拉下拉刷新组件
在和可滑动组件使用的时候,记得一定要和nestedScroll属性配合使用,用于解决滑动冲突,除此之外,还需要传递滑动组件的scroller属性,用于手势操作。
鸿蒙开发:一个轻盈的上拉下拉刷新组件
|
3天前
|
开发框架 前端开发 JavaScript
uniapp开发鸿蒙,是前端新出路吗?
相信不少前端从业者一听uniapp支持开发鸿蒙Next后非常振奋。猫林老师作为7年前端er也是非常激动,第一时间体验了下。在这里也给大家分享一下我的看法
33 17
|
3天前
|
前端开发 开发者
|
2天前
|
开发者
【HarmonyOS Next开发】用户文件访问
文件所有者为登录到该终端设备的用户,包括用户私有的图片、视频、音频、文档等。 应用对用户文件的创建、访问、删除等行为,需要提前获取用户授权,或由用户操作完成。
22 10