正点原子Linux学习笔记(九)在 LCD 上显示字符

在 LCD 上显示字符

  • 23.1 原始方式:取模显示字符
  • 23.2 freetype 简介
  • 23.3 freetype 移植
    • 下载 FreeType 源码
    • 交叉编译 FreeType 源码
    • 安装目录下的文件
    • 移植到开发板
  • 23.4 freetype 库的使用
    • 初始化 FreeType 库
    • 加载 face 对象
    • 设置字体大小
    • 加载字形图像
  • 23.5 示例代码

前面几个章节向大家介绍了如何在 LCD 屏上显示图像,本章我们就来学习下,如何在 LCD 屏上显示字符,譬如数字、字母以及中文字符等,相信很多读者都已经迫不及待的想要了解了。OK,废话不多说直接开干!
本章将会讨论如下主题。
⚫ 使用原始的方式:自己取模显示字符
⚫ 使用 freetype 访问字体文件;
⚫ freetype 简介;
⚫ freetype 移植;
⚫ freetype 的使用介绍。

23.1 原始方式:取模显示字符

LCD 显示屏是由 width * height 个像素点构成的,显示字符,一个非常容易想到的方法便是对字符取模,然后在 LCD 屏上打点显示字符;如果大家以前学习过单片机,想必接触过一些显示屏,譬如 oled、或者其它一些点阵式的显示屏,其实这些显示屏显示字符的原理都是一样的,如下所示:
在这里插入图片描述
我们可以通过一些字符取模软件获取到字符的子模;所谓子模,其实就是一个二维数组,用于表示字符点阵中,哪些小方块应该要填充颜色、哪些小方块不填充颜色。譬如上图“正”字符点阵,这是一个宽度为64(64 个小方块)、高度为 86(86 个小方块)的字符点阵,我们会使用一个二维数组来表示这个字符点阵:

unsigned char arr[86][8];

也就是一个 86 行 8 列的 unsigned char 类型数组,数组存储的其实就是字符的位图数据,字符点阵中的每一个小方块对应一个 bit 位,因为一行一共有 64 个小方块、也就对应 8 个字节(8 * 8 = 64);将填充颜色的方块使用 1 表示、不填充颜色的方块使用 0 来表示,所以一个小方块刚好可以使用一个 bit 位来描述。

以上给大家简单地介绍了字符点阵的问题,相信绝大部分读者都知道这些基础的东西,其实本不太想讲这些内容,但是考虑到可能有些读者确实就真的没接触这些,所以还是简单地提一下。我们编写一个简单的程序去测试下,网上有很多的这种字符取模的小软件,大家可以找一下,我们用这个取模软件,获取几个字符的子模,然后在我们的 LCD 屏上去显示这些字符。

23.2 freetype 简介

FreeType 一个完全免费(开源)的软件字体引擎库,设计小巧、高效、高度可定制且可移植,它提供了统一的接口来访问多种不同格式的字体文件。它提供了一个简单、易于使用且统一的接口来访问字体文件的内容,从而大大简化了这些任务。
请注意,“FreeType”也称为“FreeType 2”,以区别于旧的、已弃用的“FreeType 1”库,Freetype 1库已经不再维护和支持了。

23.3 freetype 移植

本小节我们来移植 FreeType,将 FreeType 移植到我们的开发板根文件系统中;事实上,开发板出厂系统已经移植好了这些库,但是版本稍微太低了,作为学习者,我们应该要学会自己动手移植,这个是很重要的工作;从上几个章节的学习内容可知,我们很多的编程工作都是基于别人写好的库、调用库函数来实现应用程序的功能,因为 Linux 软件生态做得非常好,有很多免费、开源的库是可以使用的,不用自己去捣鼓这些东西;所以作为一个嵌入式 Linux 软件开发人员,学会移植就显得很重要了。

下载 FreeType 源码

开发板出厂系统中,FreeType 的版本为 2.6,这个版本稍微有点低,我们选择移植 2.8 版本的 FreeType。进入到 https://download.savannah.gnu.org/releases/freetype/链接地址,如下所示:
在这里插入图片描述
在这里插入图片描述
大家要自己动手下载,开发板资料盘中是没有提供这些源码包的,包括前面几个章节介绍的库,譬如libpng、libjpeg 以及 tslib 等,都没有提供它们的源码压缩文件。

交叉编译 FreeType 源码

将下载好的 freetype-2.8.tar.gz 压缩文件拷贝到 Ubuntu 系统的用户家目录下,如下所示:
在这里插入图片描述
在 tools 目录下创建一个名为 freetype 的目录,把它作为 FreeType 的安装目录:
在这里插入图片描述
执行命令将 freetype-2.8.tar.gz 解压开来:

tar -xzf freetype-2.8.tar.gz

在这里插入图片描述
解压成功之后便会得到 FreeType 的源码目录 freetype-2.8。
进入到 freetype-2.8 目录,老规矩,同样是三部曲:配置、编译、安装。首先对交叉编译工具的环境进行初始化,前面章节内容已经提过很多次了,使用交叉编译器之前,必须要对其环境进行初始化(如果当前终端已经初始化过了,则无需再次进行初始化)。

FreeType 库基于模块化设计,意味着我们可以对其进行裁剪,将不需要的功能模块从配置中移除,减小库文件的体积;除此之外,FreeType 还支持很多配置选项,如果大家想要对 FreeType 做一些自定义配置或者对其进行裁剪,可以参考 FreeType 源码目录下 docs/CUSTOMIZE 文档,该文件对此有比较详细的说明,建议大家看一看,如果有需求的话。docs 目录下还有其它很多的说明文档,也都可以读一读。这里我们简单地配置一下,打开 include/freetype/config/ftoption.h 文件,如下所示:

vi include/freetype/config/ftoption.h

在这里插入图片描述
该文件定义了很多的配置宏,我们可以选择使能或禁用这些配置选项,具体配置哪些功能,大家自己去研究,每一个配置宏都有详细地解释说明。这里我们打开以下两个配置宏:

#define FT_CONFIG_OPTION_SYSTEM_ZLIB
#define FT_CONFIG_OPTION_USE_PNG

大家找到这两个宏,默认情况下,这两个都被注释掉了,所以是没有使能的;把这两个宏的注释去掉,使能这两个配置宏。

第一个配置宏表示使用系统安装的 zlib 库,因为 FreeType 支持 Gzip 压缩文件,会使用到 zlib 库,zlib之前我们移植好了;第二个配置宏表示支持 PNG bitmap 位图,因为 FreeType 可以加载 PNG 格式的彩色位图字形,需要依赖于 libpng 库,这个库前面我们也是移植好了。

配置好之后,保存、退出 ftoption.h 文件,接着执行如下命令对 FreeType 工程源码进行配置:

/configure --prefix=/home/dt/tools/freetype/ --host=arm-poky-linux-gnueabi --with-zlib=yes --with-bzip2=no - -with-png=yes --with-harfbuzz=no ZLIB_CFLAGS="-I/home/dt/tools/zlib/include -L/home/dt/tools/zlib/lib" 
ZLIB_LIBS=-lz LIBPNG_CFLAGS="-I/home/dt/tools/png/include -L/home/dt/tools/png/lib" LIBPNG_LIBS=-
lpng

这个配置命令很长,简单地提一下,具体的细节大家可以执行"./configure --help"查看配置帮助信息。
–prefix 选项指定 FreeType 库的安装目录;–host 选项设置为交叉编译器名称的前缀,这两个选项前面几个章节内容都已经给大家详细地解释过。
–with-zlib=yes 表示使用 zlib;
–with-bzip2=no 表示不使用 bzip2 库;
–with-png=yes 表示使用 libpng 库;
–with-harfbuzz=no 表示不使用 harfbuzz 库。
ZLIB_CFLAGS 选项用于指定 zlib 的头文件路径和库文件路径,根据实际安装路径填写;
ZLIB_LIBS 选项指定链接的 zlib 库的名称;
LIBPNG_CFLAGS 选项用于指定 libpng 的头文件路径和库文件路径,根据实际安装路径填写;
LIBPNG_LIBS 选项用于指定链接的 libpng 库的名称。
在这里插入图片描述
配置完成之后接着执行 make 编译,编译完成之后执行 make install 安装即可!

安装目录下的文件

进入到 FreeType 安装目录下,如下所示:
在这里插入图片描述
同样有 bin 目录、include 目录以及 lib 目录,大家可以自己进入到这些目录下,浏览下这些目录下的有哪些文件,对此有个印象。
如果要使用 FreeType 库,我们需要在应用程序源码中包含 include/freetype2 目录下的 ft2build.h 头文件,除此之外,还需要包含另一个头文件 FT_FREETYPE_H,这是一个用宏定义的头文件,其实就是include/freetype2/freetype/freetype.h 头文件。所以,在我们的应用程序一般是这样写:

#include <ft2build.h>
#include FT_FREETYPE_H

移植到开发板

接下来将编译得到的动态链接库文件拷贝到开发板 Linux 系统/usr/lib 目录,在拷贝之前,需将/usr/lib 目录下原有的 FreeType 库文件删除掉,执行下面这条命令:

rm -rf /usr/lib/libfreetype.*

删除之后,再将我们编译得到的库文件拷贝到开发板/usr/lib 目录下,也就是 FreeType 安装目录 lib 目录下的所有库文件,拷贝的时候注意符号链接的问题。拷贝完成之后,如下所示:
在这里插入图片描述

23.4 freetype 库的使用

整个移植工作完成之后,接着简单地介绍下 FreeType 库的使用,FreeType 库支持的功能很多、提供给用户的库函数也很多,所以笔者肯定不会给大家细聊!以介绍性为主。
FreeType 官方也提供了详细地使用帮助文档,以下便是这些文档的链接地址:
https://www.freetype.org/freetype2/docs/tutorial/step1.html
https://www.freetype.org/freetype2/docs/tutorial/step2.html
https://www.freetype.org/freetype2/docs/reference/index.html
以下这个链接是一份中文参考文档,大家可以看一下,笔者也不知道是哪位作者编写的,写的非常详细!
https://www.doc88.com/p-7178359224563.html?r=1
在正式介绍 FreeType 库使用之前,需要先了解几个涉及到的概念:

字形(glyph)
字符图像就叫做字形,一个字符能够有多种不同的字形,可以理解为字形就是字符的一种书写风格,譬如宋体的汉字“国”与微软雅黑的汉字“国”,它们的字形是不同的,也就是它们书写风格是不同;宋体的“国”与微软雅黑的“国”就是两种不同的字形。
字形索引
在字体文件中,通过字形索引找到对应的字形,而字形索引是由字符编码转换而来的,譬如 ASCII 编码、GB2312 编码、BIG5 编码、GBK 编码以及国际标准字符集使用的 Unicode 编码等。对于字符编码,如果还有不了解读者,建议自行查阅相关的书籍。
像素点(pixel)、点(point)以及 dpi
像素点大家都知道,譬如 LCD 分辨率为 800480,那就表示 LCD 水平方向有 800 个像素点、垂直方向有 480 个像素点,所以此 LCD 一共有 800480 个像素点。像素点这个概念大家都很熟悉了,也就不再多说;我们再来看下“点”的概念,点(point)是一种简单地物理单位,在数字印刷中,一个点(point)等于 1/72 英寸(1 英寸等于 25.4 毫米)。除此之外,还有一个 dpi 的概念,dpi(dots per inch)表示每英寸的像素点数,譬如 300*400dpi 表示在水平方向,每英寸有 300 个像素点、在垂直方向上每英寸有 400 个像素点。通过点数和 dpi 可以计算出像素点数,公式如下:

像素点数 = 点数 * dpi / 72

譬如,假设某一显示设备水平方向 dpi 为 300,已知水平方向的点数为 50,那么像素点数的计算方式为:
50 * 300 / 72 = 208
所以可以算出像素点数为 208,因为后面会用到这些概念,所以先给大家简单地说明一下。

字形的布局

以下两张图清晰地描述了字形布局的情况:分为水平布局和垂直布局,分别使用图 23.4.1 和图 23.4.2来描述布局情况,以下这两张图都是从官方的文档中截取过来的。

水平方向书写文字使用水平布局方式,绝大部分情况下我们一般都是在水平方向上书写文字;垂直方向书写文字使用垂直布局方式,对于汉字来说,垂直方向书写也是比较常见的,很有代表性的就是对联、还有很多古书文字的写法,也都是采用这种垂直书写。
在这里插入图片描述
在这里插入图片描述
⚫ 基准线、原点
从图中可以看到,不管是水平布局还是垂直布局,图中都可以找到一个 origin 原点,经过原点的水平线(X 轴)和垂直线(Y 轴)称为基准线,笔者将其称为水平基线和垂直基线。
对于水平布局,垂直基线在字形的左边,垂直基线简单地放置在字形上,通过图中所标注的度量数据确定与基线的位置关系。
对于垂直布局,水平基线在字形的上方,字形在垂直基线上居中放置,同样也是通过图中所标注的度量数据确定与基线的位置关系。原点、基准线可以用于定位字形,水平布局和垂直布局使用不同的约束来放置字形。

⚫ 字形的宽度和高度
每一个字形都有自己的宽度和高度,图中使用 width(宽)和 height(高)来表示,width 描述了字形轮廓的最左边到最右边的距离;而 height 描述了字形轮廓的最上边到最下边的距离。同一种书写风格,不同字符所对应的字形,它们的宽高是不一定相等的,譬如大写 A 和小写 a,宽度和高度明显是不同的;但有些字符的字形宽度和高度是相同的,这个与具体的字符有关!

⚫ bearingX 和 bearingY
bearingX 表示从垂直基线到字形轮廓最左边的距离。对于水平布局来说,字形在垂直基线的右侧,所以bearingX 是一个正数;而对于垂直布局来说,字形在垂直基线上居中放置,所以字形轮廓的最左边通常是在垂直基线的左侧,所以 bearingX 是一个负数。bearingY 则表示从水平基线到字形轮廓最上边的距离。对于垂直布局来说,bearingY 是一个正数,字形处于水平基线的下方;而对于水平布局来说,如果字形轮廓的最上边在水平基线的上方,则 bearingY 是一个正数、相反则是一个负数。

⚫ xMin/xMax、yMin/yMax
xMin 表示字形轮廓最左边的位置,xMax 则表示字形轮廓最右边的位置;yMin 表示字形轮廓最下边的位置,yMax 则表示字形轮廓最上边的位置,通过这 4 个位置可以构成一个字形的边界框(bounding box,bbox),当然这是一个假象的框子,它尽可能紧密的装入字形。

⚫ advance
advance 则表示步进宽度,相邻两个原点位置的距离(字间距)。如果是水平布局,则表示相邻的两个原点在水平方向上的距离(advanceX),也就是相邻两条垂直基线之间的距离;同理,如果是垂直布局,则表示相邻的两个原点在垂直方向上的距离(advanceY),也就是相邻两条水平基线之间的距离。

以上所提到的这些参数都很重要,大家一定要理解这些参数所表示的意义,绘制字符时,需要以这些参数作为参考值进行对齐显示。
使用 FreeType 访问字体文件,可以从字体文件中获取到字形的位图数据,位图数据存储在一个 buffer中,buffer 大小为字形的宽高个字节(字形边界框的宽高个字节),也就是图 23.4.1 中 width*height 个字节大小,每一个点使用一个字节来表示,当数组中该点对应的数值等于 0,表示该点不填充颜色;当数值大 于 0,表示该点需要填充颜色。

字符显示时如何对齐?
平时我们使用文本编辑器编写文字的时候,这些字符都是对齐显示的;譬如在一行文本中,即使包含大小写的英文字母、标点符号、汉字等这些字符,这一行字符显示在屏幕上时、都是对齐显示的;这里说的对齐是按照标准规范进行对齐,譬如逗号","显示时是靠近下边的、而不是靠近上边显示;双引号“”显示时是靠近上边的、而不是居中显示;这就是笔者认为的字符显示时的对齐规范,你可以认为每一个字符,它都有对应的一个显示规范,是靠近上边显示呢、还是靠近下边显示亦或者是靠近中间显示呢等。那我们如何保证对齐显示呢?其实就是通过图 23.4.1 中的水平基线和垂直基线,如下图所示:
在这里插入图片描述
不同字符对应的字形,水平基线到字形轮廓最上边的距离都是不一样的,譬如图中水平基线到“A”和“a”轮廓最上边的距离明显是不一样的;除此之外,有些字形的轮廓最下边已经在水平基线之下、而有些字形的轮廓最下边却又在水平基线之上。

分析完水平基线之后,我们再来看看垂直基线,每一个字形的垂直基线到字形轮廓最左边的距离也都是不一样的,譬如“韩”和“a”,很明显、它们各自的垂直基线到字形轮廓最左边的距离是不一样的。

水平基线可以作为垂直方向(上下方向)上对齐显示的基准线,而垂直基线可以作为水平方向(左右方向)上对齐显示的基准线;对于水平布局来说,相邻两条垂直基线的距离就是字间距或者叫步进宽度;从一个字形的原点加上一个步进宽度就到了下一个字形的原点。

当我们要在屏幕上画字形的时候,首先要定位到字形的左上角位置,从左上角开始,依次从左到右、从上到下,字形显示的宽度就是字形的宽度 width、字符显示的高度就是字形的高度 height。那如何找到左上角的位置,这个很简单,通过 bearingY 和 bearingX 便可确定。譬如我们将(100, 100)这个位置作为原点,那么,字符显示位置的左上角便是(100+bearingX, 100-bearingY)。

以上就给大家介绍关于字符对齐的问题,大家一定要理解这些内容,如果你理解不了上面的内容,后面的示例代码你可能就看不懂。

初始化 FreeType 库

在使用 FreeType 库函数之前,需要对 FreeType 库进行初始化操作,使用 FT_Init_FreeType()函数完成初始化操作。在调用该函数之前,我们需要定义一个 FT_Library 类型变量,调用 FT_Init_FreeType()函数时将该变量的指针作为参数传递进去;使用示例如下所示:

FT_Library library;
FT_Error error;
error = FT_Init_FreeType(&library);
if (error)
fprintf(stderr, "Error: failed to initialize FreeType library object\n");

FT_Init_FreeType 完成以下操作:
⚫ 它创建了一个 FreeType 库对象,并将 library 作为库对象的句柄。
⚫ FT_Init_FreeType()调用成功返回 0;失败将返回一个非零值错误码。

加载 face 对象

应用程序通过调用 FT_New_Face()函数创建一个新的 face 对象,其实就是加载字体文件,为啥叫 face(脸),应该是一种抽象的说法!一个 face 对象描述了一个特定的字体样式和风格,譬如"Times New Roman Regular"和"Times New Roman Italic"对应两种不同的 face。
调用 FT_New_Face()函数前,我们需要定义一个 FT_Face 类型变量,使用示例如下所示:

FT_Library library; //库对象的句柄
FT_Face face; //face 对象的句柄
FT_Error error;
FT_Init_FreeType(&library);
error = FT_New_Face(library, "/usr/share/fonts/font.ttf", 0, &face);
if (error) {
/* 发生错误、进行相关处理 */
}

}
FT_New_Face()函数原型如下所示:

FT_Error FT_New_Face(FT_Library library, const char *filepathname,
FT_Long face_index, FT_Face *aface);

函数参数以及返回值说明如下:
library:一个 FreeType 库对象的句柄,face 对象从中建立;
filepathname:字库文件路径名(一个标准的 C 字符串);
face_index:某些字体格式允许把几个字体 face 嵌入到同一个文件中,这个索引指示了你想加载的 face,
其实就是一个下标,如果这个值太大,函数将会返回一个错误,通常把它设置为 0 即可!想要知道一个字体
文件中包含了多少个 face,只要简单地加载它的第一个 face(把 face_index 设置为 0),函数调用成功返回
后,face->num_faces 的值就指示出了有多少个 face 嵌入在该字体文件中。
aface:一个指向新建 face 对象的指针,当失败时其值被设置为 NULL。
返回值:调用成功返回 0;失败将返回一个非零值的错误码。

设置字体大小

设置字体的大小有两种方式:FT_Set_Char_Size()和 FT_Set_Pixel_Sizes()。
FT_Set_Pixel_Sizes()函数
调用 FT_Set_Pixel_Sizes()函数设置字体的宽度和高度,以像素为单位,使用示例如下所示:

FT_Set_Pixel_Sizes(face, 50, 50);

第一个参数传入 face 句柄;第二个参数和第三个参数分别指示字体的宽度和高度,以像素为单位;需
要注意的是,我们可以将宽度或高度中的任意一个参数设置为 0,那么意味着设置为 0 的参数将会与另一个参数保持相等,如下所示:

FT_Set_Pixel_Sizes(face, 50, 0);

上面调用 FT_Set_Pixel_Sizes()函数时,将字体高度设置为 0,也就意味着字体高度将自动等于字体宽度50。

FT_Set_Char_Size()函数
调用 FT_Set_Char_Size()函数设置字体大小,示例如下所示,假设在一个 300x300dpi 的设备上把字体大小设置为 16pt:

error = FT_Set_Char_Size(
face,//face 对象的句柄
16*64, //以 1/64 点为单位的字体宽度
16*64, //以 1/64 点为单位的字体高度
300, //水平方向上每英寸的像素点数
300); //垂直方向上每英寸的像素点数

说明:
⚫ 字体的宽度和高度并不是以像素为单位,而是以 1/64 点(point)为单位表示(也就是 26.6 固定浮点格式),一个点是一个 1/72 英寸的距离。
⚫ 同样也可将宽度或高度其中之一设置为 0,那么意味着设置为 0 的参数将会与另一个参数保持相等。
⚫ dpi 参数设置为 0 时,表示使用默认值 72dpi。

加载字形图像

设置完成之后,接下来就可以加载字符图像了,总共分为 3 步:
a)、获取字符的字形索引
通过 FT_Get_Char_Inde()函数将字符编码转换为字形索引(glyph index),Freetype 默认使用 UTF-16 编码类型,也就是 Unicode 编码方式,采用 2 个字节来表示一个编码值。
XXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXX
XXXXXXXXXXXXXXXXXXXX

23.5 示例代码

显示单个 ASCII 字符
显示单个中文字符
显示一行字符(一行字符对齐显示)
显示多行字符(一行字符的高度计算)
字体变形(旋转、斜体………)

示例代码 23.5.1 FreeType 使用示例代码
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <string.h>
#include <errno.h>
#include <sys/mman.h>
#include <linux/fb.h>
#include <math.h> //数学库函数头文件
#include <wchar.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#define FB_DEV "/dev/fb0" //LCD 设备节点
#define argb8888_to_rgb565(color) ({ \unsigned int temp = (color); \((temp & 0xF80000UL) >> 8) | \((temp & 0xFC00UL) >> 5) | \((temp & 0xF8UL) >> 3); \})
static unsigned int width; //LCD 宽度
static unsigned int height; //LCD 高度
static unsigned short *screen_base = NULL;//LCD 显存基地址 RGB565
static unsigned long screen_size;
static int fd = -1;
static FT_Library library;
static FT_Face face;
static int fb_dev_init(void)
{struct fb_var_screeninfo fb_var = {0};struct fb_fix_screeninfo fb_fix = {0};/* 打开 framebuffer 设备 */fd = open(FB_DEV, O_RDWR);if (0 > fd) {fprintf(stderr, "open error: %s: %s\n", FB_DEV, strerror(errno));return -1;}/* 获取 framebuffer 设备信息 */ioctl(fd, FBIOGET_VSCREENINFO, &fb_var);ioctl(fd, FBIOGET_FSCREENINFO, &fb_fix);screen_size = fb_fix.line_length * fb_var.yres;width = fb_var.xres;height = fb_var.yres;/* 内存映射 */screen_base = mmap(NULL, screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if (MAP_FAILED == (void *)screen_base) {perror("mmap error");close(fd);return -1;}/* LCD 背景刷成黑色 */memset(screen_base, 0xFF, screen_size);return 0; }
static int freetype_init(const char *font, int angle) {FT_Error error;FT_Vector pen;FT_Matrix matrix;float rad; //旋转角度/* FreeType 初始化 */FT_Init_FreeType(&library);/* 加载 face 对象 */error = FT_New_Face(library, font, 0, &face);if (error) {fprintf(stderr, "FT_New_Face error: %d\n", error);exit(EXIT_FAILURE);}/* 原点坐标 */pen.x = 0 * 64;pen.y = 0 * 64; //原点设置为(0, 0)/* 2x2 矩阵初始化 */rad = (1.0 * angle / 180) * M_PI; //(角度转换为弧度)M_PI 是圆周率
#if 0 //非水平方向matrix.xx = (FT_Fixed)( cos(rad) * 0x10000L);matrix.xy = (FT_Fixed)(-sin(rad) * 0x10000L);matrix.yx = (FT_Fixed)( sin(rad) * 0x10000L);matrix.yy = (FT_Fixed)( cos(rad) * 0x10000L);
#endif
#if 1 //斜体 水平方向显示的matrix.xx = (FT_Fixed)( cos(rad) * 0x10000L);matrix.xy = (FT_Fixed)( sin(rad) * 0x10000L);matrix.yx = (FT_Fixed)( 0 * 0x10000L);matrix.yy = (FT_Fixed)( 1 * 0x10000L);
#endif/* 设置 */FT_Set_Transform(face, &matrix, &pen);FT_Set_Pixel_Sizes(face, 50, 0); //设置字体大小return 0; }
static void lcd_draw_character(int x, int y,const wchar_t *str, unsigned int color) {unsigned short rgb565_color = argb8888_to_rgb565(color);//得到 RGB565 颜色值FT_GlyphSlot slot = face->glyph;size_t len = wcslen(str); //计算字符的个数long int temp;int n;int i, j, p, q;int max_x, max_y, start_y, start_x;// 循环加载各个字符for (n = 0; n < len; n++) {// 加载字形、转换得到位图数据if (FT_Load_Char(face, str[n], FT_LOAD_RENDER))continue;start_y = y - slot->bitmap_top; //计算字形轮廓上边 y 坐标起点位置 注意是减去 bitmap_topif (0 > start_y) {//如果为负数 如何处理??q = -start_y;temp = 0;j = 0;}else { // 正数又该如何处理??q = 0;temp = width * start_y;j = start_y;}max_y = start_y + slot->bitmap.rows;//计算字形轮廓下边 y 坐标结束位置if (max_y > (int)height)max_y = height;for (; j < max_y; j++, q++, temp += width) {start_x = x + slot->bitmap_left; //起点位置要加上左边空余部分长度if (0 > start_x) {p = -start_x;i = 0;}else {p = 0;i = start_x;}max_x = start_x + slot->bitmap.width;if (max_x > (int)width)max_x = width;for (; i < max_x; i++, p++) {// 如果数据不为 0,则表示需要填充颜色if (slot->bitmap.buffer[q * slot->bitmap.width + p])screen_base[temp + i] = rgb565_color;}}//调整到下一个字形的原点x += slot->advance.x / 64; //26.6 固定浮点格式y -= slot->advance.y / 64;} }
int main(int argc, char *argv[])
{/* LCD 初始化 */if (fb_dev_init())exit(EXIT_FAILURE);/* freetype 初始化 */if (freetype_init(argv[1], atoi(argv[2])))exit(EXIT_FAILURE);/* 在 LCD 上显示中文 */int y = height * 0.25;lcd_draw_character(50, 100, L"路漫漫其修远兮,吾将上下而求索", 0x000000);lcd_draw_character(50, y+100, L"莫愁前路无知己,天下谁人不识君", 0x9900FF);lcd_draw_character(50, 2*y+100, L"君不见黄河之水天上来,奔流到海不复回", 0xFF0099);lcd_draw_character(50, 3*y+100, L"君不见高堂明镜悲白发,朝如青丝暮成雪", 0x9932CC);/* 退出程序 */FT_Done_Face(face);FT_Done_FreeType(library);munmap(screen_base, screen_size);close(fd);exit(EXIT_SUCCESS);
}

编译方法:

${CC} -o testApp testApp.c -I/home/dt/tools/freetype/include/freetype2 -L/home/dt/tools/freetype/lib -
lfreetype -L/home/dt/tools/zlib/lib -lz -L/home/dt/tools/png/lib -lpng -lm

效果图:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/10831.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

FPGA第2篇,FPGA与CPU GPU APU DSP NPU TPU 之间的关系与区别

简介&#xff1a;首先&#xff0c;FPGA与CPU GPU APU NPU TPU DSP这些不同类型的处理器&#xff0c;可以被统称为"处理器"或者"加速器"。它们在计算机硬件系统中承担着核心的计算和处理任务&#xff0c;可以说是系统的"大脑"和"加速引擎&qu…

需求规格说明书设计规范(编制实际项目案例-word)

二、 项目概述 2.1 项目背景 2.2 现状分析 2.2.1 业务现状 2.2.2 系统现状 三、 总体需求 3.1 系统范围 3.2 系统功能 3.3 用户分析 3.4 假设与依赖关系 四、 功能需求 五、 非功能性需求 5.1 用户界面需求 5.2 软硬件环境需求 5.3 产品质量需求 5.4 接口需求 5.5 其他需求 六、…

Django国际化与本地化指南

title: Django国际化与本地化指南 date: 2024/5/12 16:51:04 updated: 2024/5/12 16:51:04 categories: 后端开发 tags: Django-i18n本地化-L10n多语言国际化翻译工具表单验证性能优化 引言 在数字化时代&#xff0c;网站和应用程序必须跨越地域限制&#xff0c;服务于全球…

Qt---事件

一、Qt中的事件 鼠标事件 鼠标进入事件enterEvent 鼠标离开事件leaveEvent 鼠标按下mousePressEvent ( QMouseEvent ev) 鼠标释放mouseReleaseEvent 鼠标移动mouseMoveEvent ev->x()&#xff1a;坐标 ev->y()&#xff1a;y坐标 ev->bu…

【JS红宝书学习笔记】第3章 语言基础

第3章 语言基础 1. 语法 标识符&#xff08;变量、函数、属性或函数参数的名称&#xff09;&#xff1a;一般使用驼峰法命名&#xff0c;关键字、保留字、true、false 和 null 不能作为标识符。 标识符的第一个字符必须是一个字母、下划线&#xff08;_&#xff09;或美元符号…

docker 部署SSM项目(包含打包)

一&#xff1a;SSM项目打包 1.这个一定要勾选防止静态资源没打包上 2.第二步 3.第三步 4.更改名字(注意部署到线上的时候这里如果用docker或者window部署的话需要带这个项目名&#xff0c;不然会出现找不到接口的情况) ![在这里插入图片描述](https://img-blog.csdnimg.cn/dir…

MCU通过UART/SPI等接口更新flash的方法

MCU可提供一种方便的方式来更新flash内容以进行错误修复bugfix或产品更新update。可以使用以下任何模式更新flash内容: •系统内编程(ISP,In-System Programming):用于使用内部bootloader程序和UART/SPI对片上闪存进行编程program或重新编程reprogram。 •应用程序内编程…

comfyui安装deforum启动不了,多半是ffmpeg的问题

如果报错中出现imageio 和 ffmpeg 的字样&#xff0c;去装requirement也没啥用 这里最好到cmd中&#xff0c;进入comfyui的python环境&#xff0c;运行以下两句&#xff1a; import imageio imageio.plugins.ffmpeg.download() 图例&#xff1a; 如果节点不报错了&#xff0…

WHAT - CSS Animationtion 动画系列(四)- 移动端全屏动画

目录 一、背景1.1 GIF & Video1.2 存在的问题 二、技术方案2.1 使用CSS动画和JavaScript2.2 使用JavaScript库2.3 使用序列帧1. css animation 帧动画2. JavaScript requestAnimationFrame 帧动画 2.4 使用Canvas1. html 和 canvas 中的 video2. 基于Canvas的动画库 今天我…

【Linux】动态库与静态库的底层比较

送给大家一句话&#xff1a; 人生最遗憾的&#xff0c;莫过于&#xff0c;轻易地放弃了不该放弃的&#xff0c;固执地坚持了不该坚持的。 – 柏拉图 (x(x_(x_x(O_o)x_x)_x)x) (x(x_(x_x(O_o)x_x)_x)x) (x(x_(x_x(O_o)x_x)_x)x) 底层比较 1 前言2 编译使用比较2 如何加载Than…

java项目之企业OA管理系统源码(springboot+vue+mysql)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的企业OA管理系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 企业OA管理系统的主要使用…

Nios-II编程入门实验

文章目录 一 Verilog实现流水灯二 Nios实现流水灯2.1 创建项目2.2 SOPC添加模块2.3 SOPC输入输出连接2.4 Generate2.5 软件部分2.6 运行结果 三 Verilog实现串口3.1 代码3.2 引脚3.3 效果 四 Nios2实现串口4.1 sopc硬件设计4.2 top文件4.3 软件代码4.4 实现效果 五 参考资料六 …

Vue3 查看真实请求地址

上回说到Vue2查看真实请求地址&#xff0c;那么Vue3该如何查看呢&#xff1f; 传送门&#xff1a; Vue2 查看真实请求地址 1. bypass函数 使用bypass函数获取代理结果&#xff0c;设置响应头&#xff08;请求头设置未生效&#xff0c;也可以在响应头上看&#xff09;。 2. …

软件全套资料梳理(需求、开发、实施、运维、安全、测试、交付、认证、评审、投标等)

软件全套精华资料包清单部分文件列表&#xff1a; 工作安排任务书&#xff0c;可行性分析报告&#xff0c;立项申请审批表&#xff0c;产品需求规格说明书&#xff0c;需求调研计划&#xff0c;用户需求调查单&#xff0c;用户需求说明书&#xff0c;概要设计说明书&#xff0c…

BGP学习一:关于对等体建立和状态组改变

目录 一.BGP基本概念 &#xff08;1&#xff09;.BGP即是协议也是分类 1.早期EGP 2.BGP满足不同需求 3.BGP区域间传输的优势 &#xff08;1&#xff09;安全性——只传递路由信息 &#xff08;2&#xff09;跨网段建立邻居 4.BGP总结 5.BGP的应用 &#xff08;1&#…

Coursera吴恩达深度学习专项课程01: Neural Networks and Deep Learning 学习笔记 Week 02

Week 02 of Neural Networks and Deep Learning Course Certificate 本文是学习 https://www.coursera.org/learn/neural-networks-deep-learning 这门课的笔记 Course Intro 文章目录 Week 02 of Neural Networks and Deep LearningLearning Objectives [1] Logistic Regres…

YOLOv8_seg预测流程-原理解析[实例分割理论篇]

YOLOv8_seg的网络结构图在博客YOLOv8网络结构介绍已经更新了,由网络结构图可以看到相对于目标检测网络,实例分割网络只是在Head层不相同,如下图所示,在每个特征层中增加了Mask层(浅紫色),这和同一层的Box,cls的shape大小一样;另外还利用8080尺度的特征图,经过卷积+上…

如何快速的追加文章的内容(在不知道内容的情况下)

首先&#xff0c;需要用到的这个工具&#xff1a; 度娘网盘 提取码&#xff1a;qwu2 蓝奏云 提取码&#xff1a;2r1z 1、打开工具&#xff0c;切换到文章模块下&#xff0c;快捷键&#xff1a;Ctrl1 2、新建一个文章做演示&#xff0c;001 3、添加一个内容&#xff0c;就随便…

冯喜运:5.12黄金回撤继续上涨,下周原油走势分析

【黄金消息面分析】&#xff1a;本周&#xff0c;黄金市场迎来了自4月中旬以来的最佳单周表现。周五&#xff08;3月9日&#xff09;&#xff0c;金价攀升至2360.54美元/盎司&#xff0c;涨幅0.62%&#xff0c;而纽约商品交易所6月交割的黄金期货价格上涨1.5%&#xff0c;收报2…

bevformer详解(1):论文介绍

3D 视觉感知任务,包括基于多摄像头的3D检测和地图分割对于自动驾驶系统至关重要。本文提出了一种名为BEVFormer的新框架,它通过使用空间和时间的Transformer 学习统一的BEV表示来支持多个自动驾驶感知任务。简而言之,BEVFormer通过预定义的网格形式的Bev Query与空间和时间空…