freetype安装与应用编程

        点阵显示有局限性,如字形固定,大小固定,为解决这些问题,本文介绍应用freetype来显示字体。FreeType库是一个开源的、可移植的字体引擎,它功能强大,简单说就是支持字体的花样显示。

        pc上安装freetype库:
1、首先自行下载freetype压缩包,我下载的是freetype-2.4.10.tar.bz2
2、解压:tar xjf freetype-2.4.10.tar.bz2 
3、进入freetype目录:cd freetype
4、./configure
5、make
6、sudo make install

这就在pc上安装好了freetype。

        如果编写好了一个测试freetype的应用程序example.c,则编译的时候要指定头文件目录/usr/local/include/freetype2,链接freetype库和数学库。同时建议加上-finput-charset=xxx编译选项以指定输入文件字符集的编码方式,和-fexec-charset=xxx选项指定输出的可执行文件的字符集编码方式,不指定字符编码方式的话,可能会出错。如:

    gcc -finput-charset=GBK -fexec-charset=UTF-8 -o example example.c  -I /usr/local/include/freetype2 -lfreetype -lm

        之所以要指定头文件目录/usr/local/include/freetype2,是因为默认加载头文件的目录和头文件的实际存储目录有点出入。如对ftheader.h文件,其:
        默认加载目录:freetype/config/ftheader.h

        实际存储目录: freetype2/freetype/config/ftheader.h 

 

如果要在arm开发板上使用freetype显示字体,则按如下步骤安装:

1、./configure的时候要设置 host 选项:

        ./configure --host=arm-linux 

2、make

3、make DESTDIR=$PWD/tmp install //指定安装路径./tmp

        为方便编译arm开发板上跑的应用程序,可以把安装路径下产生的库文件复制到交叉编译工具链下:
即:sudo cp tmp/usr/local/lib/* /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/armv4t/lib -d -rf

      sudo cp tmp/usr/local/include/freetype2/* /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/usr/include -rf

到此,如果有一个开发板上的测试程序example.c,就可以这样编译了,不用指定头文件目录和链接库目录:
        arm-linux-gcc -finput-charset=GBK -fexec-charset=GBK -o example example.c -lfreetype -lm 

   把安装目录下生产的所有*so*文件复制到开板的根目录的lib目录下。我的根目录是网络文件系统/work/nfs_root/fs_mini_mdev_new,直接拷贝即可:
        cp tmp/usr/local/lib/*so* /work/nfs_root/fs_mini_mdev_new/lib -d

开发板的/lib/ 目录下有了这些 *so* 文件,freetype的应用程序就可以在开发板上跑了。

 

        下面就是要写一个example来测试一下freetype了。把freetype使用起来还是很简单的,调用几个freetype提供的函数就可以搞事情了。当然要有一些freetype的基础理论知识,这我就不怎么详细说了,读一下其官方文档即可。

        显示一个字符:
步骤:
0.初始化FT_Init_Freetype()
1.给定一个文字‘A’的ASCII码0x41
2.提供一个字体文件
3.根据编码值到字体文件加载对应的glyph ( glyph含有关键点,相对位置。实际上是根据字符的编码类型和编码值找到glyph,
不同的编码类型的字符,要用不同的charmap来寻找其glyph,一个charmap支持一种编码,即有多个charmap以支持多种编码)
可理解为字体文件含有charmap和字的对应glyph
4.设置字体大小
5.用某些函数把glyph里的关键点缩放为这个字体大小
6.转换为位图点阵(最终还是点阵)

7.在LCD上显示出来

下面是一个在开发板LCD上用freetype显示acsii字符的例子:

example.c:  

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

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


//在LCD上显示
/* color : 0x00RRGGBB */
void lcd_put_pixel(int x, int y, unsigned int color)
{
	unsigned char *pen_8 = fbmem+y*line_width+x*pixel_width;
	//找到对应的像素在内存中的位置,后面向其(*pen_8)写颜色值。
	//通过pen_8来操作fbmem,实现LCD显示
	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;//8位像素的话,颜色值直接赋给这个内存中即可。
			break;
		}
		case 16:
		{
			/* color : 0x00RRGGBB ,unsigned int 32位color的格式*/
			/* 565 */
			red   = (color >> 16) & 0xff;//右移16位后剩下高16位,再&0xff,又剩下低8位。即取出32位color的16-23位
			green = (color >> 8) & 0xff;//取出32color的8-15位
			blue  = (color >> 0) & 0xff;//取出32color的0-7位
			color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);//取出对应的组成565
			*pen_16 = color;
			break;
		}
		case 32:
		{
			*pen_32 = color;//32位像素也直接写即可。
			break;
		}
		default:
		{
			printf("can't surport %dbpp\n", var.bits_per_pixel);
			break;
		}
	}
}

//freetype声明的一个函数指针draw_bitmap,需要我们自己实现这个函数,根据bitmap把字形描绘出来
//draw_bitmap()函数:把字体在屏上显示,具体屏具体实现:
void
draw_bitmap( FT_Bitmap*  bitmap,     //draw_bimap()是把一个字符(一个字符对应的方框所有像素所对应的颜色值,
				     //颜色值是8位,16位等,即unsigned char,char等)写到LCD的framebuffer。
             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 ( 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>=var.xres || j>=var.yres )
        			continue;
     
      			lcd_put_pixel(i, j, bitmap->buffer[q * bitmap->width + p]);
      			//显示顺序是一列一列的显示,把i=x这一列,不同的y的这一列先显示。
      			//即顺序:(i,j,q*bitmap->width +p);  (i,j+1, (q+1)*bitmap->width +p ) //(x,y,color)
      			//bitmap->buffer[]是颜色值,lcd_put_pixel()是操作lcd的应用层缓冲区framebuffer.
 	  		//来直接显示颜色。
 	  	}
	}
}

int lcd_init(void)
{
	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;
	}
}

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

 	lcd_init();
  
  	/* 清屏: 全部设为黑色 */
	memset(fbmem, 0, screen_size);
  	
  	filename      = argv[1];                          /* first argument     */
  	text          = argv[2];                          /* second argument    */
  	num_chars     = strlen( text );

  	angle         = ( 0.0 / 360 ) * 3.14159 * 2;      /* use 25 degrees     */
  	//旋转的角度
  
  
 	//这个相当于像素数
  	target_height = var.yres;

  	error = FT_Init_FreeType( &library );              /* initialize library */
  
  	error = FT_New_Face( library, argv[1], 0, &face ); /* create face object */
 
#if 0
 	/* use 50pt at 100dpi */
  		error = FT_Set_Char_Size( face, 50 * 64, 0, 100, 0 ); /* set character size */

	/* pixels = 50 /72 * 100 = 69  */
#else
	FT_Set_Pixel_Sizes(face, 24, 0);
	
#endif
 
  	slot = face->glyph;	//先使slot指向face->glyph,下面也是通过glyph来设置slot

  	/* 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 (0,40) relative to the upper left corner  */
  	pen.x = 0 * 64;
  	pen.y = ( target_height - 40 ) * 64;  //(0,40是LCD坐标,转为笛卡尔),target_height是以dip为单位的分辨率。
  
  	//这个pen.x = 0 * 64是char_width值,是FT_Set_Char_Size()函数参数中的50*64一样,
  	//0,50都是LCD坐标,总共80*80个坐标(分辨率)。
  	//char_width与坐标值的关系:char_width=坐标值*64,char_width/64=n point,坐标值就代表第 n 
  	//个point,第n个分辨率单元。1分辨率就是1point

  	for ( n = 0; n < num_chars; n++ )	
  	{
   		 /* set transformation */
   
    		FT_Set_Transform( face, &matrix, &pen );//这里使用到了pen

    		/* load glyph image into the slot (erase previous one) */
    		error = FT_Load_Char( face, text[n], FT_LOAD_RENDER );//就会设置face->glyph,转换的位图又保存在哪里?
    		if ( error )					//位图点阵应该是保存在face->glyph.bitmap中。 
      			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;
  	}
  
    	FT_Done_Face    ( face );
  	FT_Done_FreeType( library );

  	return 0;
}

    编译:arm-linux-gcc -finput-charset=GBK -fexec-charset=GBK -o example example.c -lfreetype -lm 

测试:
1、把可执行文件放在网络文件系统:cp example /work/nfs_root/fs_mini_mdev_new/
2、运行程序时需要提供一个字体文件,我的字体文件是simsun.ttc

3、开发板上运行可执行程序:./example ./simsun.ttc https://blog.csdn.net/qq_22863733显示汉字:

显示效果:

freetype安装与应用编程_第1张图片

 

显示汉字:

main()中添加的代码:

wchar_t *chinese_str = L"个人博客";//要显示的汉字
 
 //wcslen()类似与char*类型作参数的strlen()函数,用来获取wchar_t*变量的长度(字符个数为单位,不是字节数(不包含终结符)
 for ( n = 0; n < wcslen(chinese_str); n++ )	
 {
   FT_Set_Transform( face, &matrix, &pen );//这里使用到了pen
   error = FT_Load_Char( face, chinese_str[n], FT_LOAD_RENDER );//chinese_srt[n]实际上是汉字的编码,本例是是GBK码。
	if (error)
	{
		printf("FT_Load_Char error\n");
		return -1;
	}
	
    draw_bitmap( &slot->bitmap,
                 slot->bitmap_left,
                 var.yres - slot->bitmap_top);
    pen.x += slot->advance.x;
    pen.y += slot->advance.y;
}
 pen.x = 0;
 pen.y = pen.y + 24*64;	//在新的一行显示ascii字符,pen.y 、pen.x 为笛卡尔坐标

编译、测试,效果:

freetype安装与应用编程_第2张图片

 

        freetype显示字体的重点是获得字符的编码,然后FT_Load_Char( face, chinese_str[n], FT_LOAD_RENDER );有了加载了glyph的face后,就可以吧字形draw_bitmap出来了。利用freetype实现电子书就是这样的原理,电子书文件保存了字符的编码值,编码可能是ANSI,utf-8等,可以把编码先转换为UIcode码再FT_Load_Char,或在FT_Load_Char时指定字符编码方式。这需要一些字符编码方面的知识,有机会再说吧,下面是我用freetype做的简单的电子书的效果给大家分享一下。

freetype安装与应用编程_第3张图片

 

 

你可能感兴趣的:(Linux应用编程,开源工具)