LCD屏幕显示文字(含交叉编译freetype字体矢量库)

news2024/9/20 11:00:20

文章目录

  • 字符的编码方式
    • 编码与字体
      • ASCII
      • ANSI
      • UNICODE
        • UNICODE 编码实现
  • ASCII 字符的点阵显示
    • ASCII字符文件
    • 主要代码
  • 中文字符的点阵显示
    • 指定编码格式
    • 汉字的点阵数据(汉字区位码)
  • 交叉编译 freetype 矢量库
    • 程序运行的一些基础知识
    • 怎么确定“系统目录”?
    • 交叉编译程序的万能命令
    • 交叉编译freetype的依赖库
  • 使用 freetype 显示一个文字
    • wchar_t 宽字符
    • Freetype库简介
    • 在LCD上令矢量字体旋转某个角度
  • 使用 freetype 显示一行文字

关闭开发板GUI:systemctl stop myir
恢复开发板GUI:systemctl start myir

字符的编码方式

编码与字体

TXT 文件中保存的是字符的核心:它的编码值
字符对应什么样的形状态:字符文件决定

有以下三种编码方式:ASCII、ANSI(GB2312)、UNICODE、

ASCII

一个字节的 7 位就可以表示 128 个数值,在 ASCII 码中最高位永远是 0

ANSI

ASNI 是 ASCII 的扩展,向下包含 ASCII。对于 ASCII 字符仍以一个字节来表示,对于非 ASCII 字符则使用 2 字节来表示。并没有固定的 ASNI 编码,它跟“本地化”(locale)密切相关。比如在中国大陆地区,ANSI 的默认编码是 GB2312;在港澳台地区默认编码是 BIG5。所以对于 ANSI 编码的 TXT 文件,如果你打开它发现乱码,那么还得再次细分它的具体编码。

在简体中文系统下,ANSI 编码代表 GB2312 编码

UNICODE

对于地球上任意一个字符,都给它一个唯一的编码值。

UNICODE 仍然向下兼容 ASCII,但是对于其他字符会有对应的数值

UNICODE 中的数值范围是 0x0000 至 0x10FFFF,有 1,114,111 即 100 多万个数值,可以表示 100 多万个字符

UNICODE 编码实现

考虑到使用多少个字节去表示一个UNICODE,避免浪费,也避免无法识文断字

UCS-2 Little endian/UTF-16 LE:

  • Little endian 表示小字节序,数值中权重低的字节放在前面
  • 文件开头的“0xff 0xfe”表示“UTF-16 LE”。

UCS-2 Big endian/UTF-16 BE

  • Big endian 表示大字节序,数值中权重低的字节放在后面
  • 文件开头的“0xfe 0xff”表示“UTF-16 BE”。

UTF8

  • 在上面 2 种方法中,每一个 UNICODE 使用 2 字节来表示,这有 3 个缺点:表示的字符数量有限、对于 ASCII 字符有空间浪费、如果文件中有某个字节丢失,这会使得后面所有字符都因为错位而无法显示。
  • 使用 UTF8 可以解决上述所有问题。UTF8 是变长的编码方法,有 2 种 UTF8格式的文件:带有头部(带有BOM)、不带头部
  • 对于其中的 ASCII 字符,在 UTF8 文件中直接用其 ASCII 码来表示
  • 对于非 ASCII 字符,使用变长的编码:每一个字节的高位都自带长度信息

如图所示,将unicode编码值0x4e2d进行编码,得到UTF-8值
在这里插入图片描述

在这里插入图片描述

ASCII 字符的点阵显示

前面介绍了编码方式,采用UNICODE编码得到编码值,在字符集里面找到对应的点阵,遍历点阵就可以得到字符了

ASCII字符文件

要在 LCD 中显示一个 ASCII 字符,即英文字母这些字符,首先是要找到字符对应的点阵。在 Linux 内核源码中有这个文件:lib\fonts\font_8x16.c(8列16行),里面以数组形式保存各个字符的点阵,例如,字符A的表示:李殴辱
上图左侧有 16 行数值,每行 1 个字节,一共16字节。每一个节对应右侧一行中 8 个像素:像素从右边数起,bit0 对应第 0 个像素,bit1 对应第 1 个像素,……,bit7对应第 7 个像素。某位的值为 1 时,表示对应的像素要被点亮;值为 0 时表示对应的像素要熄灭。

所以要显示某个字符时,根据它的 ASCII 码在 fontdata_8x16 数组中找到它的点阵,然后取出这 16 个字节去描画 16行像素

主要代码

省略 lib\fonts\font_8x16.c 里面关于点阵的代码

int fd_fb;
struct fb_var_screeninfo var;	/* Current var */
int screen_size;
unsigned char *fbmem;
unsigned int line_width;
unsigned int pixel_width;

//功能描述: 在LCD指定位置上输出指定颜色(描点)

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

//功能描述: 在LCD指定位置上显示一个8*16的字符

void lcd_put_ascii(int x, int y, unsigned char c)
{
	unsigned char *dots = (unsigned char *)&fontdata_8x16[c*16];//获取传入参数的点阵数组
	int i, b;
	unsigned char byte;

	for (i = 0; i < 16; i++)//循环16行
	{
		byte = dots[i];
		for (b = 7; b >= 0; b--)//循环8列
		{
			if (byte & (1<<b))
			{
				/* show */
				/* 首先要启动Frambuffer */
				lcd_put_pixel(x+7-b, y+i, 0xffffff); /* 白 */
			}
			else
			{
				/* hide */
				lcd_put_pixel(x+7-b, y+i, 0); /* 黑 */
			}
		}
	}
}

int main(int argc, char **argv)
{
	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))//获取 Framebuffer 参数
	{
		printf("can't get var\n");
		return -1;
	}

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

	/* 清屏: 全部设为黑色 */
	memset(fbmem, 0, screen_size);

	lcd_put_ascii(var.xres/2, var.yres/2, 'A'); /*在屏幕中间显示8*16的字母A*/
	
	munmap(fbmem , screen_size);
	close(fd_fb);
	
	return 0;	
}

中文字符的点阵显示

指定编码格式

无论在编写c程序时的编码格式是什么,在编译的时候都可以指定编码格式

我们编写 C 程序时,可以使用 ANSI 编码,或是 UTF-8 编码;在编译程序时,可以使用以下的选项告诉编译器:

在简体中文系统下,ANSI 编码代表 GB2312 编码

-finput-charset=GB2312
-finput-charset=UTF-8

GCC 就会默认 C 程序的编码方式为UTF-8,即使你是以 ANSI 格式保存,也会被当作 UTF-8 来对待

对于编译出来的可执行程序,可以指定它里面的字符是以什么方式编码,可以使用以下的选项编译器:

-fexec-charset=GB2312
-fexec-charset=UTF-8

如果不指定“-fexec-charset”,GCC 就会默认编译出的可执行程序中字符的编码方式为 UTF-8。

如果“-finput-charset”与“-fexec-charset”不一样,编译器会进行格式转换。

在这里插入图片描述

汉字的点阵数据(汉字区位码)

使用 HZK16 这个文件,它是常用汉字的 16*16 点阵字库。

汉字使用GB2312索引,所以编码时必须转化为GB2312格式

HZK16 里每个汉字使用 32 字节来描述

在这里插入图片描述

使用区码与位码找到该汉字所在点阵的位置

  • HZK16 中是以 GB2312 编码值来查找点阵的,以“中”字为例,它的编码值是“0xd6 0xd0”,其中的 0xd6 表示“区码”,表示在哪一个区:第“0xd6- 0xa1”区;其中的 0xd0 表示“位码”,表示它是这个区里的哪一个字符:第“0xd0 - 0xa1”个。每一个区有 94 个汉字。区位码从 0xa1 而不是从 0 开始,是为了兼容 ASCII 码,避免冲突,从0xa1开始。所以,我们要显示的“中”字,它的 GB2312 编码是 d6d0,它是 HZK16 里第“(0xd6-0xa1)*94+(0xd0-0xa1)”个字符

  • 每个区中有 94 个汉字,每个汉字在字库中占据 32 字节。所以,((0xd6-0xa1)*94+(0xd0-0xa1))*32得到该汉字在字符集里面的偏移地址值

主要代码如下:省略fontdata_8x16。


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 fd_hzk16;
struct stat hzk_stat;
unsigned char *hzkmem;



/**********************************************************************
 * 函数名称: lcd_put_pixel
 * 功能描述: 在LCD指定位置上输出指定颜色(描点)
 * 输入参数: x坐标,y坐标,颜色
 * 输出参数: 无
 * 返 回 值: 会
 * 修改日期        版本号     修改人	      修改内容
 * -----------------------------------------------
 * 2020/05/12	     V1.0	  zh(angenao)	      创建
 ***********************************************************************/ 
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;
		}
	}
}
/**********************************************************************
 * 函数名称: lcd_put_ascii
 * 功能描述: 在LCD指定位置上显示一个8*16的字符
 * 输入参数: x坐标,y坐标,ascii码
 * 输出参数: 无
 * 返 回 值: 无
 * 修改日期        版本号     修改人	      修改内容
 * -----------------------------------------------
 * 2020/05/12	     V1.0	  zh(angenao)	      创建
 ***********************************************************************/ 
void lcd_put_ascii(int x, int y, unsigned char c)
{
	unsigned char *dots = (unsigned char *)&fontdata_8x16[c*16];
	int i, b;
	unsigned char byte;

	for (i = 0; i < 16; i++)
	{
		byte = dots[i];
		for (b = 7; b >= 0; b--)
		{
			if (byte & (1<<b))
			{
				/* show */
				lcd_put_pixel(x+7-b, y+i, 0xffffff); /* 白 */
			}
			else
			{
				/* hide */
				lcd_put_pixel(x+7-b, y+i, 0); /* 黑 */
			}
		}
	}
}
/**********************************************************************
 * 函数名称: lcd_put_chinese
 * 功能描述: 在LCD指定位置上显示一个16*16的汉字
 * 输入参数: x坐标,y坐标,ascii码
 * 输出参数: 无
 * 返 回 值: 无
 * 修改日期        版本号     修改人	      修改内容
 * -----------------------------------------------
 * 2020/05/12	     V1.0	  zh(angenao)	      创建
 ***********************************************************************/ 
void lcd_put_chinese(int x, int y, unsigned char *str)
{
	//str[0]中保存区码、
	//str[1]中保存位码
	unsigned int area  = str[0] - 0xA1;
	unsigned int where = str[1] - 0xA1;
	unsigned char *dots = hzkmem + (area * 94 + where)*32;//偏移地址加上初始地址就是该汉字的起始地址 
	unsigned char byte;

	int i, j, b;
	for (i = 0; i < 16; i++)
		for (j = 0; j < 2; j++)
		{
			byte = dots[i*2 + j];
			for (b = 7; b >=0; b--) 
			{
				if (byte & (1<<b))
				{
					/* show */
					lcd_put_pixel(x+j*8+7-b, y+i, 0xffffff); /* 白 */
				}
				else
				{
					/* hide */
					lcd_put_pixel(x+j*8+7-b, y+i, 0); /* 黑 */
				}	
			}
		}
}

int main(int argc, char **argv)
{
	unsigned char str[] = "中";
	
	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;
	}

	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;
	}
	//打开汉字库文件
	fd_hzk16 = open("HZK16", O_RDONLY);
	if (fd_hzk16 < 0)
	{
		printf("can't open HZK16\n");
		return -1;
	}
	//获得文件的状态信息,里面含有文件长度,这在后面的 mmap 中用到。
	if(fstat(fd_hzk16, &hzk_stat))
	{
		printf("can't get fstat\n");
		return -1;
	}
	//使用 mmap 映射文件,以后就可以像访问内存一样读取文件内容;mmap 的返回结果保存在 hzkmem 中,它将作为字库的基地址。
	hzkmem = (unsigned char *)mmap(NULL , hzk_stat.st_size, PROT_READ, MAP_SHARED, fd_hzk16, 0);
	
	if (hzkmem == (unsigned char *)-1)
	{
		printf("can't mmap for hzk16\n");
		return -1;
	}

	/* 清屏: 全部设为黑色 */
	memset(fbmem, 0, screen_size);

    lcd_put_ascii(var.xres/2, var.yres/2, 'A'); /*在屏幕中间显示8*16的字母A*/
	
	printf("chinese code: %02x %02x\n", str[0], str[1]);
	lcd_put_chinese(var.xres/2 + 8,  var.yres/2, str);

	munmap(fbmem , screen_size);
	close(fd_fb);
	
	return 0;	
}

交叉编译后将可执行文件和汉字库HZK16复制到开发板

交叉编译 freetype 矢量库

使用 buildroot 来给 ARM 板编译程序、编译库会很简单

程序运行的一些基础知识

  1. 编译程序时去哪找头文件?
    系统目录:就是交叉编译工具链里的某个 include 目录;也可以自己指定:

  2. 链接时去哪找库文件 ?
    系统目录:就是交叉编译工具链里的某个 lib 目录;也可以自己指定:链

  3. 运行时去哪找库文件 ?
    系统目录:就是板子上的/lib、/usr/lib 目录;也可以自己指定:运行
    程序用环境变量 LD_LIBRARY_PATH 指定。

  4. 运行时不需要头文件,所以头文件不用放到板子上

怎么确定“系统目录”?

执行下面命令确定目录:

echo 'main(){}'| arm-buildroot-linux-gnueabihf-gcc -E -v -

它会列出头文件目录、库目录(LIBRARY_PATH)。
你需要在头文件目录中确定有没有这个文件,或是自己指定头文件目录。

在这里插入图片描述

交叉编译程序的万能命令

如果交叉编辑工具链的前缀是 arm-buildroot-linux-gnueabihf- ,比如 arm-buildroot-linux-gnueabihf-gcc ,交叉编译开源软件时,如果它里面有 configure

万能命令如下:

./configure --host=arm-buildroot-linux-gnueabihf --prefix=$PWD/tmp
make
make install

就可以在当前目录的 tmp 目录下看见 bin, lib, include 等目录,里面存有可执行程序、库、头文件。

执行如下指令 ,可以查看configure后面能够携带哪些参数

,/configure --help

交叉编译freetype的依赖库

下载freetype:版本2.10.2

编译的时候提示需要安装这些依赖库才能正常编译freetype:

有些交叉编译工具链包含该zlib库,则不需要再手动操作,如果该编译工具链不包含,则需要编译,安装,并复制到系统目录的lib和include目录下

例如:

cp include/* -rf /home/book/100ask_stm32mp157_pro-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/arm-buildroot-linux-gnueabihf/sysroot/usr/include

cp lib/* -rfd /home/book/100ask_stm32mp157_pro-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/arm-buildroot-linux-gnueabihf/sysroot/usr/lib

-d表示将lib库里面的链接关系也复制过去

所谓的编译工具链包含该库指的就是编译工具链的系统目录下包含该头文件和库,这样在编译时就可以找到所需的库与文件

注意:make编译的时候可能会找到/usr/local/lib里面的zlib和libpng库,所以要保持这两个库的版本和编译工具链的系统目录一致

使用 freetype 显示一个文字

wchar_t 宽字符

在这里插入图片描述

#include <stdio.h>
#include <string.h>
#include <wchar.h>

int main( int argc, char** argv)
{
	wchar_t *chinese_str = L"中gif";
	unsigned int *p = (wchar_t *)chinese_str;
	int i;

	printf("sizeof(wchar_t) = %d, str's Uniocde: \n", (int)sizeof(wchar_t));
	for (i = 0; i < wcslen(chinese_str); i++)//wcslen:获取字符串长度,用于宽字符集。
	{
		printf("0x%x ", p[i]);
	}
	printf("\n");

	return 0;
}


输出结果
在这里插入图片描述

每个wchar_t类型的字符占四个字节

Freetype库简介

采用了点阵字库来显示字母、汉字等等,由于对这些字体进行放大或者缩小的话,会使字体在显示时可能会出现模糊或者锯齿,故引用了矢量字体(放大缩小不会失真):

矢量字体的形成:

  • ①:确定关键点
  • ②:使用数学曲线(贝塞尔曲线)连接关键点
  • ③:填充闭合曲线内部空间

simsun.ttc文件中,开头是一些字符映射表,以适应不同的编码格式。不同的编码通过自己的字符映射表找到当前这个文字对应的点阵数据,称为glyph
在这里插入图片描述

有了以上基础,一个文字的显示过程可以概括如下:=

  • ①·给定一个字符可以确定它的编码值(ASCII、UNICODE、GB2312);
  • ②·设置字体大;
  • ③·根据编码值,从文件头部中通过charmap找到对应的关键点(glyph),它会根据字体大小调整关键点;
  • ④·把关键点转换为位图点阵;
  • ⑤·在LCD上显示出来

Freetype的使用:

  1. 初始化:FT_InitFreetype

  2. 加载(打开)字体Face:FT_New_Face

  3. 设置字体大小:FT_Set_Char_Sizes 或FT_Set_Pixel_Sizes

  4. 选择Charmp:FT_Select_Charmap,字符映射表

  5. 根据编码值charcode找到glyph_index:glyph_index = FT_Get_Char_Index(face,charcode)

  6. 根据glyph_index取出glyph:FT_Load_Glyph(face,glyph_index)

  7. 转为位图:FT_Render_Glyph

  8. 移动或旋转:FT_Set_Transform

  9. 最后显示出来
    上面的⑤⑥⑦可以使用一个函数代替:FT_Load_Char(face, charcode, FT_LOAD_RENDER),它就可以根据关键点得到位图。

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


/**********************************************************************
 * 函数名称: lcd_put_pixel
 * 功能描述: 在LCD指定位置上输出指定颜色(描点)
 * 输入参数: x坐标,y坐标,颜色
 * 输出参数: 无
 * 返 回 值: 会
 * 修改日期        版本号     修改人	      修改内容
 * -----------------------------------------------
 * 2020/05/12	     V1.0	  zh(angenao)	      创建
 ***********************************************************************/ 
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指定位置显示汉字
 * 输入参数: x坐标,y坐标,位图指针
 * 输出参数: 无
 * 返 回 值: 无
 * 修改日期        版本号     修改人	      修改内容
 * -----------------------------------------------
 * 2020/05/12	     V1.0	  zh(angenao)	      创建
 ***********************************************************************/ 
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]);//在位图里面每个像素只有一个字节,不是rgba的四个字节
		}
	}
}


int main(int argc, char **argv)
{
	//wchar_t:是C/C++的字符类型,是一种扩展的存储方式,一般为16位或32位。
	wchar_t *chinese_str = L"繁";

	FT_Library	  library;
	FT_Face 	  face;
	int error;
    FT_Vector     pen;
	FT_GlyphSlot  slot;
	int font_size = 24;

	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);
		
	fd_fb = open("/dev/fb0", O_RDWR);
	if (fd_fb < 0)
	{
		printf("can't open /dev/fb0\n");
		return -1;
	}
	//使用 ioctl 获得 LCD 分辨率、BPP,存储在var
	if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
	{
		printf("can't get var\n");
		return -1;
	}

	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;
	//fb_base :根据内核申请到的在用户空间态可以使用的基地址
	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;
	}

	/* 清屏: 全部设为黑色 */
	memset(fbmem, 0, screen_size);

	/* 显示矢量字体 */
	//初始化
	error = FT_Init_FreeType( &library );			   /* initialize library */
	/* error handling omitted */
	//加载(打开)字体Face
	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);//设置字体大小

	/* 确定座标:
	 */
	//pen.x = 0;
	//pen.y = 0;

    /* set transformation */
    //FT_Set_Transform( face, 0, &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;	
}


bitmap结构体
在这里插入图片描述

交叉编译:

arm-buildroot-linux-gnueabihf-gcc -o lcd_freetype_font lcd_freetype_font.c -lfreetype	//编译时要连接freetype库

移植交叉编译后的可执行文件和文字库(simsun.ttc)到开发板

执行:

./freetype_show_font simsun.ttc 200 //运行时,第三个参数可选,为字体大小

参考博文:【硬核】韦东山:使用freetype显示一行文字

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

参考博文:使用freetype显示单个文字

需要设置矩阵,设置旋转角度(转化为弧度值)

使用 freetype 显示一行文字

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

/* color : 0x00RRGGBB */
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指定位置显示汉字
 * 输入参数: x坐标,y坐标,位图指针
 * 输出参数: 无
 * 返 回 值: 无
 * 修改日期        版本号     修改人        修改内容
 * -----------------------------------------------
 * 2020/05/12        V1.0     zh(angenao)         创建
 ***********************************************************************/ 
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]);
        }
    }
}

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;//保存字符的处理结果:比如转换后的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;//前一个字符的原点+advance
        pen.y += slot->advance.y;
    }

    /* return string bbox */
    *abbox = bbox;
}


int display_string(FT_Face     face, wchar_t *wstr, int lcd_x, int lcd_y)
{
    int i;
    int error;
    FT_BBox bbox;
    FT_Vector pen;
    FT_Glyph  glyph;
    FT_GlyphSlot slot = face->glyph;//保存字符的处理结果:比如转换后的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像素 */

    /* 处理每个字符 */
    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;
        }

        /* 在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;
}


int main(int argc, char **argv)
{
    wchar_t *wstr = L"百问网www.100ask.net";

    FT_Library    library;
    FT_Face       face;
    int error;
    FT_BBox bbox;
    int font_size = 24;
    int lcd_x, lcd_y;

    if (argc < 4)
    {
        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);      
    
    if (argc == 5)
        font_size = strtoul(argv[4], 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;
    }

    if (ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix))
    {
        printf("can't get fix\n");
        return -1;
    }

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

    /* 清屏: 全部设为黑色 */
    memset(fbmem, 0, screen_size);

    error = FT_Init_FreeType( &library );              /* initialize library */
    
    error = FT_New_Face( library, argv[1], 0, &face ); /* create face object */

    FT_Set_Pixel_Sizes(face, font_size, 0);

    display_string(face, wstr, lcd_x, lcd_y);
    
    return 0;   
}


注意:

  • 笛卡尔坐标系与LCD屏幕的坐标系区别

  • freetype设定了每个字符的大小,设计每个字符的原点

参考博文:【硬核】韦东山:使用freetype显示一行文字

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

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

相关文章

【08】STM32·HAL库开发-HAL库介绍 | STM32Cube固件库介绍 | HAL库框架结构 | 如何使用HAL库及使用注意事项

目录 1.初识HAL库&#xff08;了解&#xff09;1.1CMSIS简介1.2HAL库简介 2.STM32Cube固件包浅析&#xff08;了解&#xff09;2.1如何获取STM32Cube固件包&#xff1f;2.2STM32Cube固件包文件夹简介2.3CMSIS文件夹关键文件2.3.1CMSIS标准规定软件包目录2.3.2Device和Include文…

背光图像增强论文调研

背光图像增强 M. Akai, Y. Ueda, T. Koga and N. Suetake, “A Single Backlit Image Enhancement Method For Improvement Of Visibility Of Dark Part,” 2021 IEEE International Conference on Image Processing (ICIP), Anchorage, AK, USA, 2021, pp. 1659-1663, doi: 10…

山西电力市场日前价格预测【2023-07-05】

日前价格预测 预测明日&#xff08;2023-07-05&#xff09;山西电力市场全天平均日前电价为168.88元/MWh。其中&#xff0c;最高日前电价为334.22元/MWh&#xff0c;预计出现在20: 30。最低日前电价为0.00元/MWh&#xff0c;预计出现在09: 45-17: 15。 以上预测仅供学习参考&a…

【Git】中文显示数字的问题的解决方案

目录 问题截图解决方案 问题截图 运行git status命令&#xff0c;会出现图片中不正常显示中文的问题 解决方案 设置终端: 右键窗口头部 -> Options...执行命令: git config --global core.quotepath false

电脑屏幕监控软件是什么?有哪些功能?

电脑屏幕监控软件是一种用于监视和记录电脑屏幕活动的工具。它可以捕捉屏幕上的所有操作&#xff0c;包括键盘输入、鼠标点击、应用程序使用等。这些软件通常被用于家庭、教育机构和企业等环境中&#xff0c;以确保电脑的安全和监督员工或孩子的活动。 为什么需要电脑屏幕监控软…

STM32实战项目—密码锁

该项目的完整工程可以在博主的资源里找到&#xff0c;仅供参考。 文章目录 一、任务要求二、实现方法2.1 输入密码判断2.2 管理员模式2.3 修改密码 三、程序设计3.1 输入密码判断3.2 进入管理员模式3.3 修改密码 四、问题总结4.1 输入内容一直提示Error4.2 密码判断一直错误 五…

Lua,不使用模块,解压文件

本来lua遍历文件夹下压缩包内容、路径可以通过加载模块&#xff1a; require(“lfs”) 和 require(“zip”) 来解决。 但是本人通过c调用lua时&#xff0c;只要lua脚本中使用require加载模块&#xff0c;就会出错。 因此为了能够让c通过lua&#xff0c;解压当前路径下压缩包&a…

【基础算法】回溯算法相关题目

系列综述&#xff1a; &#x1f49e;目的&#xff1a;本系列是个人整理为了秋招算法的&#xff0c;整理期间苛求每个知识点&#xff0c;平衡理解简易度与深入程度。 &#x1f970;来源&#xff1a;材料主要源于代码随想录进行的&#xff0c;每个算法代码参考leetcode高赞回答和…

Android RecyclerView AsyncListUtil手动刷新fillData,kotlin

Android RecyclerView AsyncListUtil手动刷新fillData&#xff0c;kotlin implementation com.github.bumptech.glide:glide:4.15.1implementation androidx.constraintlayout:constraintlayout:1.1.3 import android.os.Bundle import android.util.Log import android.view.V…

百万连接实现02:使用epoll实现的服务器

使用的操作系统&#xff1a; t$ cat /proc/version Linux version 4.19.260 (lkmaoubuntu) (gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.12)) #1 SMP Thu Sep 29 14:19:07 CST 2022 文件句柄的限制 如果不修改连接测试&#xff0c;会报错 一个tcp连接就需要…

北京大学2015计算机学科夏令营上机考试

目录 A:整数的个数 B:过滤多余的空格 C:二维数组右上左下遍历 D 合影效果 E:Simple prefix compression【做不起】 F:To Europe! To Europe!【做不起】 G:The Game【做不起】 H:Falling Leaves A:整数的个数 #include<iostream> using namespace std; int main(…

VSCode 免安装及中文设置

前言&#xff1a;VSCode作为目前最强大的文本编辑器&#xff0c;通过内部的插件市场可满足各种开发需求。使用免安装版可以自定义插件安装位置等&#xff0c;而使用安装包安装只能通过修改快捷方式自定义&#xff0c;十分不方便。因此这里分享如何安装免安装版的VSCode。 下载…

ETL是什么?怎样更快的学习ETL?

ETL是英文Extract-Transform-Load的缩写&#xff0c;用来描述将数据从源端经过抽取(extract)、转换(transform)、加载(load)至目的端的过程&#xff0c;它能够对各种分布的、异构的源数据(如关系数据)进行抽取&#xff0c;按照预先设计的规则将不完整数据、重复数据以及错误数据…

滤波后点云的个数和之前相同,只是有的点云坐标是nan

进行点云的条件滤波&#xff0c;滤波前后点云的个数不变&#xff0c;只是被滤掉的点坐标显示为nan。代码片段如下&#xff1a; pcl::ConditionAnd<pcl::PointXYZ>::Ptr range_cond(new pcl::ConditionAnd<pcl::PointXYZ>()); range_cond->addComparison(pcl::Fi…

李宏毅transformer讲解;B站内测“搜索AI助手”功能

&#x1f989; AI新闻 &#x1f680; B站内测“搜索AI助手”功能 摘要&#xff1a;据反馈&#xff0c;B站正在内测“搜索 AI 助手”功能。用户在搜索框内输入问句或在搜索词中添加“?”即可体验此新功能。截图显示&#xff0c;该功能会为用户的搜索提供一个生成的答案&#…

hcip作业二

实验要求&#xff1a; 要求&#xff1a;R1-R2-R3-R4-R5 RIP 100运行版本2&#xff1b;R6-R7 RIP 200 运行版本1 1.使用合理IP地址规划网络&#xff0c;各自创建环回接口 2.R1创建环回 172.16.1.1/24 172.16.2.1/24 172.16.3.1/24 3.要求R3使用R2访问R1环回 4.减少路由条目数量&…

盘点一个Python网络爬虫抓取股票代码问题(上篇)

点击上方“Python爬虫与数据挖掘”&#xff0c;进行关注 回复“书籍”即可获赠Python从入门到进阶共10本电子书 今 日 鸡 汤 去来江口守空船&#xff0c;绕船月明江水寒。 大家好&#xff0c;我是皮皮。 一、前言 前几天在Python白银群【厚德载物】问了一个Python网络爬虫的问题…

新手入门深度学习 | 3-2:激活函数activation

一、什么是激活函数 生物神经网络启发了人工神经网络(ANN)的发展。但是,人工神经网络并非大脑运作的近似表示。不过在我们了解为什么在人工神经网络中使用激活函数之前,先了解生物神经网络与激活函数的相关性是很有用处的。 典型神经元的物理结构包括细胞体(cell body)、…

软件的兼容性测试确保良好稳定运行的用户体验

在数字化时代&#xff0c;各种软件应用的开发和推广越来越普遍。然而&#xff0c;由于不同的操作系统、不同的设备配置和不同的软件版本&#xff0c;软件的兼容性成为了一个重要的问题&#xff0c;可以说软件的兼容性测试确保良好稳定运行的用户体验。 首先&#xff0c;软件的兼…