【Linux】用LCD文字祝愿(Framebuffer+Freetype)

目录

前言

一、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)字符的编码方式

①字符的标准

  • 字符的标准有ASCIIANSI(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/tmp
make
make install

(2)imx6ull交叉编译freetype

这里freetype库依赖于libpnglibpng 又依赖于 zlib,所以我们应该:先编译安装 zlib,再编译安装 libpng,最后编译安装 freetype。对于imx6ull不用编译安装zlib,在其工具链中有zlib。

①确定头文件、库文件在工具链中的位置

//设置交叉编译工具链
export ARCH=arm
export 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/tmp
make
make 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/include
cp 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/tmp
make
make 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/include
cp 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)

能够在 LCD指定位置上输出指定颜色。这里我们的bpp是对应的565.
//输入参数: 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);

    //释放资源
    FT_Done_FreeType(library);
    FT_Done_Face(face);

	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. 先指定第 1 个字符的原点 pen 坐标为(0, 0),计算出它的外框
  2. 再计算右边字符的原点,也计算出它的外框
  3. (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);


    //释放资源
    FT_Done_FreeType(library);
    FT_Done_Face(face);

	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

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

希希雾里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值