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

相关文章
|
11天前
|
JavaScript 前端开发 UED
【HarmonyOS Next之旅】基于ArkTS开发(二) -> UI开发四
本文介绍了Web组件开发与性能优化的相关内容。在Web组件开发部分,涵盖创建组件、设置样式与属性、添加事件和方法以及场景示例,如动态播放视频。性能提升方面,推荐使用数据懒加载、条件渲染替代显隐控制、Column/Row替代Flex、设置List组件宽高及调整cachedCount减少滑动白块等方法,以优化应用性能与用户体验。
35 1
|
11天前
|
JavaScript 前端开发 IDE
鸿蒙开发:了解布局分析ArkUI Inspector
ArkUI Inspector,知名其意,就是UI检查,它可以让开发者在DevEco Studio中快速的查看一个应用在模拟器或者真机上的UI显示效果,并且可以通过查看多次操作后的界面状态,来快速的分析定位UI界面存在的问题。
鸿蒙开发:了解布局分析ArkUI Inspector
|
11天前
|
数据挖掘 测试技术 开发工具
鸿蒙开发:hvigorw,编译构建,实现命令打包
以上呢,就是hvigorw几个常见的命令,主要用于构建不同类型的包,也是接下来流水线打包,几个比较常用的命令,所以拿来重点概述了,当然了hvigorw还有一些常见的命令,大家直接看官网介绍即可,不在多赘述。
鸿蒙开发:hvigorw,编译构建,实现命令打包
|
11天前
|
缓存 开发工具 开发者
鸿蒙开发:了解构建工具hvigorw
hvigorw作为Hvigor的wrapper包装工具,它的主要作用是,支持自动安装Hvigor构建工具和相关插件依赖,以及执行Hvigor构建命令
鸿蒙开发:了解构建工具hvigorw
|
11天前
|
安全 前端开发 开发工具
鸿蒙开发:应用内如何做更新
使用系统的,直接调用检查和显示更新弹窗即可,可以说就两个方法,我们就实现了应用更新的功能,可以说是非常的简单,如果系统的弹窗无法满足您的需求,您可以自定义弹窗,然后实现跳转应用详情页面即可。
鸿蒙开发:应用内如何做更新
|
14天前
|
JavaScript 前端开发 开发者
09.HarmonyOS Next数据驱动UI开发:ForEach与动态渲染完全指南(上)
在现代前端开发中,数据驱动UI已成为主流开发范式。HarmonyOS Next的ArkTS语言和声明式UI框架完美支持这一理念,使开发者能够以更高效、更直观的方式构建复杂应用。
45 1
|
11天前
|
编解码 UED 开发者
【HarmonyOS Next之旅】基于ArkTS开发(二) -> UI开发之常见布局
本文主要介绍了自适应布局与响应式布局的相关内容。自适应布局部分涵盖线性布局、层叠布局、弹性布局和网格布局,详细说明了各布局的特性及使用方法,例如线性布局中的排列、拉伸与缩放,弹性布局的方向、换行与对齐方式等。响应式布局则重点讲解了栅格系统和媒体查询,阐述如何通过栅格组件和媒体查询条件实现不同设备上的适配效果。这些技术帮助开发者灵活应对多尺寸屏幕的设计需求,提升用户体验。
47 0
|
18天前
|
开发框架 前端开发 JavaScript
【HarmonyOS Next之旅】基于ArkTS开发(二) -> UI开发一
本文介绍了方舟开发框架(ArkUI)及其两种开发范式:基于ArkTS的声明式开发范式和类Web开发范式。ArkUI是用于构建HarmonyOS应用界面的UI框架,提供极简UI语法和基础设施。声明式开发范式使用ArkTS语言,以组件、动画和状态管理为核心,适合复杂团队协作;类Web开发范式采用HML、CSS、JavaScript三段式开发,适用于简单界面应用,贴近Web开发者习惯。文中还概述了两者的架构和基础能力,帮助开发者选择合适的范式进行高效开发。
68 15
|
18天前
|
编解码 前端开发 Java
【HarmonyOS Next之旅】基于ArkTS开发(二) -> UI开发三
本文介绍了基于声明式UI范式的图形绘制与动画效果实现方法,涵盖绘制图形、添加动画效果及常见组件说明三部分内容。在绘制图形部分,详细讲解了如何通过Circle组件为食物成分表添加圆形标签,以及使用Path组件结合SVG命令绘制自定义图形(如应用Logo)。动画效果部分则展示了如何利用animateTo实现闪屏动画,包括渐出、放大效果,并设置页面跳转;同时介绍了页面间共享元素转场动画的实现方式。最后,文章列举了声明式开发范式中的各类组件及其功能,帮助开发者快速上手构建复杂交互页面。
59 11
|
19天前
|
开发工具
鸿蒙开发:DevEcoStudio中的代码生成
其实大家可以发现,一篇文章下来,都是基于右键后的Generate选项,所以,还是非常的简单的,当然了,还是希望大家,以上的功能,能够应用在实际的开发中,而不是停留在纸面上。
鸿蒙开发:DevEcoStudio中的代码生成