freetype简介与测试

目录

一、简要

二、文字显示过程

三、freetype中的step1

四、在PC上测试freetype

4.1 分析main函数

4.2 修改main函数

4.3 在PC上测试

4.4 得到CBox


一、简要

点阵文件中把字母或者汉字的字模取出来在LCD上显示这个方式有个缺点,这个文字的大小就固定了不能够缩放,我们浏览器就可以放大缩小字体,这些字体就成为矢量字体,比如字母A,在矢量字体文件中存放的是若干条闭合曲线的关键点,显示的时候使用数学曲线描绘并连接关键点,然后填充曲线内部空间,这样来实现缩放字体,关键点之间的相互位置是知晓的,而这种数学曲线就称为贝塞尔曲线,贝塞尔曲线在高等数学中有讲到,但不代表我们需要复习贝塞尔曲线,我们可以通过freetype库来处理这些矢量字体,在网上可以搜索到freetype的官网:https://www.freetype.org/,在里面可以下载到源码以及相当文档

在docs/reference目录有解释说明,不过都是英文的,但是有中国牛人的翻译:https://wenku.baidu.com/view/2d24be10cc7931b765ce155b.html

字体文件怎么构造的,构造字体前,需要先描绘出来,例如在1000*1000的细格子以点阵的方式描绘,由做美工的描绘,然后具备数字功底的程序员将关键点取出来及相对位置,例如我们PC上C:\Windows\Fonts存放这各种字库文件,例如文件simsun.ttc字体,存放着很多中文还兼容ASCII,存放着每个字符的关键点和关键点之间的相对位置,这些数据在术语上称为glyph(字形)


二、文字显示过程

字符编码参考:字符编码方式

  • 给定一个文字'A':0x41,"中":可能有GBK、unicode 等编码,可以确定它的编码值
  • 根据编码值从字体文件中找到"glyph" ,(由于字体文件可以是GBK、unicode等,在字库文件前还存有字符映射表charmaps,支持多种编码)
  • 设置字体大小
  • 用某些函数把glyph里的点缩放设置的大小
  • 转换为位图点阵
  • 在LCD等显示器中显示出来

三、freetype中的step1

在docs\tutorial\step1.html介绍了怎么使用相关函数,这里只截图小部分

  • 首先包含头文件里面定义了一些宏例如FT_FREETYPE_H这个宏就在ft2build.h这个头文件中定义了

 

  • 1、初始化:FT_Init_FreeType函数

 

  • 2、初始化库:FT_Init_FreeType函数,加载字体Face,可以认为是一种平面,只可意会,a.可以从文件中加载,b也可以读到内存再从内存中加载,c其他方式加载,我们主要想从文件里面做,FT_New_Face(可以认为打开字体文件),术语上是构造了一个face,face是一个结构体

 

  • 加载face之后会得到所谓的face结构体,从结构体可以确定字体文件的信息,比如num_glyphs(字形数)、units_per_EM(比如1000*1000的方框,这个框就是EM)

  • 3、设置字体大小:有FT_Set_Char_Size和FT_Set_Pixel_Sizes两个函数

  • 4、根据字母或者汉字的ASCLL或者其他编码,去文件中找到glyph,根据编码值加载glyph,在loading之前可以选择charmap,比如给定一个中字可能有 GBK、unicode、BIG5码,charmaps支持多种编码方式,可以根据FT_Select_CharMap选择字符编码方式,如果不选择的话默认是unicode码
  • glyph_index = FT_Get_Char_Index( face, charcode ); 参数是face和字符编码,得到glyph的索引

  • 根据索引取出glyph:FT_Load_Glyph(face, glyph_index, load_flags );  转为位图:FT_Render_Glyph函数

  • 5、变换:移动、旋转,FT_Set_Transform:matrix旋转的角度,delta 偏移位置


四、在PC上测试freetype

看再多的文档也比不上写代码或者看代码来得快,写代码的过程理解会更加深刻,在docs\tutorial里面有3个例子是C++,还有C语言的例子example1.c,各个函数可以参考文档中docs\reference\ft2-base_interface.html

 

4.1 分析main函数

main函数如下

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;
}
  • 一开始FT_Init_FreeType、FT_New_Face、FT_Set_Char_Size,后面出现以下语句,FT_Load_Glyph函数会把图像存在face->glyph中,而slot称为插槽
slot = face->glyph; 
  •  对于FT_Set_Char_Size函数其中char_width、char_height:单位是1/64point,point相等于物理上的尺寸,等于1/72inch(英寸),如果char_width是100的话就是100 * 1/64 * 1/72 ,horz_resolution:单位是dpi,(dpi:dots像素,per每一个,inch英寸)即每英寸里面有多少个像素,因此字符像素为 100 * 1/64 * 1/72,假设horz_resolution为200,就是100 * 1/64 * 1/72 * 200,即每个字符的像素
  • 对于lcd来说只需要关注像素的大小,因此我们用FT_Set_Pixel_Sizes

  •  旋转角度方面的计算
  /* 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 );
  • pen指示的是笛卡尔坐标,原点是在左下角,在lcd原点一般在左上角,在字体文件或者字体函数中用的都是笛卡尔坐标,这里需要进行转换为lcd的坐标,笛卡尔与lcd的x同向,因此x' = x(lcd),y坐标下,笛卡尔与lcd的y坐标相加等于lcd宽度,其中target_height相当于宽度,单位是1/64point,因此乘上64
  /* 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;
  •  这个函数旋转角度的时候用
FT_Set_Transform( face, &matrix, &pen );
  •  这里只用了FT_Load_Char,在文档step1有解释,这个函数在这里替代了FT_Get_Char_Index、FT_Load_Glyph、FT_Render_Glyph三个函数,FT_LOAD_RENDER标志位可以立刻得到点阵,就不用FT_Render_Glyph函数来转换为点阵
error = FT_Load_Char( face, text[n], FT_LOAD_RENDER );
  •  最后把点阵由buffer存在了image中,并打印出出来
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 )
{
  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' );
  }
}

 

4.2 修改main函数

包含相关注释

...
#define WIDTH   80 //分辨率,在打印的时候体现
#define HEIGHT  80

...
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         = ( 0.0 / 360 ) * 3.14159 * 2;      /* use 25 degrees     */  //角度设置为0不旋转
  target_height = HEIGHT;

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

#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); //设置为24*24的点阵大小
#endif
  /* error handling omitted */

  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 (0,40) relative to the upper left corner  */ // 这里表示在0,40的地方显示
  pen.x = 0 * 64;//单位是1/64像素 所以乘上64
  pen.y = ( target_height - 40 ) * 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 ); //load_char之后就可以得到字符的点阵
    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;
}

draw_bitmap中参数的相关解释

    draw_bitmap( &slot->bitmap,     //得到点阵,笛卡尔左上角的坐标
                 slot->bitmap_left, //转换为点阵lcd的x坐标,两者坐标相等
                 target_height - slot->bitmap_top );//lcd的高度减去笛卡尔高度就是点阵的y坐标

 


4.3 在PC上测试

  • 先安装freetype环境
tar xjf freetype-2.4.10.tar.bz2 
./configure
make
sudo make install
gcc -o example1 example1.c  -I /usr/local/include/freetype2 -lfreetype -lm
  • 显示字母的效果,参数二为字库,参数三只能输入字符,不能是中文,因为FT_Load_Char函数会把中文当成一个字节,需要后面引入宽字符

  • 想显示中文怎么做,继续修改main函数,可以直接指定unicode码,然后FT_Load_Char得到位图,如果是这种写法:char *str = "小马";这样写需要分辨中文两个字节,字母一个字节,因此我们引入宽字符,每一个字/字母都用四个字节来表示,需要包含wchar.h头文件
#include <wchar.h>

...
  double        angle;
  int           target_height;
  int           n, num_chars;

  //int chinese_str[] = {0x97e6, 0x4e1c, 0x5c71, 0x0067}; //这样写表示一个字/字母,需要我们手动去查找unicode码
  //char *str = "小马"; 这样写需要分辨中文两个字节,字母一个字节
  wchar_t *chinese_str = L"韦gif";  //每一个字/字母都用四个字节来表示
  unsigned int *p = (wchar_t *)chinese_str; 
  int i;

	printf("Uniocde: \n");
  for (i = 0; i < wcslen(chinese_str); i++)
  {
  	printf("0x%x ", p[i]);
  }
  printf("\n");
  //return 0;

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

  for ( n = 0; n < wcslen(chinese_str); n++ ) //宽字符长度
  {
    /* set transformation */
    FT_Set_Transform( face, &matrix, &pen );

    /* load glyph image into the slot (erase previous one) */
    error = FT_Load_Char( face, chinese_str[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;
  }
  • 测试如下,指定输入字符集DBK,输出字符集为unicode,因此打印出来的为unicode码,FT_Load_Char根据unicode码就可以得到位图,然后打印显示

  • 在打印函数中修改代码打印一下行数
void
show_image( void )
{
  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' );
  }
}
...  
/* 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)的位置,反而超过了

 

4.4 得到CBox

  • 去描绘一个字符的时候,有可能超过基线,参考文档中step2

  • 因此在main函数最后,每次循环会重新指定pen的位置
    /* increment pen position */
    pen.x += slot->advance.x;
    pen.y += slot->advance.y;
  • 在文档中介绍了通过FT_Glyph_Get_CBox函数,可以得到xMax,xMin,yMax,yMin

  • 修改代码,FT_Get_Glyph把插槽slot提取出来,因为每一次循环solt都不一样,FT_Glyph_Get_CBox根据glyph找到bbox,bbox就含有相关的参数
#include FT_GLYPH_H
....
int
main( int     argc,
      char**  argv )
{
...
  FT_BBox bbox;
  FT_Glyph  glyph;
...
  for ( n = 0; n < wcslen(chinese_str); n++ )
  {
    /* set transformation */
    FT_Set_Transform( face, &matrix, &pen );

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

    error = FT_Get_Glyph( face->glyph, &glyph ); //把插槽放在glyph中
	if (error)
	{
		printf("FT_Get_Glyph error!\n");
		return -1;
	}

    FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_TRUNCATE, &bbox );//根据glyph找到bbox

    /* now, draw to our target surface (convert position) */
    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); //原点的位置,单位为1/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);

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

编译之后得到相关的参数 

  • 根据打印把字符描绘出来,字/字母可能越过基线,根据CBox可以算出整个文本中的宽度和长度,如下图绿色的方框

 

  • 9
    点赞
  • 41
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值