freetype学习总结

freetype学习总结

基于韦东山IMX6ULL开发板和配套资料中LCD屏幕学习

freetype库资料包:开源的字体引擎库 freetype 和字体文件 simsun.ttc,资料包括:

​ 1、freetype-2.10.2.tar.xz

​ 2、freetype-doc-2.10.2.tar.xz

​ 3、libpng-1.6.37.tar.xz

​ 4、zlib-1.2.11.tar.gz

​ 5、simsun.ttc文件,freetype可以直接使用

freetype 依赖于 libpng, libpng 又依赖于 zlib,所以我们应该:先编译安装 zlib,再编译安装 libpng,最后编译安装 freetype。 但是,有些工具链里有 zlib, 那就不用编译安装 zlib。

freetype官方文档:FreeType Documentation

1. LCD显示字符问题引入

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

矢量字体形成分三步:

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

以字母“A”为例,它的的关键点如图中的黄色所示:

在这里插入图片描述

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

在这里插入图片描述

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

在这里插入图片描述

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

在嵌入式设备上移植开源的字体渲染引擎freetype,调用对应的API接口,提供字体文件,就可以让freetype库帮我们取出关键点、实现闭合曲线,填充颜色,达到显示矢量字体的目的。

2. freetype概念

FreeType是一个开源的字体渲染引擎,它能够加载和渲染多种格式的字体文件,如TrueType(.ttf)、OpenType(.otf)等。FreeType提供了一组API来处理字体数据,包括字符的加载、渲染以及获取字符的各种信息(例如字形边界框、位图等)。它被广泛应用于各种需要显示文本的应用程序中,尤其是在那些需要高质量文本渲染的地方。

2.1 嵌入式设备使用FreeType的方法步骤

  • 安装 FreeType 库
    • 下载FreeType源码。
    • 配置并编译FreeType以适应你的目标平台。
    • 将编译好的库文件和头文件部署到你的开发环境中。
  • 编写代码:
    • 初始化FreeType库。
    • 加载字体文件。
    • 设置字体大小。
    • 渲染指定的字符或字符串。
    • 处理渲染后的位图数据。
    • 清理资源。
  • 集成到图形系统
    • 根据你的嵌入式系统的图形库(如SDL,OpenGL ES,或者自定义的绘图函数),将渲染后的位图绘制到屏幕上。

2.2 嵌入式设备使用FreeType的注意事项

  • 内存限制:嵌入式设备可能有严格的内存限制,因此要确保 FreeType 的配置和使用不会占用过多内存。
  • 性能优化:根据嵌入式设备的性能特性,对 FreeType 的使用进行优化,比如选择合适的字体大小和渲染选项。
  • 字体文件大小:考虑存储空间限制,选择合适大小的字体文件。
  • 跨平台兼容性:确保 FreeType 的编译设置与你的目标硬件平台兼容。

3. freetype官方C示例

3.1 example1.c源码

/* example1.c                                                      */
/*                                                                 */
/* This small program shows how to print a rotated string with the */
/* FreeType 2 library.                                             */


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

#include <ft2build.h>
#include FT_FREETYPE_H


#define WIDTH   640
#define HEIGHT  480


/* origin is the upper left corner */
unsigned char image[HEIGHT][WIDTH];


/* Replace this function with something useful. */

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;


  /* for simplicity, we assume that `bitmap->pixel_mode' */
  /* is `FT_PIXEL_MODE_GRAY' (i.e., not a bitmap font)   */

  for ( i = x, p = 0; i < x_max; i++, p++ )
  {
    for ( j = y, q = 0; j < y_max; j++, q++ )
    {
      if ( i < 0      || j < 0       ||
           i >= WIDTH || j >= HEIGHT )
        continue;

      image[j][i] |= bitmap->buffer[q * bitmap->width + p];
    }
  }
}


void
show_image( void )
{
  int  i, j;


  for ( i = 0; i < HEIGHT; i++ )
  {
    for ( j = 0; j < WIDTH; j++ )
      putchar( image[i][j] == 0 ? ' '
                                : image[i][j] < 128 ? '+'
                                                    : '*' );
    putchar( '\n' );
  }
}


int
main( int     argc,
      char**  argv )
{
  FT_Library    library;
  FT_Face       face;

  FT_GlyphSlot  slot;
  FT_Matrix     matrix;                 /* transformation matrix */
  FT_Vector     pen;                    /* untransformed origin  */
  FT_Error      error;

  char*         filename;
  char*         text;

  double        angle;
  int           target_height;
  int           n, num_chars;


  if ( argc != 3 )
  {
    fprintf ( stderr, "usage: %s font sample-text\n", argv[0] );
    exit( 1 );
  }

  filename      = argv[1];                           /* first argument     */
  text          = argv[2];                           /* second argument    */
  num_chars     = strlen( text );
  angle         = ( 25.0 / 360 ) * 3.14159 * 2;      /* use 25 degrees     */
  target_height = HEIGHT;

  error = FT_Init_FreeType( &library );              /* initialize library */
  /* error handling omitted */

  error = FT_New_Face( library, filename, 0, &face );/* create face object */
  /* error handling omitted */

  /* use 50pt at 100dpi */
  error = FT_Set_Char_Size( face, 50 * 64, 0,
                            100, 0 );                /* set character size */
  /* error handling omitted */

  /* cmap selection omitted;                                        */
  /* for simplicity we assume that the font contains a Unicode cmap */

  slot = face->glyph;

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

  /* the pen position in 26.6 cartesian space coordinates; */
  /* start at (300,200) relative to the upper left corner  */
  pen.x = 300 * 64;
  pen.y = ( target_height - 200 ) * 64;

  for ( n = 0; n < num_chars; n++ )
  {
    /* set transformation */
    FT_Set_Transform( face, &matrix, &pen );

    /* load glyph image into the slot (erase previous one) */
    error = FT_Load_Char( face, text[n], FT_LOAD_RENDER );
    if ( error )
      continue;                 /* ignore errors */

    /* now, draw to our target surface (convert position) */
    draw_bitmap( &slot->bitmap,
                 slot->bitmap_left,
                 target_height - slot->bitmap_top );

    /* increment pen position */
    pen.x += slot->advance.x;
    pen.y += slot->advance.y;
  }

  show_image();

  FT_Done_Face    ( face );
  FT_Done_FreeType( library );

  return 0;
}

/* EOF */

4. 嵌入式设备上使用FreeType的简单示例

4.1 简单示例代码

在嵌入式设备上使用FreeType来加载字体并渲染字符 “A”,然后假设你有一个自定义的绘图函数draw_pixel来在屏幕上绘制点。

代码:

#include <ft2build.h>
#include FT_FREETYPE_H
#include <stdio.h>
#include <stdlib.h>

// 假设这是你的绘图函数
void draw_pixel(int x, int y, unsigned char color) {
    // 实现具体的绘图逻辑
    // 这里只是一个示例
    printf("Draw pixel at (%d, %d) with color %u\n", x, y, color);
}

int main() {
    FT_Library ft;
    if (FT_Init_FreeType(&ft)) {
        fprintf(stderr, "Could not init FreeType Library\n");
        return 1;
    }

    FT_Face face;
    if (FT_New_Face(ft, "/path/to/your/fontfile.ttf", 0, &face)) {
        fprintf(stderr, "Could not open font\n");
        return 1;
    }

    FT_Set_Pixel_Sizes(face, 0, 24);

    if (FT_Load_Char(face, 'A', FT_LOAD_RENDER)) {
        fprintf(stderr, "Could not load character 'A'\n");
        return 1;
    }

    FT_Bitmap bitmap = face->glyph->bitmap;
    int width = bitmap.width;
    int height = bitmap.rows;
    unsigned char* buffer = bitmap.buffer;

    for (int y = 0; y < height; ++y) {
        for (int x = 0; x < width; ++x) {
            if (buffer[y * width + x] > 0) {
                int screen_x = 100 + x;
                int screen_y = 100 + y;
                draw_pixel(screen_x, screen_y, 255);
            }
        }
    }

    FT_Done_Face(face);
    FT_Done_FreeType(ft);

    return 0;
}

4.2 代码分析

代码分析参考官方freetype教程:

在这里插入图片描述

在这里插入图片描述

  • 包含必要的头文件:
#include <ft2build.h>
#include FT_FREETYPE_H
#include <stdio.h>
#include <stdlib.h>

在这里插入图片描述

在这里插入图片描述

  • 初始化FreeType库:
FT_Library ft;
if (FT_Init_FreeType(&ft)) {
    fprintf(stderr, "Could not init FreeType Library\n");
    exit(1);
}

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 加载字体文件:
FT_Face face;
if (FT_New_Face(ft, "/path/to/your/fontfile.ttf", 0, &face)) {
    fprintf(stderr, "Could not open font\n");
    exit(1);
}

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 设置字体大小:
FT_Set_Pixel_Sizes(face, 0, 24); // 设置为24像素高

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

  • 加载置顶字符:
if (FT_Load_Char(face, 'A', FT_LOAD_RENDER)) {
    fprintf(stderr, "Could not load character 'A'\n");
    exit(1);
}

在这里插入图片描述

在这里插入图片描述

  • 获取位图信息:
FT_Bitmap bitmap = face->glyph->bitmap;
int width = bitmap.width;
int height = bitmap.rows;
unsigned char* buffer = bitmap.buffer;

在这里插入图片描述

在这里插入图片描述

  • 绘制位图:
// 假设 draw_pixel(x, y, color) 是绘图函数
for (int y = 0; y < height; ++y) {
    for (int x = 0; x < width; ++x) {
        if (buffer[y * width + x] > 0) {  // 如果像素不透明
            int screen_x = 100 + x;  // 假设起始位置是 (100, 100)
            int screen_y = 100 + y;
            draw_pixel(screen_x, screen_y, 255);  // 使用白色绘制
        }
    }
}
  • 清理资源:
FT_Done_Face(face);
FT_Done_FreeType(ft);

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

5. 交叉编译freetype

freetype 依赖于 libpng, libpng 又依赖于 zlib,所以我们应该:先编译安装 zlib,再编译安装 libpng,最后编译安装 freetype。 但是,有些工具链里有 zlib, 那就不用编译安装 zlib。文章开头资料包中包含需要的库。

基于IMX6ULL开发板验证,使用对于的工具链对freetype交叉编译。

5.1 确定头文件、库文件在工具链中的目录

先设置交叉编译工具链:

export ARCH=arm
export CROSS_COMPILE=arm-buildroot-linux-gnueabihf-
export PATH=$PATH:/home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin

IMX6ULL开发板为例,它的工具链是arm-buildroot-linuxgnueabihf-gcc,可以执行以下命令:

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

在这里插入图片描述

5.2 交叉编译、安装libpng

先把freetype-2.10.2.tar.xz 、freetype-doc-2.10.2.tar.xz 、libpng-1.6.37.tar.xz 、zlib-1.2.11.tar.gz库的压缩文件上传到Ubuntu:

在这里插入图片描述

freetype依赖于libpng,所以需要先编译、安装libpng。命令如下:

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
cd tmp
cp include/* -rf /home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../lib/gcc/arm-buildroot-linux-gnueabihf/7.5.0/include
cp lib/* -rfd /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

5.3 交叉编译、安装freetype

命令如下:

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
cd tmp
cp include/freetype2/* -rfd /home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../lib/gcc/arm-buildroot-linux-gnueabihf/7.5.0/include
cp lib/* -rfd /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

6. 使用freetype在LCD显示矢量字体

freetype使用统一的接口来访问多种字体格式文件,从而实现矢量字体显示。关键点(glyph)存在字体文件中,Windows使用的字体文件在c:\Windows\Fonts目录下,扩展名为TTF的都是矢量字库,本次使用实验使用的是新宋字体simsun.ttc,可以在资料包中下载。

.ttf 和 .ttc 文件都是字体文件格式,但它们之间存在一些关键的区别:

  • .ttf (TrueType Font)
    • TrueType 字体是一种常见的计算机字体类型,由苹果公司和微软共同开发。
    • 这种格式的字体文件通常包含一个单独的字体样式。例如,你可能会有一个文件用于常规样式,另一个文件用于粗体样式等。
    • .ttf 文件可以跨平台使用,在 Windows、macOS 以及许多其他操作系统上都可以被支持。
  • .ttc (TrueType Collection)
    • TrueType Collection 是一种特殊的字体文件格式,它允许将多个 TrueType 字体打包到一个单一的文件中。
    • 在 TTC 文件内,你可以找到多个字体变体(如常规、斜体、粗体等),这有助于减少文件大小并提高加载效率,因为相关联的字体数据会被共享。
    • 使用 TTC 可以节省磁盘空间,并且在某些情况下可以加快字体加载速度,因为它减少了需要读取的文件数量。

.ttf 文件是单个字体样式的标准容器,而 .ttc 文件则是一个更高效的格式,能够在一个文件中封装多款相关的字体样式。对于用户而言,安装 .ttc 文件就像安装普通的 .ttf文件一样简单,但是背后的数据结构更为紧凑和优化。

freetype对.ttf和.ttc字体格式都支持:

在这里插入图片描述

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

在这里插入图片描述

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

一个文字的显示过程可以概括如下(参考4.2 代码分析):

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

6.1 在LCD上显示一个矢量字体

在LCD上显示一个矢量字体的源码:

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

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

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

6.1.1 使用wchar_t获得字符的UNICODE值

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

char *str = “中”;

如果想在代码中能直接使用UNICODE值,需要使用wchar_t,宽字符,示例代码如下:

01 #include <stdio.h>
02 #include <string.h>
03 #include <wchar.h>
04
05 int main( int argc, char** argv)
06 {
07 		wchar_t *chinese_str = L"中 gif";
08 		unsigned int *p = (wchar_t *)chinese_str;
09 		int i;
10
11 		printf("sizeof(wchar_t) = %d, str's Uniocde: \n", (int)sizeof(wchar_t));
12 		for (i = 0; i < wcslen(chinese_str); i++)
13 		{
14 			printf("0x%x ", p[i]);
15 		}
16 		printf("\n");
17
18 		return 0;
19 }

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
6.1.2 使用freetype得到位图

使用 freetype 得到一个字符的位图,需要 4 个步骤:

在这里插入图片描述

  • 初始化freetype库

    158 error = FT_Init_FreeType( &library ); /* initialize library */
    
  • 加载字体文件,保存在&face中

    161 error = FT_New_Face( library, argv[1], 0, &face ); /* create face object */
    162 /* error handling omitted */
    163 slot = face->glyph;
    

    第 163 行是从 face 中获得 FT_GlyphSlot,后面的代码中文字的位图就是保存在 FT_GlyphSlot 里。

  • 设置字体大小

    165 FT_Set_Pixel_Sizes(face, font_size, 0);
    
  • 根据编码值得到位图

    使用 FT_Load_Char 函数,就可以实现这 3 个功能:

    • 根据编码值获得 glyph_index: FT_Get_Char_Index
    • 根据 glyph_idex 取出 glyph: FT_Load_Glyph
    • 渲染出位图: FT_Render_Glyph
    175 /* load glyph image into the slot (erase previous one) */
    176 error = FT_Load_Char( face, chinese_str[0], FT_LOAD_RENDER );
    

    执行 FT_Load_Char 之后,字符的位图被存在 slot->bitmap 里,即 face->glyph->bitmap。

6.1.3 在屏幕上显示位图

位图里的数据格式是怎样的?参考 3.1 example1.c 的代码,可以得到下图:

在这里插入图片描述

在屏幕上显示出这些位图:

183 draw_bitmap( &slot->bitmap,
184 			 var.xres/2,
185 			 var.yres/2);

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

在这里插入图片描述

6.1.4 交叉编译代码

IMX6ULL使用如下命令编译:

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

如果提示以下错误:

freetype_show_font.c:12:10: fatal error: ft2build.h: No such file or directory
	#include <ft2build.h>
		^~~~~~~~~~~~
compilation terminated.

之前编译出 freetype 后,得到的 ft2build.h 是位于 freetype2 目录里,把整个 freetype2 目录复制进了工具链里。

但是包括头文件时,用的是“ #include <ft2build.h>”,要么改成:

#include <freetype2/ft2build.h>

要么把工具链里 incldue/freetype2/*.h 复制到上一级目录,使用这种方法:跟 freetype 文档保持一致。执行以下命令:

cd /home/book/100ask_imx6ull-sdk/ToolChain/arm-buildroot-linux-gnueabihf_sdk-buildroot/bin/../lib/gcc/arm-buildroot-linux-gnueabihf/7.5.0/include
mv freetype2/* ./

再次执行以下命令:

arm-buildroot-linux-gnueabihf-gcc -o freetype_show_font freetype_show_font.c -lfreetype
6.1.5 测试

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

./freetype_show_font ./simsun.ttc

或者

./freetype_show_font ./simsun.ttc 300

在这里插入图片描述

在这里插入图片描述

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

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

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

“在LCD上令矢量字体旋转某个角度源码“和“在LCD上显示一个矢量字体的源码”对比:

在这里插入图片描述

在这里插入图片描述

6.2.1 代码分析

在这里插入图片描述

在这里插入图片描述

6.2.2 交叉编译代码

IMX6ULL使用如下命令编译:

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

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

./freetype_show_font_angle ./simsun.ttc 90 200

在这里插入图片描述

在这里插入图片描述

6.3 使用freetype显示一行文字

6.3.1 使用freetype显示一行文字的方法

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

在这里插入图片描述

6.3.1.1笛卡尔坐标系

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

从下图可知, X 方向坐标值是一样的。

在 Y 方向坐标值需要换算,假设 LCD 的高度是 V。

在 LCD 坐标系中坐标是(x, y),那么它在笛卡尔坐标系中的坐标值为(x, V-y)。

反过来也是一样的,在笛卡尔坐标系中坐标是(x, y),那么它在 LCD 坐标系中坐标值为(x, V-y)。

在这里插入图片描述

6.3.1.2 每个字符的大小可能不同

在使用 FT_Set_Pixel_Sizes 函数设置字体大小时,这只是“期望值”。比如“百问网 www.100ask.net”,如果把“ .”显示得跟其他汉字一样大,不好看。

所以在显示一行文字时,后面文字的位置会受到前面文字的影响。

freetype 字体的尺寸(freetype Metrics),参考 4.2 代码分析中官方freetype教程中“Managing Glyphs”教程:

在这里插入图片描述

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

在 baseline 上,每一个字符都有它的原点(origin),比如上图中 baseline左边的黑色圆点就是字母“ g”的原点。当前 origin 加上 advance 就可以得到下一个字符的 origin,比如上图中 baseline 右边的黑色圆点。在显示一行中多个文件字时,后一个文字的原点依赖于前一个文字的原点及 advance。

字符的位图是有可能越过 baseline 的,比如上图中字母“ g”在 baseline下方还有图像。

上图中红色方框内就是字母“g”所点据的位图,它的四个角落不一定与原点重合。

上图中那些xMin、xMax、yMin、yMax如何获得?可以使用FT_Glyph_Get_CBox函数获得一个字体的这些参数,将会保存在一个FT_BBox结构体中,以后想计算一行文字的外框时要用到这些信息:

在这里插入图片描述

6.3.1.3 在指定位置显示一行文字

要显示一行文字时,每一个字符都有自己外框: xMin、 xMax、 yMin、 yMax。把这些字符的 xMin、 yMin 中的最小值取出来,把这些字符的 xMax、 yMax 中的最大值取出来,就可以确定这行文字的外框了。

如下图,在指定位置(x, y)显示一行文字:

在这里插入图片描述

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

参考4.2代码分析中官方freetype文档

FT_Library:

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

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

FT_Face:

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

为什么称之为 face?

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

代码如下:

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

FT_GlyphSlot:

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

在这里插入图片描述

一个 face 中有很多字符,生成一个字符的点阵位图时,位图保存在哪里?保存在插槽中: face->glyph。

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

代码如下:

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

FT_Glyph:

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

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

error = FT_Get_Glyph(slot , &glyph);

FT_BBox:

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

在这里插入图片描述

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

FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_TRUNCATE, &bbox );

示例代码:

在这里插入图片描述

6.3.2 使用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;

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

6.3.3 代码分析
6.3.3.1 计算一行文字的外框

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

102 int compute_string_bbox(FT_Face face, wchar_t *wstr, FT_BBox *abbox)
103 {
104 	int i;
105 	int error;
106 	FT_BBox bbox;
107 	FT_BBox glyph_bbox;
108 	FT_Vector pen;
109 	FT_Glyph glyph;
110 	FT_GlyphSlot slot = face->glyph;
111	
112 	/* 初始化 */
113 	bbox.xMin = bbox.yMin = 32000;
114 	bbox.xMax = bbox.yMax = -32000;
115	
116 	/* 指定原点为(0, 0) */
117 	pen.x = 0;
118 	pen.y = 0;
119	
120 	/* 计算每个字符的 bounding box */
121 	/* 先 translate, 再 load char, 就可以得到它的外框了 */
122 	for (i = 0; i < wcslen(wstr); i++)
123 	{
124 		/* 转换: transformation */
125 		FT_Set_Transform(face, 0, &pen);
126		
127 		/* 加载位图: load glyph image into the slot (erase previous one) */
128 		error = FT_Load_Char(face, wstr[i], FT_LOAD_RENDER);
129 		if (error)
130 		{
131 			printf("FT_Load_Char error\n");
132 			return -1;
133 		}
134		
135 		/* 取出 glyph */
136 		error = FT_Get_Glyph(face->glyph, &glyph);
137 		if (error)
138 		{
139 			printf("FT_Get_Glyph error!\n");
140 			return -1;
141 		}
142		
143 		/* 从 glyph 得到外框: bbox */
144 		FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_TRUNCATE, &glyph_bbox);
145		
146 		/* 更新外框 */
147 		if ( glyph_bbox.xMin < bbox.xMin )
148 			bbox.xMin = glyph_bbox.xMin;
149		
150 		if ( glyph_bbox.yMin < bbox.yMin )
151 			bbox.yMin = glyph_bbox.yMin;
152		
153 		if ( glyph_bbox.xMax > bbox.xMax )
154 			bbox.xMax = glyph_bbox.xMax;
155		
156 		if ( glyph_bbox.yMax > bbox.yMax )
157 			bbox.yMax = glyph_bbox.yMax;
158		
159 		/* 计算下一个字符的原点: increment pen position */
160 		pen.x += slot->advance.x;
161 		pen.y += slot->advance.y;
162 	}
163	
164 	/* return string bbox */
165 	*abbox = bbox;
166 }
6.3.3.2 调整原点并绘制

代码如下:

169 int display_string(FT_Face face, wchar_t *wstr, int lcd_x, int lcd_y)
170 {
171 	int i;
172 	int error;
173 	FT_BBox bbox;
174 	FT_Vector pen;
175 	FT_Glyph glyph;
176 	FT_GlyphSlot slot = face->glyph;
177	
178 	/* 把 LCD 坐标转换为笛卡尔坐标 */
179 	int x = lcd_x;
180 	int y = var.yres - lcd_y;
181	
182 	/* 计算外框 */
183 	compute_string_bbox(face, wstr, &bbox);
184	
185 	/* 反推原点 */
186 	pen.x = (x - bbox.xMin) * 64; /* 单位: 1/64 像素 */
187 	pen.y = (y - bbox.yMax) * 64; /* 单位: 1/64 像素 */
188	
189 	/* 处理每个字符 */
190 	for (i = 0; i < wcslen(wstr); i++)
191 	{
192 		/* 转换: transformation */
193 		FT_Set_Transform(face, 0, &pen);
194		
195 		/* 加载位图: load glyph image into the slot (erase previous one) */
196 		error = FT_Load_Char(face, wstr[i], FT_LOAD_RENDER);
197 		if (error)
198 		{
199 			printf("FT_Load_Char error\n");
200 			return -1;
201 		}
202		
203 		/* 在 LCD 上绘制: 使用 LCD 坐标 */
204 		draw_bitmap( &slot->bitmap,
205 				slot->bitmap_left,
206 				var.yres - slot->bitmap_top);
207		
208 		/* 计算下一个字符的原点: increment pen position */
209 		pen.x += slot->advance.x;
210 		pen.y += slot->advance.y;
211 	}
212	
213 	return 0;
214 }
6.3.4 交叉编译代码

IMX6ULL使用如下命令编译:

arm-buildroot-linux-gnueabihf-gcc -o show_line show_line.c -lfreetype
6.3.5 测试

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

./freetype_show_font_angle ./simsun.ttc 90 200

在这里插入图片描述

在这里插入图片描述

基于freetyp的基础使用,可以进行更复杂的LCD操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

studyingdda

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

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

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

打赏作者

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

抵扣说明:

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

余额充值