Freetype 介绍和使用

目录

一、矢量字体引入

二、Freetype 介绍

1.给定一个字符,怎么在字体文件中找到它的关键点?

2.文字显示过程

3.如何使用 freetype 库

三、在 LCD 上显示一个矢量字体

1.使用 wchar_t 获得字符的 UNICODE 值

2.使用 freetype 得到位图

3.在屏幕上显示位图

4.编译

5.上机测试

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

1.关键代码解析

2.编译

3.上机测试

五、使用 freetype 显示一行文字

1.笛卡尔坐标系

2.每个字符的大小可能不同

3.怎么在指定位置显示一行文字

4.freetype 的几个重要数据结构

(1)FT_Library

(2)FT_Face

(3)FT_GlyphSlot

(4)FT_Glyph

(5)FT_BBox

5.计算一行文字的外框

6.调整原点并绘制

7.编译

8.上机测试


一、矢量字体引入

使用点阵字库显示英文字母、汉字时,大小固定,如果放大缩小则会模糊甚至有锯齿出现,为了解决这个问题,引用矢量字体。

矢量字体形成分三步:

  • 1.确定关键点
  • 2.使用数学曲线(贝塞尔曲线)连接头键点
  • 3.填充闭合区线内部空间

什么是关键点?以字母“A”为例,它的的关键点如图 6.16 中的黄色所示。

再用数学曲线(比如贝塞尔曲线)将关键点都连接起来,得到一系列的封闭的曲线, 如图 6.17 所示:

最后把封闭空间填满颜色,就显示出一个 A 字母,如图 6.18 所示:

如果需要放大或者缩小字体,关键点的相对位置是不变的,只要数学曲线平滑,字体就不会变形。

二、Freetype 介绍

Freetype 是开源的字体引擎库,它提供统一的接口来访问多种字体格式文件, 从而实现矢量字体显示。我们只需要移植这个字体引擎,调用对应的 API 接口, 提供字体文件,就可以让 freetype 库帮我们取出关键点、实现闭合曲线,填充 颜色,达到显示矢量字体的目的。

关 键 点 (glyph) 存 在 字 体 文 件 中 , Windows 使 用 的 字 体 文 件 在 c:\Windows\Fonts 目录下,扩展名为 TTF 的都是矢量字库,本次使用实验使用的是新宋字体 simsun.ttc

1.给定一个字符,怎么在字体文件中找到它的关键点?

首先要确定该字符的编码值:比如 ASCII 码、GB2312 码、UNICODE 码。如 果字体文件支持某种编码格式(charset),就可以使用这类编码值去找到该字符 的关键点(glyph)。有些字体文件支持多种编码格式(charset),这在文件中被称为 charmaps (注意:这个单词是复数,意味着可能支持多种 charset )。

以 simsun.ttc 为例,该字体文件的格如下:头部含有 charmaps,可以使 用某种编码值去 charmaps 中找到它对应的关键点。下图中的“A、B、中、国、 韦”等只是 glyph 的示意图,表示关键点。

Charmaps 表示字符映射表,字体文件可能支持哪一些编码,GB2312、 UNICODE、BIG5 或其他。如果字体文件支持该编码,使用编码值通过 charmap 就 可以找到对应的 glyph,一般而言都支持 UNICODE 码。

2.文字显示过程

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

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

3.如何使用 freetype 库

  1. 初始化:FT_InitFreetype
  2. 加载(打开)字体 Face:FT_New_Face
  3. 设置字体大小:FT_Set_Char_Sizes 或 FT_Set_Pixel_Sizes
  4. 选择 charmap:FT_Select_Charmap
  5. 根据编码值 charcode 找到 glyph_index:glyph_index = FT_Get_Char_Index(face,charcode) 根据 glyph_index 取出 glyph:FT_Load_Glyph(face,glyph_index)
  6. 转为位图:FT_Render_Glyph
  7. 移动或旋转:FT_Set_Transform
  8. 最后显示出来。

上面的5,6,7可以使用一个函数代替:FT_Load_Char(face, charcode, FT_LOAD_RENDER),它就可以得到位图。

三、在 LCD 上显示一个矢量字体

1.使用 wchar_t 获得字符的 UNICODE 值

要显示一个字符,首先要确定它的编码值。常用的是 UNICODE 编码,在程序 里使用这样的语句定义字符串时,str 中保存的要么是 GB2312 编码值,要么是 UTF-8 格式的编码值,即使编译时使用“-fexec-charset=UTF-8”,str 中保存的也不是直接能使用的 UNICODE 值:

如果想在代码中能直接使用 UNICODE 值,需要使用 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++)
	{
		printf("0x%x ", p[i]);
	}
	printf("\n");

	return 0;
}

UTF-8 格式保存 test_wchar.c,编译、测试命令如下:

每个 wchar_t 占据 4 字节,可执行程序里 wchar_t 中保存的就是字符的 UNICODE 值。

注意:如果 test_wchar.c 是以 ANSI(GB2312)格式保存,那么需要使用以下命令来编译:

gcc -finput-charset=GB2312 -fexec-charset=UTF-8 -o test_wchar test_wchar.c

-finput-charset=GB2312表示将输入文件的字符集设置为GB2312

-fexec-charset=UTF-8表示将执行文件的字符集设置为UTF-8

-o test_wchar表示输出文件名为test_wchar

最后的test_wchar.c是源文件名。

2.使用 freetype 得到位图

使用 freetype 显示一个字符并不难,要使用 freetype 得到一个字符的位图,只需要 4 个步骤,代码先贴出来再分析:

  1. 初始化 freetype 库
  2. 加载字体文件,保存在&face 中:
  3. 设置字体大小
  4. 根据编码值得到位图

3.在屏幕上显示位图

位图里的数据格式是怎样的?参考官方示例的代码,可以得到:

要在屏幕上显示出这些位图,并不复杂,draw_bitmap 函数代码如下,由于位图中每一个像素用一个字节来表示,在 0x00RRGGBB 的颜色格式中它只能表示蓝色,所以在 LCD 上显示出来的文字是蓝色的。

4.编译

freetype_show_font.c

#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]);
		}
	}
}


int main(int argc, char **argv)
{
	wchar_t *chinese_str = L"繁";

	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;

	if (argc < 3)
	{
		printf("Usage : %s <font_file> <angle> [font_size]\n", argv[0]);
		return -1;
	}

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

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

	/* 确定座标:
	 */
	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;	
}

在这里我们得先交叉编译工具链 freetype,然后编译程序:

arm-buildroot-linux-gnueabihf-gcc -o freetype_show_font freetype_show_font.c -lfreetype

如果出现以下错误:

显示找不到头文件,具体解决方法可看:交叉编译freetype-CSDN博客

5.上机测试

将编译好的 freetype_show_font 文件与 simsun.ttc 字体文件拷贝至开发板,这 2 个文件放在同一个目录下,然后执行以下命令。

./freetype_show_font ./simsun.ttc 

如果实验成功,我们将在屏幕中间看到一个蓝色的“繁”字。

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

1.关键代码解析

2.编译

freetype_show_font_angle.c

#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]);
		}
	}
}


int main(int argc, char **argv)
{
	wchar_t *chinese_str = L"繁";

	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;

	if (argc < 3)
	{
		printf("Usage : %s <font_file> <angle> [font_size]\n", argv[0]);
		return -1;
	}

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

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

	/* 确定座标:
	 */
	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;	
}

编译:

arm-buildroot-linux-gnueabihf-gcc -o freetype_show_font_angle freetype_show_font_angle.c -lfreetype -lm 

-lfreetype   链接freetype 库

-lm             链接数学库

3.上机测试

将编译好的 freetype_show_font_angle 文件与 simsun.ttc 字体文件拷 贝至开发板,这 2 个文件放在同一个目录下,然后执行以下命令。

./freetype_show_font_angle ./simsun.ttc 90 200

如果实验成功,我们将在屏幕中间看到一个蓝色的、旋转了 90 度的“繁”字。

五、使用 freetype 显示一行文字

实验:在 LCD 上指定一个左上角坐标(x, y),把一行文字显示出来。 下图中,文字的外框用虚线表示,外框的左上角坐标就是(x, y)。

1.笛卡尔坐标系

在 LCD 的坐标系中,原点在屏幕的左上角。对于笛卡尔坐标系,原点在左下角。freetype 使用笛卡尔坐标系,在显示时需要转换为 LCD 坐标系。

从图可知,X 方向坐标值是一样的。 在 Y 方向坐标值需要换算,假设 LCD 的高度是 V。 在 LCD 坐标系中坐标是(x, y),那么它在笛卡尔坐标系中的坐标值为(x, V-y)。 反过来也是一样的,在笛卡尔坐标系中坐标是(x, y),那么它在 LCD 坐标 系中坐标值为(x, V-y)

2.每个字符的大小可能不同

在使用 FT_Set_Pixel_Sizes 函数设置字体大小时,这只是“期望值”。

比如“百问网 www.100ask.net”,如果把“.”显示得跟其他汉字一样大,不好看。 所以在显示一行文字时,后面文字的位置会受到前面文字的影响。

在这里,freetype 帮我们考虑到了这些影响。

在显示一行文字时,这些文字会基于同一个基线来绘制位图:baseline。

在 baseline 上,每一个字符都有它的原点(origin),比如上图中 baseline 左边的黑色圆点就是字母“g”的原点。

当前 origin 加上 advance 就可以得到 下一个字符的 origin,比如上图中 baseline 右边的黑色圆点。

在显示一行中多个文件字时,后一个文字的原点依赖于前一个文字的原点及 advance。 字符的位图是有可能越过 baseline 的,比如上图中字母“g”在 baseline 下方还有图像。 上图中红色方框内就是字母“g”所点据的位图,它的四个角落不一定与原点重合。

上图中那些 xMin 、 xMax 、 yMin 、 yMax 如 何 获 得 ?

可以使用 FT_Glyph_Get_CBox 函数获得一个字体的这些参数,将会保存在一个 FT_BBox 结构体中,以后想计算一行文字的外框时要用到下图这些信息:

3.怎么在指定位置显示一行文字

要显示一行文字时,每一个字符都有自己外框:xMin、xMax、yMin、yMax。

把这些字符的 xMin、yMin 中的最小值取出来,把这些字符的 xMax、yMax 中的最大值取出来,就可以确定这行文字的外框了。

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

  1. 先指定第 1 个字符的原点 pen 坐标为(0, 0),计算出它的外框
  2. 再计算右边字符的原点,也计算出它的外框,把所有字符都处理完后就可以得到一行文字的整体外框:假设外框左上角坐标为(x', y')。
  3. 想在(x, y)处显示这行文字,调整一下 pen 坐标即可。怎么调整? pen 为(0, 0)时对应左上角(x', y');那么左上角为(x, y)时就可以算出 pen 为(x-x', y-y')。

4.freetype 的几个重要数据结构

要想形象地理解程序,需要先介绍一下 freetype 中几个数据结构:

(1)FT_Library

对应 freetype 库,使用 freetype 之前要先调用以下代码:

FT_Library library; /* 对应 freetype 库 */
error = FT_Init_FreeType( &library ); /* 初始化 freetype 库 */

(2)FT_Face

它对应一个矢量字体文件,在源码中使用 FT_New_Face 函数打开字体文件后,就可以得到一个 face。

为什么称之为 face?

估计是文字都是写在二维平面上的吧,正对着人脸?不用管原因了,总之认为它对应一个字体文件就可以。

代码如下:

error = FT_New_Face(library, font_file, 0, &face ); /* 加载字体文件 */

(3)FT_GlyphSlot

插槽?用来保存字符的处理结果:比如转换后的 glyph、位图,如图:

一个 face 中有很多字符,生成一个字符的点阵位图时,位图保存在哪里?

保存在插槽中:face->glyph

  • 生成第 1 个字符位图时,它保存在 face->glyph 中;
  • 生成第 2 个字符位图时,也会保存在 face->glyph 中,会覆盖第 1 个字符的位图。

代码如下:

FT_GlyphSlot slot = face->glyph; /* 插槽: 字体的处理结果保存在这里 */

(4)FT_Glyph

字体文件中保存有字符的原始关键点信息,使用 freetype 的函数可以放大、 缩小、旋转,这些新的关键点保存在插槽中(注意:位图也是保存在插槽中)。

新的关键点使用 FT_Glyph 来表示,可以使用这样的代码从 slot 中获得 glyph:

error = FT_Get_Glyph(slot , &glyph);

(5)FT_BBox

FT_BBox 结构体定义如下,它表示一个字符的外框,即新 glyph 的外框:

可以使用以下代码从 glyph 中获得这些信息:

FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_TRUNCATE, &bbox );

针对上述流程,示例代码如下:

5.计算一行文字的外框

前面提到过,一行文字中:后一个字符的原点=前一个字符的原点+advance。 所以要计算一行文字的外框,需要按照排列顺序处理其中的每一个字符。

代码解析如下:

6.调整原点并绘制

代码解析如下:

7.编译

show_line.c

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

    /* 初始化 */
    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;
}


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;

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

编译:

arm-buildroot-linux-gnueabihf-gcc -o show_line show_line.c -lfreetype

8.上机测试

将编译好的 show_line 文件与 simsun.ttc 字体文件拷贝至开发板,这 2 个文件放在同一个目录下,然后执行以下命令(其中的 3 个数字分别表示 LCD 的 X 坐标、Y 坐标、字体大小):

./show_line ./simsun.ttc 10 200 80

如果实验成功,可以在 LCD 上看到一行文字“百问网 www.100ask.net”。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java——数组排序

一、排序介绍 1、排序的概念 排序是将多个数据按照指定的顺序进行排列的过程。 2、排序的种类 排序可以分为两大类&#xff1a;内部排序和外部排序。 3、内部排序和外部排序 1&#xff09;内部排序 内部排序是指数据在内存中进行排序&#xff0c;适用于数据量较小的情况…

一个 buffer 使用的负反馈实例

端到端拥塞控制其实就是负反馈的实施。典型的做法是识别到一系列标志性事件&#xff0c;比如丢包&#xff0c;时延增加等&#xff0c;然后对这些事件做反应&#xff0c;进而形成负反馈&#xff0c;但 inflight 守恒是一种完全不同的做法&#xff0c;它将负反馈平铺到了整个传输…

pytorch 笔记:pytorch 优化内容(更新中)

1 Tensor创建类 1.1 直接创建Tensor&#xff0c;而不是从Python或Numpy中转换 不要使用原生Python或NumPy创建数据&#xff0c;然后将其转换为torch.Tensor直接用torch.Tensor创建或者直接&#xff1a;torch.empty(), torch.zeros(), torch.full(), torch.ones(), torch.…

嵌入式Linux系统编程 — 3.2 stat、fstat 和 lstat 函数查看文件属性

目录 1 文件有哪些属性 2 stat函数 2.1 stat函数简介 2.2 struct stat 结构体 2.3 struct timespec 结构体 2.4 示例程序 3 fstat 和 lstat 函数 3.1 fstat 函数 3.2 lstat 函数 1 文件有哪些属性 Linux文件属性是对文件和目录的元数据描述&#xff0c;包括文件类型…

代码随想录-算法训练营day61【代码随想录-算法训练营-总结】

代码随想录-035期-算法训练营【博客笔记汇总表】-CSDN博客 算法训练营&#xff0c;60天&#xff0c;包含这些内容&#xff1a; 01、数组 02、链表 03、哈希表 04、字符串 05、双指针法 06、栈与队列 07、二叉树 08、回溯算法 09、贪心算法 10、动态规划 11、单调栈 12、图论 做…

数据库同步软件PanguSync常见错误解决方法

​​​​​​在部署PanguSync数据库同步软件的过程中&#xff0c;常常会遇见一些错误提示&#xff0c;某些老铁可能会一脸懵逼&#xff0c;本文对一些常见的错误信息进行了总结&#xff0c;并提供了解决方法。 1.")"附近有语法错误 该问题是由于源表未设置主键&…

安卓打造安装包(应用打包、规范处理安装包、安全加固)

本章介绍应用安装包的基本制作规范&#xff0c;主要包括&#xff1a;如何导出既美观又精简的APK文件、如何按照上线规范调整App的相关设置、如何对APK文件进行安全加固以防止安装包被破解。 应用打包 本节介绍APK安装包的打包过程&#xff0c;包括&#xff1a;如何利用Androi…

Java Web学习笔记21——前后端分离开发

前后端混合开发&#xff1a; 沟通成本比较高。 分工不明确。 不便管理&#xff0c;不便于后期的维护和拓展。 前后端分离开发&#xff1a; 当前主流的开发模式&#xff1a;前后端分离开发&#xff1a; 接口文档&#xff1a; 接口并不是interface。 接口指的是业务功能。 …

选择排序(直接选择排序与堆排序)----数据结构-排序②

1、选择排序 1.1 基本思想 每一次从待排序的数据元素中选出最小&#xff08;或最大&#xff09;的一个元素&#xff0c;放在序列的起始位置&#xff0c;直到全部待排序的数据元素排完就停止 。 1.2 直接选择排序 排序思想&#xff1a; ①在元素集合array[i]--array[n-1]中选择…

Java项目如何外发告警日志到企业微信

前言 最近领导交代了一个需求,就是有些许客户不单单满足平台告警日志外发到邮箱、短信的形式,还要以消息聊天的形式外发给企业微信。 具体操作 1、注册企业微信。 2、登录企业微信,找到应用管理,创建应用。 3、创建完之后需要记录以下图片中两个值的信息。 4、然后记录下…

stanfordcorenlp+python做中文nlp任务,得到的结果中全是空字符串,而不是中文字符串

问题描述 代码&#xff1a; from stanfordcorenlp import StanfordCoreNLP import logging#中文中的应用&#xff0c;一定记得下载中文jar包&#xff0c;并标志lang‘zh’ nlp_zh StanfordCoreNLP(rD:\stanford-corenlp-full-2016-10-31, port8094, langzh,quietFalse,logg…

【排序算法】总结篇

✨✨这些 排序算法都是指的 需要进行比较的排序算法 ✨✨下面都是略微讲解一下思路&#xff0c;如果需要详细了解哪一个排序&#xff0c;点击&#x1f449;链接即可 ✨✨对于时间、空间复杂度、稳定性&#xff0c;希望你&#x1f9d1;‍&#x1f393;能够理解记忆&#x1f9d1;…

SpeedyBee飞塔F405 V3 50A

遥控器常用的几种协议&#xff1a; 一文打尽PWM协议、PPM协议、PCM协议、SBUS协议、XBUS协议、DSM协议 | STM32的通用定时器TIM3实现PPM信号输出 - 蔡子CaiZi - 博客园 (cnblogs.com) SpeedyBee飞塔的官方教程&#xff1a; FlowUs 息流 - 新一代生产力工具 为8位电调刷写固…

【纯血鸿蒙】——自适应布局如何实现?

界面级一多能力有 2 类&#xff1a; 自适应布局: 略微调整界面结构 响应式布局&#xff1a;比较大的界面调整 本文章先主要讲解自适应布局&#xff0c;响应式布局再后面文章再细讲。话不多说&#xff0c;开始了。 自适应布局 针对常见的开发场景&#xff0c;方舟开发框架提…

融合创新:Web3如何重新定义网络生态

随着区块链技术的不断发展和Web3时代的到来&#xff0c;我们正在见证着互联网生态的巨大变革。Web3将传统的互联网架构转变为去中心化、开放、透明的新网络生态&#xff0c;为创新和合作提供了全新的可能性。本文将深入探讨Web3如何重新定义网络生态&#xff0c;探索融合创新的…

HAL STM32F1 通过查表方式实现SVPWM驱动无刷电机测试

HAL STM32F1 通过查表方式实现SVPWM驱动无刷电机测试 &#x1f4cd;相关篇《基于开源项目HAL STM32F4 DSP库跑SVPWM开环速度测试》 ✨针对STM32F1系列&#xff0c;没有专门的可依赖的DSP库&#xff0c;为了实现特定函数的浮点运算快速计算&#xff0c;通过查表方式来实现&#…

搭建多平台比价软件你必须知道的几大知识板块

为了搭建一个多平台比价系统并使其发挥作用&#xff0c;你需要考虑以下几个关键的平台支持方面&#xff1a; 数据API采集平台&#xff1a; 电商平台&#xff1a;如亚马逊、淘宝、京东等&#xff0c;这些平台提供了丰富的商品信息和价格数据。旅行服务平台&#xff1a;如携程、…

git凭证

默认是manager # 将凭证缓存到内存中&#xff0c;默认缓存15分钟 git config --global credential.helper cache# 将凭证存储到磁盘上的纯文本文件中 git config --global credential.helper store# 使用 Git 凭证管理器 git config --global credential.helper manager-core查…

红队神器Evil-winrm的使用

前言 Evil-winrm 工具最初是由 Hackplayers 团队开发的。开发该工具的目的是尽可能简化渗透测试&#xff0c;尤其是在 Microsoft Windows 环境中。 Evil-winrm 使用 PowerShell 远程协议 (PSRP)&#xff0c;且系统和网络管理员经常使用Windows Remote Management 协议进行上传和…

C++基础四:C++模板编程

目录 一:函数模板 二:类模板 空间配置器allocator 一:函数模板 模板代码只能同一实现,不能先声明,再在另一文件实现,模板代码都是放在头文件当中的,在头文件中直接实现 二:类模板 template<typename T=int> class SeqStack // 模板名称+类型参数列表 = 类名称…