freetype 矢量字体

概念

从点阵字库里把 字母或汉字点阵 取出来在LCD上显示,缺点就是选定点阵文件后文字的大小就确定了,如8x16和16x16点阵,不能缩放,不实用(浏览器上的字体可以缩放)。

矢量字体文件
保存若干闭合曲线的关键点glyph(保证相对位置不变);
使用数学曲线(贝塞尔曲线)连接关键点;
填充内部空间(多条曲线,闭合曲线)。

参考freetype文件及 官方文档 有 使用示例 freetype2.4.10 :
http://wenku.baidu.com/view/2d24be10cc7931b765ce155b.html

文字的显示过程
1.根据文字确定它的编码值(根据GBK,unicode等)
2.根据"编码值"从字体文件(字体文件中包含多个charmaps字符映射表,如gbk,unicode等)中找到"glyph关键点"
3.设置字体大小
4.用某些函数把glyph里的点缩放为设置字体大小
5.把glyph转化为位图点阵
6.在LCD中显示。

写代码
1.初始化库:FT_Init_Freetype;
2.创建一个新的Face对象来打开一个字体文件:FT_New_Face;
3.以像素形式设置字符大小:FT_Set_Pixel_Sizes;
4.装载一个字形glyph图像,并把它转换为位图。
 FT_select_charmap(选择字符编码,默认unicode),
 FT_Get_Char_Index(获得字形索引),
 FT_Load_Glyph (从face装载字形图像到槽slot=face->glyph),
 FT_Render_Glyph(转化为位图)
 或者直接使用 FT_Load_char(face,unicode码,FT_LOAD_RENDER)。
5.移动,旋转:FT_Set_Transform。

使用

注意:LCD和笛卡尔坐标系是倒着的,字体文件和字体函数中用的都是笛卡尔坐标系,即原点在左下角;而在lcd上显示的原点坐标在左上角。
转换关系:x’=x,y’=height-y (height为lcd的y方向分辨率)。
在这里插入图片描述
我们描画字符时要指定它的原点,原点那条线称为基线,但是字母可能会超过基线。可以用FT_Glyph_Get(face->glyph,&glyph)将槽slot=face->glyph中的字体提取出来(下次FT_Load_Glyph时槽中数据会变)存在glyph变量里;FT_Glyph_Get_CBox( glyph, bbox_mode, &bbox )函数来获得glyph的参数信息。bbox变量里面包含了glyph的x_Min,x_Max,y_Min,y_Max。下一个原点的坐标,为原点坐标加上advance。
origin(0,40)x(0-23)y(37-60)advance(24,0)
origin(24,40)x(25-36)y(37-51)advance(12,0)
origin(36,40)x(38-46)y(40-56)advance(12,0)
origin(48,40)x(49-59)y(40-56)advance(12,0)
显示一行文字,就是要算出上下边界的框,用于居中显示或多行显示。
在这里插入图片描述
前面设置FT_Set_Pixel_Sizes字符大小,汉字是24x24,但是英文等字符实际可能不是24x24,这样排列更加美观。
FT_Load_char得到字体的点阵存在槽slot=face->glyph中。
glyph

  typedef struct  bdf_glyph_t_{
    char*           name;        //名字
    long            encoding;    //编码值
    unsigned short  swidth;      /* Scalable width.可扩展宽度*/
    unsigned short  dwidth;      // 设备宽度
    bdf_bbx_t       bbx;         // Glyph bounding box.
    unsigned char*  bitmap;      // 关键点点阵
    unsigned long   bpr;         // 行字节数
    unsigned short  bytes;       /* Number of bytes used for the bitmap. */
  } bdf_glyph_t;

bitmap

  typedef struct  FT_Bitmap_{
    int             rows;			//高度
    int             width;			//宽度
    int             pitch;			//跨度:即一行宽度
    unsigned char*  buffer;			//点阵地址
    short           num_grays;
    char            pixel_mode;
    char            palette_mode;
    void*           palette;
  } FT_Bitmap;

bbx

  typedef struct  bdf_bbx_t_{
    unsigned short  width;
    unsigned short  height;
    short           x_offset;
    short           y_offset;
    short           ascent;
    short           descent;
  } bdf_bbx_t;

draw_bitmap( &slot->bitmap,slot->bitmap_left,target_height - slot->bitmap_top );
slot变量中存有位图信息,LCD和笛卡尔坐标系是倒着的,x坐标不变,y坐标是高度减去y坐标,转化为lcd坐标。
用宽字符来处理中英文字符同时显示,每个字符都用四个字节表示,英文有基线中文没有。

#define WIDTH   80	//分辨率,宽度和高度
#define HEIGHT  80
unsigned char image[HEIGHT][WIDTH];		//全局数组,保存字符的点阵

//将位图信息存入数组,参数:位图地址,位图左上角的x和y坐标值
void draw_bitmap( FT_Bitmap*  bitmap,FT_Int x,FT_Int y){
  FT_Int  i, j, p, q;
  FT_Int  x_max = x + bitmap->width;
  FT_Int  y_max = y + bitmap->rows;
  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 ){	//打印image数组中的数据
  int  i, j;
  for ( i = 0; i < HEIGHT; i++ ){
  	printf("%02d", i);
    for ( j = 0; j < WIDTH; j++ )
      putchar( image[i][j] == 0 ? ' ': image[i][j] < 128 ? '+': '*' );
    putchar( '\n' );
  }
}

int main( int  argc,char**  argv ){
  //char *str = "韦gif";	不可以,这样要分辨字母(1个字节)和汉字(2个字节)
  wchar_t *chinese_str = L"韦gif";	//宽字符:要打印的字母和汉字  
  
  FT_Init_FreeType( &library );  //初始化FreeType库
  FT_New_Face( library, argv[1], 0, &face ); //创建Face对象来打开 字体文件
  FT_Set_Pixel_Sizes(face, 24, 0);	//以像素形式设置字符大小:24x24 
  slot = face->glyph;	//槽:FT_Load_Glyph是将取出的关键点glyph存在slot中
 
  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;	//确定字符原点坐标,注意是在左下角,lcd坐标在左上角
  pen.y = ( target_height - 40 ) * 64;

  for ( n = 0; n < wcslen(chinese_str); n++ ){	  
	  FT_Set_Transform( face, &matrix, &pen );	/*设置旋转矩阵*/ 
	  FT_Load_Char( face, chinese_str[n], FT_LOAD_RENDER );//三合一,加载图像进槽(删除槽中之前的)
	  FT_Get_Glyph( face->glyph, &glyph );	//
	  FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_TRUNCATE, &bbox );	
	  
	  draw_bitmap( &slot->bitmap,slot->bitmap_left,target_height - slot->bitmap_top ); /* 画出点阵(转换坐标) */
	  //打印坐标
	  printf("Unicode: 0x%x\n", chinese_str[n]);
	  printf("origin.x/64 = %d, origin.y/64 = %d\n", pen.x/64, pen.y/64);
	  printf("xMin = %d, xMax = %d, yMin = %d, yMax = %d\n", bbox.xMin, bbox.xMax, bbox.yMin, bbox.yMax);
	  printf("slot->advance.x/64 = %d, slot->advance.y/64 = %d\n", slot->advance.x/64, slot->advance.y/64);	
	 
	  pen.x += slot->advance.x;	 /* increment pen position */
	  pen.y += slot->advance.y;
  }
  show_image();
  FT_Done_Face    ( face );
  FT_Done_FreeType( library );
  return 0;
}

在lcd上显示

上面的draw_bitmap是把点阵存在image[i][j]数组里,现在直接把它写到framebuffer里就可以了。参考点阵显示修改draw_bitmap函数,不保存在数组里,直接输出到framebuffer中。

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 ( 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;
      //image[j][i] |= bitmap->buffer[q * bitmap->width + p];
      lcd_put_pixel(i, j, bitmap->buffer[q * bitmap->width + p]);
    }
  }
}

交叉编译freetype库
tar xjf freetype-2.4.10.tar.bz2
./configure --host=arm-linux  配置
make 编译
make DESTDIR=$PWD/tmp install 安装在临时目录
编译安装freetype库,复制头文件和库文件到交叉编译工具链中,arm-linux-gcc交叉编译。要在开发板上运行,还要将动态库文件复制到根文件系统。
编译出来的头文件应该放入:/usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/usr/include
sudo cp * /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/armv4t/lib -d -rf //复制到交叉编译工具链
cp so /work/nfs_root/fs_mini_mdev_new/lib -d //复制到根文件系统

编译出来的库文件应该放入:/usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/armv4t/lib
cp * /usr/local/arm/4.3.2/arm-none-linux-gnueabi/libc/usr/include -rf

arm-linux-gcc -finput-charset=GBK -fexec-charset=UTF-8 -o example1 example1.c -lfreetype -lm //指定头文件,库文件,数学库
./example1 ./simsun.ttc abc //运行指定字体文件,显示字符abc

显示多行及居中显示
1.从左显示:先描画,算出上一行边框,在描画下一行。
2.居中显示:先算出文字边框大小,再确定中心坐标并描画。在这里插入图片描述

显示多行文字,在显示单行时FT_Glyph_Get_CBox,记录该行字体的line_box_ymin和line_box_ymax行最大和最小高度,用于计算下一行的原点坐标。

	wchar_t *wstr1 = L"你好gif";
	wchar_t *wstr2 = L"www.你好123.net";
	int line_box_ymin = 10000;
	int line_box_ymax = 0;
	/* 显示矢量字体 */
	error = FT_Init_FreeType( &library );			   /* initialize library */	
	error = FT_New_Face( library, argv[1], 0, &face ); /* create face object */	
	slot = face->glyph;
	FT_Set_Pixel_Sizes(face, 24, 0);
	/* 确定第一行的座标:
	 * lcd_x = 0,lcd_y = 24
	 * 笛卡尔座标系:
	 * x = lcd_x = 0,y = var.yres - lcd_y = var.yres - 24*/
	pen.x = 0 * 64;
	pen.y = (var.yres - 24) * 64;
	for (i = 0; i < wcslen(wstr1); i++){   
	    FT_Set_Transform( face, 0, &pen);	/* set transformation */  
	    FT_Load_Char( face, wstr1[i], FT_LOAD_RENDER );	/* load glyph image into the slot (erase previous one) */
		FT_Get_Glyph( face->glyph, &glyph );		
		FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_TRUNCATE, &bbox );
		if (line_box_ymin > bbox.yMin)	//记录该行的最大最小y值
			line_box_ymin = bbox.yMin;
		if (line_box_ymax < bbox.yMax)
			line_box_ymax = bbox.yMax;		
	    draw_bitmap( &slot->bitmap,slot->bitmap_left,var.yres - slot->bitmap_top);		
		pen.x += slot->advance.x;	/* increment pen position */
		//pen.y += slot->advance.y;
	}
	/* 确定下一行的座标:
	 * lcd_x = 0,lcd_y = line_box_ymax - line_box_ymin + 24
	 * 笛卡尔座标系:
	 * x = lcd_x = 0, y = var.yres - lcd_y = var.yres - (line_box_ymax - line_box_ymin + 24)*/
	pen.x = 0 * 64;
	pen.y = (var.yres - (line_box_ymax - line_box_ymin + 24)) * 64;	

根据坐标可以把图像画出来,可以把这些文字最大的框算出来,把这串文字居中显示。

居中显示参考文章4.6高级文本渲染.
基线在原点(0,0)位置,计算字符串的边界框,优点是不需要平移字形bbox,计算出结果后再平移到正确位置。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值