目录
前言
一、LCD操作原理
(1)LCD和Framebuffer。
(2)LCD的操作:
(3)核心函数(后续也会经常用到)
①open函数
②ioctl函数
③mmap函数
二、字符的点阵显示
(1)字符的编码方式
①字符的标准
②UNICODE的编码实现
(2)字符的点阵显示
①ASCII字符的点阵显示
②中文字符的点阵显示
(3)点阵显示的缺点
三、Freetype的手工交叉编译
(1)基础知识以及解决方法
①交叉编译程序的万能命令
(2)imx6ull交叉编译freetype
①确定头文件、库文件在工具链中的位置
②交叉编译、安装libpng
③交叉编译、安装freetype
四、使用Freetype显示单个文字
(1)实现流程步骤
①一个文字的显示过程:
②具体的函数实现过程:
(2)具体实现
①描点函数(lcd_put_pixel)
②显示位图函数(draw_bitmap)
③主函数(main)
(3)实现效果
五、使用Freetype显示多行文字(支持倾斜角度)
(1)方法原理
①字符大小示意
②在指定位置显示一行文字
(2)具体实现
①具体的实现流程
②核心函数
(3)实现效果
前言
在一定了解LCD显示文字的原理后,实现多行文字的显示,这个过程中,熟悉和认知Linux系统下的库编译和常用函数。
1.LCD的显示,是基于Framebuffer来实现的,主体使用open、ioctl以及mmap来实现。
2.相较于字符点阵显示,使用矢量字体可以很好实现字体不同尺寸的显示。
3.一行文字里字符大小不一,采用统一大小显示,效果是不好的。基于笛卡尔坐标和字符大小外框结构,定义字符外框和调整到下一个字符的原点,使用freetype可以实现一行文字的显示。
一、LCD操作原理
(1)LCD和Framebuffer。
在Linux中用Framebuffer驱动程序来控制LCD。
- 这里Framebuffer是一块内存,里面保存着一帧图像。
 - 以现用的LCD屏(1024*768)为例,每一个像素的颜色用32为表示,Framebuffer的大小:→1024*768*32/8=3MB
 - 对Framebuffer写入,也会相应的映射到LCD屏幕上。
 
(2)LCD的操作:
- 驱动程序设置好LCD程序, 
  
- 根据LCD的参数设置LCD控制器的时序、信号极性;
 - 根据LCD分辨率、BPP分配Framebuffer。
 
 - APP使用ioctl函数来获得LCD分辨率、BPP
 - APP通过mmap映射Framebuffer,在Framebuffer中写入数据。
 

(3)核心函数(后续也会经常用到)
①open函数
open函数用于打开相应的设备节点,这里就是打开LCD设备节点。
//头文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//函数原型
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
//核心参数
pathname:文件的路径
flags:表示打开文件的方式
mode:表示创建文件的权限,一般不起效,当flags为O_CREAT时有效
返回值:打开成功返回文件描述符,失败则返回-1
 
②ioctl函数
ioctl函数一般用于跟驱动程序交互,读出和传递一些参数(例如分辨率、BPP)。
//头文件
#include <sys/ioctl.h>
//函数原型
int ioctl(int fd, unsigned long request, ...);
//核心参数
fd:文件描述符---接open返回值
request:表示与驱动程序交互的命令,用不同的命令控制驱动程序输出我们需要的数据。
返回值:打开成功返回文件描述符,失败返回-1.
 
③mmap函数
mmap函数用于将设备文件映射到内存,构建联系。
//头文件
#include <sys/mman.h>
//函数原型
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
//核心参数
addr:表示指定映射的内存起始地址。设为NULL表示系统自动选定地址,在成功映射后返回该地址。
length:表示将文件中多大的内容映射到内存中。
prot:表示映射区域的保护方式
    PROT_EXEC 映射区域可被执行
    PROT_READ 映射区域可被读出
    PROT_WRITE 映射区域可被写入
    PROT_NONE 映射区域不能存取
flags:表示影响映射区域的不同特性,通常有两种:
    MAP_SHARED 表示对映射区域写入的数据会复制回文件内,原来的文件会改变。
    MAP_PRIVATE 表示对映射区域的操作会产生一个映射文件的复制,对此区域的任何修改都不会写回原来的文件内容中。
返回值:若成功映射,将返回指向映射的区域的指针,失败将返回-1。 
二、字符的点阵显示
字符的核心---它的编码值,字符对应什么样的形状,这是由字符文件决定的。
两个分析软件:Hex Editor Neno以及Notepad。使用Hex Editor Neno可以详细分析输入字符具体的编码值,用Notepad可以分析不同编码方式之间的区别联系。
(1)字符的编码方式
①字符的标准
- 字符的标准有ASCII、ANSI(ASCII的拓展)以及UNICODE 
  
-  
    “ American Standard Code for Information Interchange”,美国信息交换标准代码
 - ANSI是不固定的,通常“本地化”密切相关,国内大陆地区就是采用的GB2312,港澳台地区则是BIG5。
 - UNICODE是为了解决不同编码标准中一个数值对应不同字符问题,在地球上任意一个字符,都只有一个唯一的数值。
 
 -  
    
 
②UNICODE的编码实现
- UNICODE的编码实现---当遇到不同的编码,如何正确实现数值表示。 
  
- 第一种方法:用3个字节表示一个UNICODE,很省事但是非常浪费空间。
 - 第二种方法:UTF-16 LE;只用两个字节,小字节序,数值中权重低的字节放在前面。UTF-16 BE;大字节序,数值中权重低的字节放在后面。全世界常用的字符都可以表示。 
    
- 但是缺点也很明显:①表示的字符数量有限②对于 ASCII 字符有空间浪费③如果文件中有某个字节丢失,这会使得后面所有字符都因为错位而无法显示
 
 - 第三种方法:UTF8可解决上面的问题;具体原理图如下:
 
 

(2)字符的点阵显示
通过对每一个像素的点亮熄灭,来实现字符的显示,常见的是对ASCII字符和中文字符的点阵显示。
①ASCII字符的点阵显示
- 在 Linux 内核源码中有这个文件:lib\fonts\font_8x16.c, 里面以数组形式保存各个字符的点阵。
 - 要显示某个字符时,根据它的 ASCII 码在 fontdata_8x16 数组中找到它的点阵,然后取出这 16 个字节去描画 16 行像素。
 

②中文字符的点阵显示
- 使用HZK16字库,它是常用汉字的16*16点阵字库。HZK16里每个汉字使用32字节来描述。
 -  
  HZK16 中是以 GB2312 编码值来查找点阵的。所以要注意将文件编码格式转换成GB2312。具体如下:
 

(3)点阵显示的缺点
- 使用点阵字库显示英文字母、汉字时,大小是固定的。
 - 如果进行文字的放大缩小则会模糊甚至有锯齿出现。
 
三、Freetype的手工交叉编译
针对点阵显示的缺点,这里引用矢量字体解决。
Freetype 是开源的字体引擎库,它提供统一的接口来访问多种字体格式文件,从而实现矢量字体显示。
(1)基础知识以及解决方法
①交叉编译程序的万能命令
//安装的库有configure命令时可采用,将库文件都安装到当前目录tmp文件夹里./configure --host=arm-buildroot-linux-gnueabihf --prefix=$PWD/tmpmakemake install
(2)imx6ull交叉编译freetype
这里freetype库依赖于libpng,libpng 又依赖于 zlib,所以我们应该:先编译安装 zlib,再编译安装 libpng,最后编译安装 freetype。对于imx6ull不用编译安装zlib,在其工具链中有zlib。
①确定头文件、库文件在工具链中的位置
//设置交叉编译工具链export ARCH=armexport CROSS_COMPILE=arm-buildroot-linux-gnueabihf-export PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueab ihf_sdk-buildroot/bin//编译main{}时,列出系统目录echo 'main(){}'| arm-buildroot-linux-gnueabihf-gcc -E -v -//确定头文件目录/home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../lib/gcc/arm-buildroot-linux-gnueabihf/7.5.0/include//确定库文件目录/home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/ bin/../lib/gcc/arm-buildroot-linux-gnueabihf/7.5.0/../../../../arm-buildroot-linux-gnueabihf/lib
头文件路径:

编译路径、库路径:
②交叉编译、安装libpng
//复制文件到当前目录cp /home/book/01_all_series_quickstart/04_嵌入式Linux应用开发基础知识/source/10_freetype/libpng-1.6.37.tar.xz .///解压
tar xJf libpng-1.6.37.tar.xz//交叉编译程序命令
cd libpng-1.6.37./configure --host=arm-buildroot-linux-gnueabihf --prefix=$PWD/tmpmakemake install//移动到tmp
cd tmp//将include以及lib文件拷贝到之前指定的系统头文件和库文件目录下
cp include/* -rf /home/book/100ask_imx6ull-sdk/To olChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../lib/gcc/arm-buildroot-lin ux-gnueabihf/7.5.0/includecp lib/* -rfd /home/book/100ask_imx6ull-sdk/Tool Chain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../lib/gcc/arm-buildroot-linux -gnueabihf/7.5.0/../../../../arm-buildroot-linux-gnueabihf/lib
③交叉编译、安装freetype
//复制文件到当前目录cp /home/book/01_all_series_quickstart/04_嵌入式Linux应用开发基础知识/source/10_freetype/freetype-2.10.2.tar.xz .///解压
tar xJf freetype-2.10.2.tar.xz//交叉编译程序命令
cd freetype-2.10.2
./configure --host=arm-buildroot-linux-gnueabihf --prefix=$PWD/tmpmakemake install//移动到tmp
cd tmp//将include以及lib文件拷贝到之前指定的系统头文件和库文件目录下
cp include/* -rf /home/book/100ask_imx6ull-sdk/To olChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../lib/gcc/arm-buildroot-lin ux-gnueabihf/7.5.0/includecp lib/* -rfd /home/book/100ask_imx6ull-sdk/Tool Chain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../lib/gcc/arm-buildroot-linux -gnueabihf/7.5.0/../../../../arm-buildroot-linux-gnueabihf/lib
四、使用Freetype显示单个文字
我们移植Freetype字体引擎,调用对应的 API 接口,提供字体文件,就可以让 freetype 库帮我们取出关键点、实现闭合曲线,填充颜色,达到显示矢量字体的目的。
(1)实现流程步骤
①一个文字的显示过程:
- 给定一个字符可以确定它的编码值(ASCII、UNICODE、GB2312)
 - 设置字体大小
 -  
  根据编码值,从文件头部中通过 charmap( 字符映射表 ) 找到对应的关键点 (glyph) ,它会根据字体大小调整关键点;
 -  
  把关键点转换为位图点阵
 -  
  在LCD上显示出来
 
②具体的函数实现过程:
-  
  初始化: 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
 -  
  最后显示出来
 
(2)具体实现
①描点函数(lcd_put_pixel)
//输入参数: x坐标,y坐标,颜色
void lcd_put_pixel(int x, int y, unsigned int color)
{
	unsigned char *pen_8 = fbmem+y*line_width+x*pixel_width;
	unsigned short *pen_16;	
	unsigned int *pen_32;	
	unsigned int red, green, blue;	
	pen_16 = (unsigned short *)pen_8;
	pen_32 = (unsigned int *)pen_8;
	switch (var.bits_per_pixel)
	{
		case 8:
		{
			*pen_8 = color;
			break;
		}
		case 16:
		{
			/* 565 */
			red   = (color >> 16) & 0xff;
			green = (color >> 8) & 0xff;
			blue  = (color >> 0) & 0xff;
			color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
			*pen_16 = color;
			break;
		}
		case 32:
		{
			*pen_32 = color;
			break;
		}
		default:
		{
			printf("can't surport %dbpp\n", var.bits_per_pixel);
			break;
		}
	} 
 ②显示位图函数(draw_bitmap)
根据bitmap位图,调用lcd_put_pixel描点函数在LCD指定位置显示汉字。
//输入参数: x坐标,y坐标,位图指针
void draw_bitmap( FT_Bitmap*  bitmap,
             FT_Int      x,
             FT_Int      y)
{
	FT_Int  i, j, p, q;
	FT_Int  x_max = x + bitmap->width;
	FT_Int  y_max = y + bitmap->rows;
	//printf("x = %d, y = %d\n", x, y);
	for ( j = y, q = 0; j < y_max; j++, q++ )
	{
		for ( i = x, p = 0; i < x_max; i++, p++ )
		{
			if ( i < 0      || j < 0       ||
				i >= var.xres || j >= var.yres )
			continue;
			//image[j][i] |= bitmap->buffer[q * bitmap->width + p];
			lcd_put_pixel(i, j, bitmap->buffer[q * bitmap->width + p]);
		}
	}
} 
 ③主函数(main)
具体的实现流程:
- 第一步:打开LCD的设备节点以及获取LCD参数
 - 第二步:映射framebuffer
 - 第三步:使用 wchar_t 获得字符的 UNICODE 值
 - 第四步:屏幕、字体库初始化,构建字体文件对象
 - 第五步:使用freetype获得位图(可设置倾斜角度)
 - 最后:调用draw_bitmap函数显示位图
 
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <wchar.h>
#include <sys/ioctl.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
int fd_fb;
struct fb_var_screeninfo var;	/* Current var */
int screen_size;
unsigned char *fbmem;
unsigned int line_width;
unsigned int pixel_width;
int main(int argc, char **argv)
{
    FT_Library	  library;
	FT_Face 	  face;
	int error;
    FT_Vector     pen;
	FT_GlyphSlot  slot;
    int font_size = 24;
	FT_Matrix	  matrix;				  /* transformation matrix */
	double		  angle;
    //第一步:打开LCD的设备节点以及获取LCD参数
	if (argc < 2)
	{
		printf("Usage : %s <font_file> [font_size]\n", argv[0]);
		return -1;
	}
	if (argc == 3)
		font_size = strtoul(argv[2], NULL, 0);
	
	angle  = ( 1.0* strtoul(argv[2], NULL, 0) / 360 ) * 3.14159 * 2;	   /* use 25 degrees	 */
	if (argc == 4)
		font_size = strtoul(argv[3], NULL, 0);
		
	fd_fb = open("/dev/fb0", O_RDWR);
	if (fd_fb < 0)
	{
		printf("can't open /dev/fb0\n");
		return -1;
	}
	if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
	{
		printf("can't get var\n");
		return -1;
	}
    //第二步:映射framebuffer
    line_width  = var.xres * var.bits_per_pixel / 8;
	pixel_width = var.bits_per_pixel / 8;
	screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
	fbmem = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
	if (fbmem == (unsigned char *)-1)
	{
		printf("can't mmap\n");
		return -1;
	}
    //第三步:使用 wchar_t 获得字符的 UNICODE 值
	wchar_t *chinese_str = L"希";
    //第四步:屏幕、字体库初始化,构建字体文件对象
	/* 清屏: 全部设为黑色 */
	memset(fbmem, 0, screen_size);
	
	/* 显示矢量字体 */
	error = FT_Init_FreeType( &library );			   /* initialize library */
	/* error handling omitted */
	
	error = FT_New_Face( library, argv[1], 0, &face ); /* create face object */
	/* error handling omitted */	
	slot = face->glyph;
	FT_Set_Pixel_Sizes(face, font_size, 0);
    //第五步:使用freetype得到位图
	/* 确定座标:
	 */
	pen.x = 0;
	pen.y = 0;
	/* set up matrix */
	matrix.xx = (FT_Fixed)( cos( angle ) * 0x10000L );
	matrix.xy = (FT_Fixed)(-sin( angle ) * 0x10000L );
	matrix.yx = (FT_Fixed)( sin( angle ) * 0x10000L );
	matrix.yy = (FT_Fixed)( cos( angle ) * 0x10000L );
	
    /* set transformation */
    FT_Set_Transform( face, &matrix, &pen);
    /* load glyph image into the slot (erase previous one) */
    error = FT_Load_Char( face, chinese_str[0], FT_LOAD_RENDER );
	if (error)
	{
		printf("FT_Load_Char error\n");
		return -1;
	}
    
	//最后一步:根据位图汉字显示
    draw_bitmap( &slot->bitmap,
                 var.xres/2,
                 var.yres/2);
	return 0;	
} 
 (3)实现效果
交叉编译后,将文件复制到板子上运行,实现文字的LCD显示。
//交叉编译.c文件时,会报错,先执行以下的操作
//ft2build.h位于freetype2目录里,与工具链头文件不对应,需要移动到上级目录
cd /home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/arm-buildroot-linux-gnueabihf/sysroot/usr/include
mv freetype2/* ./
//编译并将文件复制到NFS挂载的目录下
arm-buildroot-linux-gnueabihf-gcc -o freetype_show_font freetype_show_font.c -lfreetype -lm
cp freetype_show_font simsun.ttc ~/nfs_rootfs
//上机测试
./freetype_show_font ./simsun.ttc 45 200

五、使用Freetype显示多行文字(支持倾斜角度)
只用上面的函数实现一行文字的显示,会出现一些问题,如每个字符的大小可能不同,如果设置的字体大小都一样,显示效果不好。针对这个问题,freetype库里有相应的解决方法。
在深入了解韦老师的示例代码后,进一步实现支持倾斜角度显示一行文字。这个过程中把代码思路进一步规整整理。
(1)方法原理
①字符大小示意
对于一个字符大小的定义如下图所示,具体需要考虑xMin、xMax、yMin以及yMax。这些参数我们可以通过FT_Glyph_Get_CBox函数获得。

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

具体步骤为:
- 先指定第 1 个字符的原点 pen 坐标为(0, 0),计算出它的外框
 - 再计算右边字符的原点,也计算出它的外框
 -  
   在 (x, y) 处显示这行文字时,调整一下 pen 坐标。
 
(2)具体实现
①具体的实现流程
- 第一步:打开LCD的设备节点以及获取LCD参数
 - 第二步:获得设置参数x_label、y_label、字体大小以及倾斜角度
 - 第三步:映射framebuffer
 - 第四步:使用 wchar_t 获得字符串的 UNICODE 值
 - 第五步:屏幕、字体库初始化,构建字体文件对象
 - 第六步:绘制一行字符串(计算外框以及调整原点)
 
②核心函数
在freetype显示单个字符里描点函数(lcd_put_pixel)以及显示位图函数(draw_bitmap)基础上。
a.计算一行文字的外框(compute_string_bbox)
int compute_string_bbox(FT_Face       face, wchar_t *wstr, FT_BBox  *abbox)
{
    int i;
    int error;
    FT_BBox bbox;
    FT_BBox glyph_bbox;
    FT_Vector pen;
    FT_Glyph  glyph;
    FT_GlyphSlot slot = face->glyph;
    /* 初始化 */
    bbox.xMin = bbox.yMin = 32000;
    bbox.xMax = bbox.yMax = -32000;
    /* 指定原点为(0, 0) */
    pen.x = 0;
    pen.y = 0;
    /* 计算每个字符的bounding box */
    /* 先translate, 再load char, 就可以得到它的外框了 */
    for (i = 0; i < wcslen(wstr); i++)
    {
        /* 转换:transformation */
        FT_Set_Transform(face, 0, &pen);
        /* 加载位图: load glyph image into the slot (erase previous one) */
        error = FT_Load_Char(face, wstr[i], FT_LOAD_RENDER);
        if (error)
        {
            printf("FT_Load_Char error\n");
            return -1;
        }
        /* 取出glyph */
        error = FT_Get_Glyph(face->glyph, &glyph);
        if (error)
        {
            printf("FT_Get_Glyph error!\n");
            return -1;
        }
        
        /* 从glyph得到外框: bbox */
        FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_TRUNCATE, &glyph_bbox);
        /* 更新外框 */
        if ( glyph_bbox.xMin < bbox.xMin )
            bbox.xMin = glyph_bbox.xMin;
        if ( glyph_bbox.yMin < bbox.yMin )
            bbox.yMin = glyph_bbox.yMin;
        if ( glyph_bbox.xMax > bbox.xMax )
            bbox.xMax = glyph_bbox.xMax;
        if ( glyph_bbox.yMax > bbox.yMax )
            bbox.yMax = glyph_bbox.yMax;
        
        /* 计算下一个字符的原点: increment pen position */
        pen.x += slot->advance.x;
        pen.y += slot->advance.y;
    }
    /* return string bbox */
    *abbox = bbox;
}
 
b.调整原点并绘制 (支持倾斜角度)
int display_string(FT_Face     face, wchar_t *wstr, int lcd_x, int lcd_y, double angle)
{
    int i;
    int error;
    FT_BBox bbox;
    FT_Vector pen;							/*字符原点*/
    FT_Glyph  glyph;						/*对应字符的处理结果,含glyph和位图*/
	FT_Matrix	  matrix;				  	/* transformation matrix */
    FT_GlyphSlot slot = face->glyph;
    /* 把LCD坐标转换为笛卡尔坐标 */
    int x = lcd_x;
    int y = var.yres - lcd_y;
    /* 计算外框 */
    compute_string_bbox(face, wstr, &bbox);
    /* 反推原点 */
    pen.x = (x - bbox.xMin) * 64; /* 单位: 1/64像素 */
    pen.y = (y - bbox.yMax) * 64; /* 单位: 1/64像素 */
	/*设置matrix*/
	matrix.xx = (FT_Fixed)( cos( angle ) * 0x10000L );
	matrix.xy = (FT_Fixed)(-sin( angle ) * 0x10000L );
	matrix.yx = (FT_Fixed)( sin( angle ) * 0x10000L );
	matrix.yy = (FT_Fixed)( cos( angle ) * 0x10000L );
    /* 处理每个字符 */
    for (i = 0; i < wcslen(wstr); i++)
    {
        /* 转换:transformation */
        FT_Set_Transform(face, &matrix, &pen);
        /* 加载位图: load glyph image into the slot (erase previous one) */
        error = FT_Load_Char(face, wstr[i], FT_LOAD_RENDER);
        if (error)
        {
            printf("FT_Load_Char error\n");
            return -1;
        }
        /* 在LCD上绘制: 使用LCD坐标 */
        draw_bitmap( &slot->bitmap,
                        slot->bitmap_left,
                        var.yres - slot->bitmap_top);
        /* 计算下一个字符的原点: increment pen position */
        pen.x += slot->advance.x;
        pen.y += slot->advance.y;
    }
    return 0;
} 
c.main()
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <wchar.h>
#include <sys/ioctl.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
int fd_fb;
struct fb_var_screeninfo var;	/* Current var */
struct fb_fix_screeninfo fix;   /* Current fix */
int screen_size;
unsigned char *fbmem;
unsigned int line_width;
unsigned int pixel_width;
int main(int argc, char **argv)
{
    FT_Library	  library;					/*freetype库*/
	FT_Face 	  face;						/*对应字体文件*/
	FT_BBox bbox;
	int lcd_x, lcd_y, lcd_y2, lcd_y3;						/*初始坐标*/
	int error;
    int font_size = 24;
	FT_Matrix	  matrix;				  	/* transformation matrix */
	double		  angle;
    //第一步:打开LCD的设备节点以及获取LCD参数
    fd_fb = open("/dev/fb0", O_RDWR);
	if (fd_fb < 0)
	{
		printf("can't open /dev/fb0\n");
		return -1;
	}
	if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
	{
		printf("can't get var\n");
		return -1;
	}
	
	//第二步,设置的参数x_label、y_label、字体大小以及倾斜角度
	if (argc < 5)
	{
        printf("Usage : %s <font_file> <lcd_x> <lcd_y> [font_size]\n", argv[0]);
        return -1;
	}
	lcd_x = strtoul(argv[2], NULL, 0);
	lcd_y = strtoul(argv[3], NULL, 0);
	font_size = strtoul(argv[4], NULL, 0);
	
	if (argc == 6)
		angle  = ( 1.0* strtoul(argv[5], NULL, 0) / 360 ) * 3.14159 * 2;	   /* use 25 degrees	 */
    //第三步:映射framebuffer
    line_width  = var.xres * var.bits_per_pixel / 8;
	pixel_width = var.bits_per_pixel / 8;
	screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
	fbmem = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
	if (fbmem == (unsigned char *)-1)
	{
		printf("can't mmap\n");
		return -1;
	}
    //第四步:使用 wchar_t 获得字符串的 UNICODE 值
	wchar_t *wline1 = L"希希雾里";
	wchar_t *wline2 = L"对自己越坦然,自己就越轻松!";
	wchar_t *wline3 = L"加油呀,争取换个地方继续搬砖,嘿嘿";
    //第五步:屏幕、字体库初始化,构建字体文件对象
	/* 清屏: 全部设为黑色 */
	memset(fbmem, 0, screen_size);
	
	/* 显示矢量字体 */
	error = FT_Init_FreeType( &library );			   /* initialize library */
	/* error handling omitted */
	
	error = FT_New_Face( library, argv[1], 0, &face ); /* create face object */
	
	FT_Set_Pixel_Sizes(face, font_size, 0);
	//第六步,绘制多行字符串(计算外框以及调整原点)
    display_string(face, wline1, lcd_x, lcd_y, angle);
	lcd_y2 = lcd_y + 80;
	display_string(face, wline2, lcd_x, lcd_y2, angle);
	lcd_y3 = lcd_y2 + 80;
	display_string(face, wline3, lcd_x, lcd_y3, angle);
	return 0;	
} 
(3)实现效果
//显示多行文字,祝福自己
./freetype_show_font ./simsun.ttc 100 0 40 0

参考视频:
5_Framebuffer应用编程_哔哩哔哩_bilibili
6.1_字符的编码方式_哔哩哔哩_bilibili 6.2_ASCII字符的点阵显示_哔哩哔哩_bilibili
6.3_中文字符的点阵显示_哔哩哔哩_bilibili 6-4.交叉编译程序_以freetype为例_哔哩哔哩_bilibili
6.5_使用freetype显示单个文字_哔哩哔哩_bilibili 6.6_使用freetype显示一行文字_哔哩哔哩_bilibili



















