嵌入式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),它就可以得到位图。

相关文章
|
14天前
|
JSON 机器人 Linux
推荐一款嵌入式Linux开源框架与封装-cpp-tbox
推荐一款嵌入式Linux开源框架与封装-cpp-tbox
62 3
|
14天前
|
Ubuntu 算法 Linux
嵌入式Linux的学习误区
该文指出了学习嵌入式Linux开发的两个常见误区。一是过分专注于学习桌面或服务器版Linux,而非关注嵌入式开发本身,实际上只需熟悉基本操作即可。二是试图在没有基础的情况下直接阅读Linux内核源代码,这是不切实际的,应先建立基础知识再进行源码学习。文章还提到了在嵌入式系统中获取和处理屏幕数据的示例,包括使用gsnap工具将framebuffer数据转为图像,以及涉及的交叉编译过程。
14 0
|
14天前
|
Linux 编译器 测试技术
嵌入式 Linux 下的 LVGL 移植
嵌入式 Linux 下的 LVGL 移植
|
14天前
|
Linux 开发工具
【ZYNQ】配置嵌入式 Linux 静态 IP 地址
【ZYNQ】配置嵌入式 Linux 静态 IP 地址
|
6月前
|
消息中间件 缓存 Unix
[面试必备]嵌入式Linux内核开发必须了解的三十道题
[面试必备]嵌入式Linux内核开发必须了解的三十道题
|
8月前
|
Linux
嵌入式Linux QT开发之如何实现获取磁盘空间大小的应用逻辑
嵌入式Linux QT开发之如何实现获取磁盘空间大小的应用逻辑
158 0
|
11月前
|
Linux Go 人机交互
嵌入式linux之go语言开发(十三)LittlevGL,漂亮的嵌入式GUI的go语言绑定
嵌入式linux之go语言开发(十三)LittlevGL,漂亮的嵌入式GUI的go语言绑定
|
11月前
|
存储 XML JSON
嵌入式linux之go语言开发(十二)参数配置文件存储模块开发
嵌入式linux之go语言开发(十二)参数配置文件存储模块开发
|
11月前
|
Linux Go C语言
嵌入式linux之go语言开发(十一)让web服务器跑在终端上,通过网页配置终端参数
嵌入式linux之go语言开发(十一)让web服务器跑在终端上,通过网页配置终端参数
|
11月前
|
存储 监控 物联网
嵌入式linux之go语言开发(十)
嵌入式linux之go语言开发(十)