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

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

1.5.3 在LCD上显示一个矢量字体


使用wchar_t获得字符的UNICODE值

要显示一个字符,首先要确定它的编码值。常用的是UNICODE编码,在程序里使用这样的语句定义字符串时,str中保存的要么GB2312编码值,要么是UTF-8格式的编码值,即使编译时使用“-fexec-charset=UTF-8”,str中保存的也不是直接能使用的UNICODE值:char *str = “中”;

如果想在代码中能直接使用UNICODE值,需要使用wchar_t,宽字符,示例代码如下:

01 #include <stdio.h>
02 #include <string.h>
03 #include <wchar.h>
04
05 int main( int argc, char** argv)
06 {
07      wchar_t *chinese_str = L"中gif";
08      unsigned int *p = (wchar_t *)chinese_str;
09      int i;
10
11      printf("sizeof(wchar_t) = %d, str's Uniocde: \n", (int)sizeof(wchar_t));
12      for (i = 0; i < wcslen(chinese_str); i++)
13      {
14              printf("0x%x ", p[i]);
15      }
16      printf("\n");
17
18      return 0;
19 }
20 }


以UTF-8格式保存test_wchar.c,编译、测试命令如下:

book@book-virtual-machine:~/10_freetype/01_wchar$ gcc -o test_wchar test_wchar.c
book@book-virtual-machine:~/10_freetype/01_wchar$ ./test_wchar
sizeof(wchar_t) = 4, str's Uniocde:
0x4e2d 0x67 0x69 0x66


每个wchar_t占据4字节,可执行程序里wchar_t中保存的就是字符的UNICODE值。

注意:如果test_wchar.c是以ANSI(GB2312)格式保存,那么需要使用以下命令来编译:

gcc -finput-charset=GB2312  -fexec-charset=UTF-8  -o test_wchar test_wchar.c

使用freetype得到位图

参考“freetype-doc-2.10.2\freetype-2.10.2\docs\tutorial\image.c”,使用freetype显示一个字符并不难。使用GIT下载所有源码后,本节源码位于如下目录:

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


要使用freetype得到一个字符的位图,只需要4个步骤,代码先贴出来再分析:

1670899174996.jpg

① 初始化freetype库

158     error = FT_Init_FreeType( &library );                      /* initialize library */

② 加载字体文件,保存在&face中:

161     error = FT_New_Face( library, argv[1], 0, &face ); /* create face object */
162     /* error handling omitted */
163     slot = face->glyph;


第163行是从face中获得FT_GlyphSlot,后面的代码中文字的位图就是保存在FT_GlyphSlot里。


③ 设置字体大小

165     FT_Set_Pixel_Sizes(face, font_size, 0);


④ 根据编码值得到位图

使用FT_Load_Char函数,就可以实现这3个功能:

a. 根据编码值获得glyph_index:FT_Get_Char_Index

b. 根据glyph_idex取出glyph:FT_Load_Glyph

c. 渲染出位图:FT_Render_Glyph

代码如下:

175     /* load glyph image into the slot (erase previous one) */
176     error = FT_Load_Char( face, chinese_str[0], FT_LOAD_RENDER );

执行FT_Load_Char之后,字符的位图被存在slot->bitmap里,即face->glyph->bitmap。


3. 在屏幕上显示位图

位图里的数据格式是怎样的?参考example1.c的代码,可以得到下面这个图:

要在屏幕上显示出这些位图,并不复杂:

1670899216136.jpg

183     draw_bitmap( &slot->bitmap,
184                  var.xres/2,
185                  var.yres/2);


draw_bitmap函数代码如下,由于位图中每一个像素用一个字节来表示,在0x00RRGGBB的颜色格式中它只能表示蓝色,所以在LCD上显示出来的文字是蓝色的:

1670899240383.jpg


4. 编译

编译命令(如果你使用的交叉编译链前缀不是arm-buildroot-linux-gnueabihf,请自行修改命令):

$ arm-buildroot-linux-gnueabihf-gcc -o freetype_show_font freetype_show_font.c  -lfreetype


它会提示如下错误:

freetype_show_font.c:12:10: fatal error: ft2build.h: No such file or directory
 #include <ft2build.h>
          ^~~~~~~~~~~~
compilation terminated.


我们不是已经编译过freetype并且把头文件复制进工具链里了吗?怎么还有这个错误?

我们编译出freetype后,得到的ft2build.h是位于freetype2目录里,我们把整个freetype2目录复制进了工具链里。

但是包括头文件时,用的是“#include <ft2build.h>”,要么改成:

#include <freetype2/ft2build.h>
要么把工具链里incldue/freetype2/*.h 复制到上一级目录,我们使用这种方法:跟freetype文档保持一致。执行以下命令:
book@PC$ cd   /home/book/100ask_stm32mp157_pro-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/arm-buildroot-linux-gnueabihf/sysroot/usr/include
book@PC$ mv  freetype2/*   ./


然后再次执行以下命令:

$ arm-buildroot-linux-gnueabihf-gcc -o freetype_show_font freetype_show_font.c  -lfreetype


上机

将编译好的freetype_show_font文件与simsun.ttc字体文件拷贝至开发板,这2个文件放在同一个目录下,然后执行以下命令。

[root@board:~]# ./freetype_show_font  ./simsun.ttc   
[root@board:~]# ./freetype_show_font  ./simsun.ttc   300


如果实验成功,我们将在屏幕中间看到一个蓝色的“繁”字。


1.5.4 LCD上令矢量字体旋转某个角度


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

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


在实现显示一个矢量字体后,我们可以添加让该字旋转某个角度的功能,主要代码还是参照example1.c。


在实现显示一个矢量字体后,我们可以添加让该字旋转某个角度的功能,主要代码还是参照example1.c。


关键代码

① 定义2个变量:角度、矩阵,如下:

1670899356820.jpg

② 设置角度值:

1670899366090.jpg

③ 设置矩阵、交形、加载位图:

1670899374181.jpg

2. 编译

编译命令(如果你使用的交叉编译链前缀不是arm-buildroot-linux-gnueabihf,请自行修改命令):

$ arm-buildroot-linux-gnueabihf-gcc -o freetype_show_font_angle freetype_show_font_angle.c  -lfreetype -lm 

上机

将编译好的freetype_show_font_angle文件与simsun.ttc字体文件拷贝至开发板,这2个文件放在同一个目录下,然后执行以下命令。

[root@board:~]# ./freetype_show_font_angle  ./simsun.ttc   90  200


如果实验成功,我们将在屏幕中间看到一个蓝色的、旋转了90度的“繁”字。


1.6 使用freetype显示一行字


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

01_all_series_quickstart\
04_嵌入式Linux应用开发基础知识\source\10_freetype\
04_show_line\show_line.c


本节的目的:在LCD上指定一个左上角坐标(x, y),把一行文字显示出来。下图中,文字的外框用虚线表示,外框的左上角坐标就是(x, y)。

1670899411705.jpg


1.6.1 笛卡尔坐标系


在LCD的坐标系中,原点在屏幕的左上角。对于笛卡尔坐标系,原点在左下角。freetype使用笛卡尔坐标系,在显示时需要转换为LCD坐标系。

从下图可知,X方向坐标值是一样的。

在Y方向坐标值需要换算,假设LCD的高度是V。

在LCD坐标系中坐标是(x, y),那么它在笛卡尔坐标系中的坐标值为(x, V-y)。

反过来也是一样的,在笛卡尔坐标系中坐标是(x, y),那么它在LCD坐标系中坐标值为(x, V-y)。

1670899425528.jpg


1.6.2 每个字符的大小可能不同


在使用FT_Set_Pixel_Sizes函数设置字体大小时,这只是“期望值”。比如“百问网www.100ask.net”,如果把“.”显示得跟其他汉字一样大,不好看。

所以在显示一行文字时,后面文字的位置会受到前面文字的影响。

幸好,freetype帮我们考虑到了这些影响。

对于freetype字体的尺寸(freetype Metrics),需要参考下图这个文档:

1670899611344.jpg

上述文档中列出了一个图,摘录如下:

1670899629774.jpg

在显示一行文字时,这些文字会基于同一个基线来绘制位图:baseline。

在baseline上,每一个字符都有它的原点(origin),比如上图中baseline左边的黑色圆点就是字母“g”的原点。当前origin加上advance就可以得到下一个字符的origin,比如上图中baseline右边的黑色圆点。在显示一行中多个文件字时,后一个文字的原点依赖于前一个文字的原点及advance。

字符的位图是有可能越过baseline的,比如上图中字母“g”在baseline下方还有图像。

上图中红色方框内就是字母“g”所点据的位图,它的四个角落不一定与原点重合。

上图中那些xMin、xMax、yMin、yMax如何获得?可以使用FT_Glyph_Get_CBox函数获得一个字体的这些参数,将会保存在一个FT_BBox结构体中,以后想计算一行文字的外框时要用到这些信息:

1670899640790.jpg


1.6.3 怎么在指定位置显示一行文字


要显示一行文字时,每一个字符都有自己外框:xMin、xMax、yMin、yMax。把这些字符的xMin、yMin中的最小值取出来,把这些字符的xMax、yMax中的最大值取出来,就可以确定这行文字的外框了。


要想在指定位置(x, y)显示一行文字,步骤如下图所示:

1670899674937.jpg

① 先指定第1个字符的原点pen坐标为(0, 0),计算出它的外框

② 再计算右边字符的原点,也计算出它的外框

把所有字符都处理完后就可以得到一行文字的整体外框:假设外框左上角坐标为(x’, y’)。

③ 想在(x, y)处显示这行文字,调整一下pen坐标即可

怎么调整?

pen为(0, 0)时对应左上角(x’, y’);

那么左上角为(x, y)时就可以算出pen为(x-x’, y-y’)。


1.6.4 freetype的几个重要数据结构


要想形象地理解程序,需要先介绍一下freetype中几个数据结构:

FT_Library
对应freetype库,使用freetype之前要先调用以下代码:
FT_Library  library; /* 对应freetype库 */
error = FT_Init_FreeType( &library ); /* 初始化freetype库 */


FT_Face

它对应一个矢量字体文件,在源码中使用FT_New_Face函数打开字体文件后,就可以得到一个face。

为什么称之为face?

估计是文字都是写在二维平面上的吧,正对着人脸?不用管原因了,总之认为它对应一个字体文件就可以。

代码如下:

error = FT_New_Face(library, font_file, 0, &face ); /* 加载字体文件 */


FT_GlyphSlot

插槽?用来保存字符的处理结果:比如转换后的glyph、位图,如下图:

1670899717050.jpg

一个face中有很多字符,生成一个字符的点阵位图时,位图保存在哪里?保存在插槽中:face->glyph。

生成第1个字符位图时,它保存在face->glyph中;生成第2个字符位图时,也会保存在face->glyph中,会覆盖第1个字符的位图。

代码如下:

FT_GlyphSlot  slot = face->glyph; /* 插槽: 字体的处理结果保存在这里 */

FT_Glyph

字体文件中保存有字符的原始关键点信息,使用freetype的函数可以放大、缩小、旋转,这些新的关键点保存在插槽中(注意:位图也是保存在插槽中)。

新的关键点使用FT_Glyph来表示,可以使用这样的代码从slot中获得glyph:

error = FT_Get_Glyph(slot , &glyph);

FT_BBox

FT_BBox结构体定义如下,它表示一个字符的外框,即新glyph的外框:

1670899798797.jpg

可以使用以下代码从glyph中获得这些信息:

FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_TRUNCATE, &bbox );

针对上述流程,示例代码如下:

1670899808647.jpg


1.6.5 计算一行文字的外框


前面提到过,一行文字中:后一个字符的原点=前一个字符的原点+advance。

所以要计算一行文字的外框,需要按照排列顺序处理其中的每一个字符。


代码如下,注释写得很清楚了:

102 int compute_string_bbox(FT_Face       face, wchar_t *wstr, FT_BBox  *abbox)
103 {
104     int i;
105     int error;
106     FT_BBox bbox;
107     FT_BBox glyph_bbox;
108     FT_Vector pen;
109     FT_Glyph  glyph;
110     FT_GlyphSlot slot = face->glyph;
111
112     /* 初始化 */
113     bbox.xMin = bbox.yMin = 32000;
114     bbox.xMax = bbox.yMax = -32000;
115
116     /* 指定原点为(0, 0) */
117     pen.x = 0;
118     pen.y = 0;
119
120     /* 计算每个字符的bounding box */
121     /* 先translate, 再load char, 就可以得到它的外框了 */
122     for (i = 0; i < wcslen(wstr); i++)
123     {
124         /* 转换:transformation */
125         FT_Set_Transform(face, 0, &pen);
126
127         /* 加载位图: load glyph image into the slot (erase previous one) */
128         error = FT_Load_Char(face, wstr[i], FT_LOAD_RENDER);
129         if (error)
130         {
131             printf("FT_Load_Char error\n");
132             return -1;
133         }
134
135         /* 取出glyph */
136         error = FT_Get_Glyph(face->glyph, &glyph);
137         if (error)
138         {
139             printf("FT_Get_Glyph error!\n");
140             return -1;
141         }
142
143         /* 从glyph得到外框: bbox */
144         FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_TRUNCATE, &glyph_bbox);
145
146         /* 更新外框 */
147         if ( glyph_bbox.xMin < bbox.xMin )
148             bbox.xMin = glyph_bbox.xMin;
149
150         if ( glyph_bbox.yMin < bbox.yMin )
151             bbox.yMin = glyph_bbox.yMin;
152
153         if ( glyph_bbox.xMax > bbox.xMax )
154             bbox.xMax = glyph_bbox.xMax;
155
156         if ( glyph_bbox.yMax > bbox.yMax )
157             bbox.yMax = glyph_bbox.yMax;
158
159         /* 计算下一个字符的原点: increment pen position */
160         pen.x += slot->advance.x;
161         pen.y += slot->advance.y;
162     }
163
164     /* return string bbox */
165     *abbox = bbox;
166 }


1.6.6 调整原点并绘制


代码如下,也不复杂:

169 int display_string(FT_Face     face, wchar_t *wstr, int lcd_x, int lcd_y)
170 {
171     int i;
172     int error;
173     FT_BBox bbox;
174     FT_Vector pen;
175     FT_Glyph  glyph;
176     FT_GlyphSlot slot = face->glyph;
177
178     /* 把LCD坐标转换为笛卡尔坐标 */
179     int x = lcd_x;
180     int y = var.yres - lcd_y;
181
182     /* 计算外框 */
183     compute_string_bbox(face, wstr, &bbox);
184
185     /* 反推原点 */
186     pen.x = (x - bbox.xMin) * 64; /* 单位: 1/64像素 */
187     pen.y = (y - bbox.yMax) * 64; /* 单位: 1/64像素 */
188
189     /* 处理每个字符 */
190     for (i = 0; i < wcslen(wstr); i++)
191     {
192         /* 转换:transformation */
193         FT_Set_Transform(face, 0, &pen);
194
195         /* 加载位图: load glyph image into the slot (erase previous one) */
196         error = FT_Load_Char(face, wstr[i], FT_LOAD_RENDER);
197         if (error)
198         {
199             printf("FT_Load_Char error\n");
200             return -1;
201         }
202
203         /* 在LCD上绘制: 使用LCD坐标 */
204         draw_bitmap( &slot->bitmap,
205                         slot->bitmap_left,
206                         var.yres - slot->bitmap_top);
207
208         /* 计算下一个字符的原点: increment pen position */
209         pen.x += slot->advance.x;
210         pen.y += slot->advance.y;
211     }
212
213     return 0;
214 }


1.6.7 上机实验


编译命令(如果你使用的交叉编译链前缀不是arm-buildroot-linux-gnueabihf,请自行修改命令):

$ arm-buildroot-linux-gnueabihf-gcc  -o  show_line  show_line.c   -lfreetype 

将编译好的show_line文件与simsun.ttc字体文件拷贝至开发板,这2个文件放在同一个目录下,然后执行以下命令(其中的3个数字分别表示LCD的X坐标、Y坐标、字体大小):

[root@board:~]# ./show_line ./simsun.ttc 10 200 80

如果实验成功,可以在LCD上看到一行文字“百问网www.100ask.net”。

相关文章
|
4月前
|
存储 网络协议 Ubuntu
【Linux开发实战指南】基于UDP协议的即时聊天室:快速构建登陆、聊天与退出功能
UDP 是一种无连接的、不可靠的传输层协议,位于IP协议之上。它提供了最基本的数据传输服务,不保证数据包的顺序、可靠到达或无重复。与TCP(传输控制协议)相比,UDP具有较低的传输延迟,因为省去了建立连接和确认接收等过程,适用于对实时性要求较高、但能容忍一定数据丢失的场景,如在线视频、语音通话、DNS查询等。 链表 链表是一种动态数据结构,用于存储一系列元素(节点),每个节点包含数据字段和指向下一个节点的引用(指针)。链表分为单向链表、双向链表和循环链表等类型。与数组相比,链表在插入和删除操作上更为高效,因为它不需要移动元素,只需修改节点间的指针即可。但访问链表中的元素不如数组直接,通常需要从
265 2
|
29天前
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
81 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
|
2月前
|
存储 Linux 开发工具
如何进行Linux内核开发【ChatGPT】
如何进行Linux内核开发【ChatGPT】
|
3月前
|
Java Linux API
Linux设备驱动开发详解2
Linux设备驱动开发详解
43 6
|
3月前
|
消息中间件 算法 Unix
Linux设备驱动开发详解1
Linux设备驱动开发详解
49 5
|
3月前
|
NoSQL Linux C语言
嵌入式GDB调试Linux C程序或交叉编译(开发板)
【8月更文挑战第24天】本文档介绍了如何在嵌入式环境下使用GDB调试Linux C程序及进行交叉编译。调试步骤包括:编译程序时加入`-g`选项以生成调试信息;启动GDB并加载程序;设置断点;运行程序至断点;单步执行代码;查看变量值;继续执行或退出GDB。对于交叉编译,需安装对应架构的交叉编译工具链,配置编译环境,使用工具链编译程序,并将程序传输到开发板进行调试。过程中可能遇到工具链不匹配等问题,需针对性解决。
|
3月前
|
编解码 安全 Linux
基于arm64架构国产操作系统|Linux下的RTMP|RTSP低延时直播播放器开发探究
这段内容讲述了国产操作系统背景下,大牛直播SDK针对国产操作系统与Linux平台发布的RTMP/RTSP直播播放SDK。此SDK支持arm64架构,基于X协议输出视频,采用PulseAudio和Alsa Lib处理音频,具备实时静音、快照、缓冲时间设定等功能,并支持H.265编码格式。此外,提供了示例代码展示如何实现多实例播放器的创建与管理,包括窗口布局调整、事件监听、视频分辨率变化和实时快照回调等关键功能。这一技术实现有助于提高直播服务的稳定性和响应速度,适应国产操作系统在各行业中的应用需求。
109 3
|
4月前
|
Web App开发 缓存 Linux
FFmpeg开发笔记(三十六)Linux环境安装SRS实现视频直播推流
《FFmpeg开发实战》书中第10章提及轻量级流媒体服务器MediaMTX,适合测试RTSP/RTMP协议,但不适合生产环境。推荐使用SRS或ZLMediaKit,其中SRS是国产开源实时视频服务器,支持多种流媒体协议。本文简述在华为欧拉系统上编译安装SRS和FFmpeg的步骤,包括安装依赖、下载源码、配置、编译以及启动SRS服务。此外,还展示了如何通过FFmpeg进行RTMP推流,并使用VLC播放器测试拉流。更多FFmpeg开发内容可参考相关书籍。
103 2
FFmpeg开发笔记(三十六)Linux环境安装SRS实现视频直播推流
|
4月前
|
Linux
FFmpeg开发笔记(三十四)Linux环境给FFmpeg集成libsrt和librist
《FFmpeg开发实战》书中介绍了直播的RTSP和RTMP协议,以及新协议SRT和RIST。SRT是安全可靠传输协议,RIST是可靠的互联网流传输协议,两者于2017年发布。腾讯视频云采用SRT改善推流卡顿。以下是Linux环境下为FFmpeg集成libsrt和librist的步骤:下载安装源码,配置、编译和安装。要启用这些库,需重新配置FFmpeg,添加相关选项,然后编译和安装。成功后,通过`ffmpeg -version`检查版本信息以确认启用SRT和RIST支持。详细过程可参考书中相应章节。
91 1
FFmpeg开发笔记(三十四)Linux环境给FFmpeg集成libsrt和librist
|
4月前
|
弹性计算 运维 自然语言处理
阿里云OS Copilot测评:重塑Linux运维与开发体验的智能革命
阿里云OS Copilot巧妙地将大语言模型的自然语言处理能力与操作系统团队的深厚经验相结合,支持自然语言问答、辅助命令执行等功能,为Linux用户带来了前所未有的智能运维与开发体验。