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

news2024/11/23 6:51:42

目录

前言

一、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);

	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);


	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

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

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

相关文章

4K高清修复,模糊视频4k修复是怎么实现的?

在当今数字时代&#xff0c;高分辨率视频已成为大众观影的标配。4K分辨率作为其中高端的选项&#xff0c;提供了比传统1080p高出四倍的细节和清晰度&#xff0c;使得观众们能够更加身临其境地享受影视作品。然而&#xff0c;有时候我们可能会遇到4K视频质量不佳的问题&#xff…

Chapter3-用适合的方式发送和接收消息

3.1 不同类型的消费者 消费者可分为两种类型。 一个是DefaultMQPushConsumer &#xff0c;由系统控制读取操作&#xff0c;收到消息后自动调用传人的处理方法来处理&#xff1b;另 一个是 DefaultMlConsumer &#xff0c;读取操作中的大部分功能由使用者自主控制 。 3.1.1 Def…

uni-app:登录与支付--用户信息

用户信息 实现用户头像昵称区域的基本布局 在 my-userinfo 组件中&#xff0c;定义如下的 UI 结构&#xff1a; <template><view class"my-userinfo-container"><!-- 头像昵称区域 --><view class"top-box"><image src"…

如何通过小程序容器技术实现App的灰度发布

在当今移动应用市场竞争激烈的环境下&#xff0c;如何更快地发布新版本、更精确地测试和调整、更好地了解用户需求和行为&#xff0c;成为了每个App开发者面临的重要挑战。在这个背景下&#xff0c;灰度发布和小程序容器技术成为了越来越受欢迎的解决方案。 灰度发布是指将新版…

【Linux高性能服务器编程】I/O复用的高级应用

文章目录一、基于 select 的非阻塞 connect二、基于 poll 的聊天室程序2.1 客户端2.2 服务器三、基于 epoll 实现同时处理 TCP 和 UDP 服务一、基于 select 的非阻塞 connect connect系统调用的 man 手册中有如下一段内容&#xff1a; EINPROGERESS The socket is nonblocking…

ChatGPT既然这么火,有没有弊端呢?

介绍 在现代社会中&#xff0c;人们越来越依赖技术来解决问题。聊天机器人是一种最新的技术趋势&#xff0c;这种技术可以为人们带来很多便利。而ChatGPT聊天机器人则是其中的一种&#xff0c;它使用了大型的语言模型GPT&#xff08;Generative Pre-trained Transformer&#…

实操干货丨ChatGPT+XMind,效率爆炸,天下无敌

日常办公中&#xff0c;常用工具我们最熟悉的莫过于office铁三角&#xff1a;Word/Excel/PPT&#xff0c;除了这哥仨&#xff0c;再让你推荐一个。 XMind&#xff0c;绝对位列其中。 今天给大家分享一个 ChatGPTXMind的绝佳玩法&#xff0c;不需要下载安装任何额外的工具。 …

【MySQL | 基础篇】05、MySQL 事务详解

目录 一、事务简介 二、事务操作 2.1 未控制事务 2.2 控制事务一 2.3 控制事务二 三、事务四大特性 四、并发事务问题 五、事务隔离级别 六、并发事务演示 6.1 脏读演示 6.2 不可重复读演示 6.3 幻读演示 一、事务简介 事务是一组操作的集合&#xff0c;它是一个不…

【云原生】Kubernetes 中容器跨主机网络是怎么样的?

文章目录前言什么是 FlannelFlannel 的后端实现有哪些UDPVXLANHost-gw基于 Flannel UDP 模式的实现跨主通信UDP 模式案例实现基于 Flannel VXLAN 模式的跨主通信VXLAN 模式案例实现总结前言 在云原生领域&#xff0c;Kubernetes 已经成为了最主流的容器管理工具。Kubernetes 支…

JMM内存模型详解

1、概要 JMM全称叫Java Memory Model&#xff08;Java内存模型&#xff09;&#xff0c;什么是JMM&#xff0c;为什么要设置JMM&#xff0c;要弄清楚这个&#xff0c;咱们要先从计算机硬件存储体系说起。 2、计算机硬件存储体系 Window任务管理器Mac资源管理器window和mac是两…

Flink 优化 (二) --------- 状态及 Checkpoint 调优

目录一、RocksDB 大状态调优1. 开启 State 访问性能监控2. 开启增量检查点和本地恢复3. 调整预定义选项4. 增大 block 缓存5. 增大 write buffer 和 level 阈值大小6. 增大 write buffer 数量7. 增大后台线程数和 write buffer 合并数8. 开启分区索引功能9. 参数设定案例二、Ch…

拐点!智能座舱破局2023

“这是我们看到的整个座舱域控渗透率&#xff0c;2022年是8.28%&#xff0c;主力的搭载车型仍然是30-35万区间。”3月29日&#xff0c;2023年度&#xff08;第五届&#xff09;高工智能汽车市场峰会上&#xff0c;高工智能汽车研究院首发《2022-2025年中国智能汽车产业链市场数…

WRF中替换LAI数据

WRF中替换LAI数据 本次下载的LAI数据是GLASS中的AVHRR(1981-2018)(V50)&#xff0c;可以从这个这里获取数据GLASS_AVHRR_LAI数据集。由于我需要做一个时间序列的模拟&#xff0c;总共下载了从1990-2018年灌溉季3-9月份对应的天数为73&#xff0c;105&#xff0c;137&#xff0…

人工智能大时代——AIGC综述

生成式AI分类 模型按照输入输出的数据类型分类&#xff0c;目前主要包括9类。 有趣的是&#xff0c;在这些已发布大模型的背后&#xff0c;只有六个组织&#xff08;OpenAI, Google, DeepMind, Meta, runway, Nvidia&#xff09;参与部署了这些最先进的模型。 其主要原因是&am…

11.基于粒子群算法的含风光燃储微网优化调度(论文复现)

说明书 相关代码资源&#xff1a;基本算法智能微电网粒子群优化算法,微源&#xff1a;光伏、风机、发电机、储能等 基于多目标算法的冷热电联供型综合能源系统运行优化 基于多目标粒子群算法冷热电联供综合能源系统运行优化 MATLAB代码&#xff1a;基于粒子群算法的含风光燃…

0成本 使用home assistant远程开关机电脑

环境&#xff1a;dockerwin10HACS 问题&#xff1a;在外网手机上远程开关机家中电脑 解决办法&#xff1a;开机&#xff1a;WOL&#xff0c;关机ssh命令 背景&#xff1a;在部署HACS后&#xff0c;便想用HACS中的命令来开关机windows电脑&#xff0c;开机很简单&#xff0c;使用…

暴力破解之验证码识别

文章目录背景操作步骤1、安装python模块2、安装Captcha-killer模块3、尝试进行验证码识别背景 渗透测试过程中&#xff0c;现在验证码越来越多&#xff0c;这对测试的时候遇到的阻力不小&#xff0c;一位大佬给我安利了一个burp插件&#xff0c;Captcha-killer&#xff0c;可以…

ROS开发之如何使用ICM20948 IMU模块?

文章目录0.引言1.创建工作空间2.获取IMU功能包并编译3.检查IMU端口4.启动launch显示IMU测量结果0.引言 笔者研究课题涉及多传感器融合&#xff0c;除了前期对ROS工具的学习&#xff0c;还需要用IMU获取数据&#xff0c;对其他传感器的姿态纠正。本文使用IMU模块获取姿态数据。I…

华为乾坤王辉:新一代网络安全融合体系,筑牢企业数字化转型基石丨2023 INSEC WORLD

科技云报道原创。 随着数字化时代的到来&#xff0c;网络安全形势持续动荡。 围绕产业未来发展趋势、信息安全产业可持续发展、信息安全技术发展路径等话题&#xff0c;一场信息安全行业年度盛会——INSEC WORLD世界信息安全大会在西安盛大召开。 本届大会汇聚了近50位海内…

大数据技术(入门篇) --- 使用 Spring Boot 操作 CDH6.2.0 Hadoop

前言 本人是web后端研发&#xff0c;习惯使用spring boot 相关框架&#xff0c;因此技术选型直接使用的是spring boot&#xff0c;目前并未使用 spring-data-hadoop 依赖&#xff0c;因为这个依赖已经在 2019 年终止了&#xff0c;可以点击查看 &#xff0c;所以我这里使用的是…