[FreeType]提取、绘制字体的字形轮廓

C/C++第三方开源库的介绍和相关讨论
头像
523066680
Administrator
Administrator
帖子: 347
注册时间: 2016年07月19日 12:14
拥有现金: 锁定
储蓄: 锁定
Has thanked: 32 times
Been thanked: 31 times
联系:

[FreeType]提取、绘制字体的字形轮廓

帖子 #1 523066680 » 2016年11月12日 10:00

1楼:最小示例代码、坐标解读
2楼:坐标解读、完善输出
2楼:带错误判断的示例代码
3楼:

Code: [全选] [展开/收缩] [Download] (Simple_Outline.c)
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <ft2build.h>
  4. #include <freetype/ftoutln.h>
  5. #include FT_FREETYPE_H
  6.  
  7. FT_Library      library;
  8. FT_Face         face;
  9. FT_GlyphSlot    slot;
  10. FT_Error        error;
  11. FT_Outline      outline;
  12.  
  13. void ftinit(void)
  14. {
  15.     char*   filename;
  16.     filename            = "C:/windows/fonts/consola.ttf";
  17.     FT_Init_FreeType( &library );                //初始化库实例(句柄)
  18.     FT_New_Face( library, filename, 0, &face );  //初始化face对象,设置字体
  19. }
  20.  
  21. void LoadGlyph(FT_ULong symbol)
  22. {
  23.     //获取符号对应的索引,可以是unicode编码值(如果该字体支持)
  24.     FT_UInt index = FT_Get_Char_Index(face, symbol);
  25.  
  26.     //装载字符
  27.     FT_Load_Glyph(face,
  28.                   index,
  29.                   FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP);
  30.  
  31.     //装载字符到face后,可以从字形槽获取点阵信息,也可以获取轮廓信息
  32.     slot = face->glyph;                 //字形槽
  33.     outline = slot->outline;            //轮廓对象
  34. }
  35.  
  36. int
  37. main( int argc, char** argv )
  38. {
  39.  
  40.     ftinit();
  41.     LoadGlyph( 'a' );
  42.    
  43.     printf("n_contours: %d \n n_points: %d \n flags: %d\n",
  44.                         outline.n_contours,          //轮廓曲线的数量
  45.                         outline.n_points,            //坐标点数量
  46.                         outline.flags                //暂时不确定含义
  47.                     );
  48.  
  49.     for (int i=0; i<outline.n_contours; i++  )
  50.     {
  51.         printf("Contour %d: %d\n", i, outline.contours[i]);
  52.     }
  53.     printf("\n");
  54.  
  55.     for (int i = 0; i<outline.n_points ; i++)
  56.     {
  57.         printf("%2d : %4d %4d %d\n",
  58.                                 i,
  59.                                 outline.points[i].x,
  60.                                 outline.points[i].y,
  61.                                 outline.tags[i]
  62.                             );
  63.     }
  64.  
  65.     FT_Done_Face        ( face );
  66.     FT_Done_FreeType( library );
  67.  
  68.     return 0;
  69. }


单纯的提取,没有错误判断
编译参考(其中-lpng -lz不是必须的)

gcc -std=c11 Simple_Outline.c -o Simple_Outline ^
-ID:/lib/freetype-2.7/include ^
-LD:/lib/freetype-2.7/objs/.libs ^
-lfreetype -lpng -lz


输出结果
Code: [全选] [展开/收缩] [Download] (Untitled.txt)
  1. n_contours: 2
  2.  n_points: 48
  3.  flags: 0
  4. Contour 0: 32
  5. Contour 1: 47
  6.  
  7.  0 :  805    0 1
  8.  1 :  801  135 1
  9.  2 :  719   54 0
  10.  3 :  550  -18 0
  11.  4 :  457  -18 1
  12.  5 :  371  -18 0
  13.  6 :  249   26 0
  14.  7 :  170  103 0
  15.  8 :  133  207 0
  16.  9 :  133  268 1
  17. 10 :  133  419 0
  18. 11 :  358  590 0
  19. 12 :  578  590 1
  20. 13 :  786  590 1
  21. 14 :  786  678 1
  22. 15 :  786  767 0
  23. 16 :  672  874 0
  24. 17 :  555  874 1
  25. 18 :  470  874 0
  26. 19 :  305  836 0
  27. 20 :  217  801 1
  28. 21 :  217  958 1
  29. 22 :  250  970 0
  30. 23 :  331  993 0
  31. 24 :  421 1011 0
  32. 25 :  519 1022 0
  33. 26 :  569 1022 1
  34. 27 :  660 1022 0
  35. 28 :  806  982 0
  36. 29 :  907  900 0
  37. 30 :  961  776 0
  38. 31 :  961  692 1
  39. 32 :  961    0 1
  40. 33 :  786  457 1
  41. 34 :  565  457 1
  42. 35 :  500  457 0
  43. 36 :  406  431 0
  44. 37 :  346  383 0
  45. 38 :  317  316 0
  46. 39 :  317  274 1
  47. 40 :  317  245 0
  48. 41 :  335  192 0
  49. 42 :  375  151 0
  50. 43 :  439  127 0
  51. 44 :  485  127 1
  52. 45 :  545  127 0
  53. 46 :  700  200 0
  54. 47 :  786  279 1

这些坐标画出来是这样子的:
simple2.png
simple2.png (11.06 KiB) 查看 5135 次
simple2.png
simple2.png (11.06 KiB) 查看 5135 次

左边是点绘,右边是线绘(没有划分),绿色是初始点,橙色是末尾点。正确解读请移步二楼

头像
523066680
Administrator
Administrator
帖子: 347
注册时间: 2016年07月19日 12:14
拥有现金: 锁定
储蓄: 锁定
Has thanked: 32 times
Been thanked: 31 times
联系:

FreeType Outlines 原始数据结构、轮廓坐标解读、绘制

帖子 #2 523066680 » 2016年11月12日 10:43

FreeType Outlines 数据结构、轮廓坐标解读(暂时解说 TrueType 字体)

参考官方文档
FT_Outline
顺便做翻译,不清楚的暂时保留英文

Code: [全选] [展开/收缩] [Download] (Untitled.c)
  1. typedef struct  FT_Outline_
  2. {
  3.     short       n_contours;      /* 轮廓的数量           */
  4.     short       n_points;        /* 坐标数量             */
  5.  
  6.     FT_Vector*  points;          /* 轮廓坐标(数组)     */
  7.     char*       tags;            /* 每组坐标的标签       */
  8.     short*      contours;        /* 轮廓边界位置(数组) */
  9.  
  10.     int         flags;           /* outline masks        */
  11.  
  12. } FT_Outline;


解释
n_contours
轮廓的数量,比如字符a和o,就分为内外两个轮廓

points
坐标,数组,长度为 n_points。FT_Vector是顶点类型,提取坐标数据的时候像这样:
.points[n].x .points[n].y

tags
针对每个points的标记,数组,长度为 n_points。

If bit 0 is unset, the point is 'off' the curve, i.e., a Bézier control point,
while it is 'on' if set.

Bit 1 is meaningful for 'off' points only. If set, it indicates a third-order Bézier
arc control point; and a second-order control point if unset.

If bit 2 is set, bits 5-7 contain the drop-out mode (as defined in the OpenType
specification; the value is the same as the argument to the SCANMODE instruction).

Bits 3 and 4 are reserved for internal purposes.

contours
轮廓信息,数组,长度为 n_contours,给定每一组轮廓的边界索引。例如,第1组轮廓数据是从
索引0 - contours[0],第2组轮廓数据从 索引contours[0]+1 - contours[1],以此类推。

flags
A set of bit flags used to characterize the outline and give hints to the scan-converter
and hinter on how to convert/grid-fit it. See FT_OUTLINE_XXX.


以字符a为例
n_contours: 2
n_points: 48
Contour[0] = 32
Contour[1] = 47

第一条轮廓的顶点坐标索引为 0 - 32
第二条轮廓的顶点坐标索引为 33 - 47

伪代码
Code: [全选] [展开/收缩] [Download] (Untitled.c)
  1. for (int c = 0; c < outline.n_contours; c++ )
  2. {
  3.     int i = (c == 0 ? 0 : outline.contours[c-1]+1 );
  4.     根据给出的坐标连线;
  5.     for (; i<=outline.contours[c] ; i++)
  6.     {
  7.         坐标( outline.points[i].x, outline.points[i].y );
  8.     }
  9.     绘线结束;
  10. }

line.png
line.png (3.05 KiB) 查看 5125 次
line.png
line.png (3.05 KiB) 查看 5125 次

但是这里仅仅是直线连接,二次曲线就相对麻烦点了。借用FontForge查看字符a的轮廓(consola.ttf)

FontForge.png
FontForge.png (19.21 KiB) 查看 5115 次
FontForge.png
FontForge.png (19.21 KiB) 查看 5115 次

其中有绿褐色向上箭头的是单个轮廓的起点,圆点和 x 标记分别表示Bezier曲线上的定点和控制点。

在FontForge 字符轮廓编辑界面,右键点击某个顶点->GetInfo 可以查看坐标。
其中实心三角形、方形和实心圆形的坐标在 outlines.points 中已经给出,而空心圆形的坐标则需要
自己计算(空心圆的坐标恰好是两个相邻点的中点)。

参考 Freetype初探 - 了解TrueType Outlines 和 PostScript Outlines
Truetype 字体使用2次贝塞尔曲线和直线线段描绘字体轮廓,2次Bezier曲线分为2个定点和1个控制点

正确的坐标解读方式为:
Code: [全选] [展开/收缩] [Download] (Untitled.txt)
  1. 2次Bezier曲线简写为 B+编号 的形式
  2. 编号  x    y   tags值
  3. =======================================
  4.  0 :  805    0 1      起点
  5.  1 :  801  135 1      直线点,B1起点
  6.  2 :  719   54 0      B1控制点
  7.                       求出2, 3的中间点,作为B1终点、B2起点
  8.  3 :  550  -18 0      B2控制点
  9.  4 :  457  -18 1      B2终点,B3起点
  10.  5 :  371  -18 0      B3控制点
  11.                       求出5, 6的中间点,作为B3终点、B4起点
  12.  6 :  249   26 0      B4控制点
  13.                       求出6, 7的中间点,作为B4终点、B5起点
  14.  7 :  170  103 0
  15.                       …… 以此类推
  16.  8 :  133  207 0
  17.  9 :  133  268 1
  18.  
  19. 从上面可以发现,每出现一个0(tags)都代表一节Bezier曲线的控制点,当出现连续的0的时候,
  20. 曲线的定点需要自己通过相邻的点坐标求出。
  21.  
  22.  (表达有不对的地方还请多多指教)

头像
523066680
Administrator
Administrator
帖子: 347
注册时间: 2016年07月19日 12:14
拥有现金: 锁定
储蓄: 锁定
Has thanked: 32 times
Been thanked: 31 times
联系:

接2楼,OpenGL2.1 绘制 FreeFype Outlines 轮廓线

帖子 #3 523066680 » 2016年11月14日 16:55

使用 OpenGL 库(2.1) 绘制 FreeFype Outlines 导出的轮廓线

在GetDatafromOutline函数中提取和整理坐标数据,在display函数中绘制。
2次曲线没有自己写函数计算,而是使用gl2.1自带的求值器glMap,只要做一些初始设置并丢3个坐标信息到函数里就可以了。

Code: [全选] [展开/收缩] [Download] (Untitled.cpp)
  1. // Code By: 523066680
  2. //    Date: 2016-11
  3.  
  4. #include <GL/glut.h>
  5. #include <unistd.h>
  6. #include <stdio.h>
  7.  
  8. #include <ft2build.h>
  9. #include <freetype/ftbbox.h>
  10. #include <freetype/ftoutln.h>
  11. #include FT_FREETYPE_H
  12.  
  13. #define SIZE_X 500
  14. #define SIZE_Y 500
  15.  
  16. int winID;
  17. FT_Library      library;
  18. FT_Face         face;
  19.  
  20. FT_GlyphSlot    slot;
  21. FT_Error        error;
  22. FT_Outline      outline;
  23.  
  24. float curves[100][9];
  25. float lines[100][2];
  26.  
  27. int ci;
  28. int li;
  29. long code = 97;
  30.  
  31. void GetDatafromOutline(void);
  32. void LoadGlyph(long symbol);
  33.  
  34. void display(void)
  35. {
  36.     int    i, j;
  37.     // 清理颜色缓冲区
  38.     glClear( GL_COLOR_BUFFER_BIT );
  39.     glColor3f(1.0, 1.0, 1.0);
  40.     glMapGrid1f(20, 0.0, 1.0);
  41.  
  42.     for (int c = 0; c < ci; c++)
  43.     {
  44.         glMap1f(GL_MAP1_VERTEX_3, 0.0, 1.0, 3, 3, curves[c]);
  45.         glEvalMesh1(GL_LINE, 0, 20);
  46.     }
  47.  
  48.     glBegin(GL_LINES);
  49.     for (int n = 0; n < li; n++)
  50.     {
  51.         glVertex2fv( lines[n] );
  52.     }
  53.     glEnd();
  54.  
  55.     glutSwapBuffers();
  56. }
  57.  
  58. void idle(void)
  59. {
  60.     usleep(100000);
  61.     glutPostRedisplay();
  62. }
  63.  
  64. void reshape(int Width, int Height)
  65. {
  66.     const float fa = 32.0;
  67.     const float half = 1500.0;
  68.  
  69.     glViewport(0, 0, Width, Height);     //视口范围
  70.     glMatrixMode(GL_PROJECTION);              // 投影视图矩阵
  71.     glLoadIdentity();
  72.     glOrtho(-half, half, -half, half, -10.0, 100.0);
  73.     glMatrixMode(GL_MODELVIEW);               // 模型视图矩阵
  74.     glLoadIdentity();
  75.     gluLookAt(0.0,0.0,fa, 0.0,0.0,0.0, 0.0,1.0,fa);  //设置观察点
  76.             // 观察点,   朝向的坐标, 观察点向上坐标
  77. }
  78.  
  79. void keypress(unsigned char key, int mousex, int mousey)
  80. {
  81.     switch (key)
  82.     {
  83.         case 'q':
  84.             glutDestroyWindow(winID);
  85.             exit(0);
  86.             break;
  87.         case 'Q':
  88.             glutDestroyWindow(winID);
  89.             exit(0);
  90.             break;
  91.         case 'a':
  92.             code++;
  93.             LoadGlyph(code);
  94.             GetDatafromOutline();
  95.             glutPostRedisplay();
  96.             break;
  97.     }
  98. }
  99.  
  100. void init(void)
  101. {
  102.     glClearColor(0.0, 0.0, 0.0, 0.0);
  103.     glLineWidth( 2.0 );
  104.     glPointSize( 2.0 );
  105.     glEnable(GL_POINT_SMOOTH);
  106.     glEnable(GL_LINE_SMOOTH);
  107.     glEnable(GL_MAP1_VERTEX_3);
  108. }
  109.  
  110. void ftinit(void)
  111. {
  112.     char*   filename;
  113.     filename            = "C:/windows/fonts/consola.ttf";
  114.  
  115.     error = FT_Init_FreeType( &library );
  116.     error = FT_New_Face( library, filename, 0, &face );  /* create face object */
  117.  
  118.     slot = face->glyph;
  119. }
  120.  
  121. void LoadGlyph(long symbol)
  122. {
  123.     //这里可以是unicode编码值,字体必须支持才行
  124.     FT_UInt index = FT_Get_Char_Index(face, symbol);
  125.     FT_Error error = FT_Load_Glyph(face,
  126.                                    index,
  127.                                    FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP);
  128.     outline = slot->outline;
  129. }
  130.  
  131. void GetDatafromOutline(void)
  132. {
  133.     ci = 0;
  134.     li = 0;
  135.     int bgn;
  136.     int next;
  137.  
  138.     for (int cts = 0; cts < outline.n_contours ; cts++ )
  139.     {
  140.         bgn = cts == 0 ? 0 : outline.contours[cts-1] + 1;
  141.  
  142.         for (int i = bgn; i <= outline.contours[cts] ; i++)
  143.         {
  144.             //i==终点时,next回到某个轮廓的起点
  145.             next = ( i == outline.contours[cts] ? bgn : i+1 );    
  146.  
  147.             if ( outline.tags[i] == 0 )
  148.             {
  149.                 curves[ci][2] = 0.0;
  150.                 curves[ci][3] = (float)outline.points[i].x;    //控制点
  151.                 curves[ci][4] = (float)outline.points[i].y;
  152.                 curves[ci][5] = 0.0;
  153.                 curves[ci][8] = 0.0;
  154.  
  155.                 if ( outline.tags[i-1] == 1 )            //1, 0
  156.                 {
  157.                     curves[ci][0] = (float)outline.points[i-1].x;
  158.                     curves[ci][1] = (float)outline.points[i-1].y;
  159.                     if (outline.tags[next] == 1)         //1, 0, 1      
  160.                     {
  161.                         curves[ci][6] = (float) outline.points[next].x;
  162.                         curves[ci][7] = (float) outline.points[next].y;
  163.                     }
  164.                     else if ( outline.tags[next] == 0 )  //1, 0, 0
  165.                     {
  166.                         curves[ci][6] = (float)(outline.points[i].x + outline.points[next].x)/2;
  167.                         curves[ci][7] = (float)(outline.points[i].y + outline.points[next].y)/2;
  168.                     }
  169.                 }
  170.                 else                                     //0, 0
  171.                 {
  172.                     curves[ci][0] = curves[ci-1][6];     //起点为上一段曲线的终点
  173.                     curves[ci][1] = curves[ci-1][7];
  174.                     if ( outline.tags[next] == 0 )       //0, 0, 0
  175.                     {
  176.                         curves[ci][6] = (float)(outline.points[i].x + outline.points[next].x)/2 ;
  177.                         curves[ci][7] = (float)(outline.points[i].y + outline.points[next].y)/2 ;
  178.                     }
  179.                     else                                 //0, 0, 1
  180.                     {
  181.                         curves[ci][6] = (float)outline.points[next].x ;
  182.                         curves[ci][7] = (float)outline.points[next].y ;
  183.                     }
  184.                 }
  185.                 ci++;
  186.             }
  187.             else
  188.             {
  189.                 //直线线段
  190.                 if ( outline.tags[next] == 1 )           //1, 1
  191.                 {
  192.                     lines[li][0] = outline.points[i].x;
  193.                     lines[li][1] = outline.points[i].y;
  194.                     lines[li+1][0] = outline.points[next].x;
  195.                     lines[li+1][1] = outline.points[next].y;
  196.                     li+=2;
  197.                 }
  198.             }
  199.         }
  200.     }
  201.  
  202. }
  203.  
  204.  
  205. int main( int argc, char** argv )
  206. {
  207.  
  208.     ftinit();
  209.     LoadGlyph('m');
  210.     GetDatafromOutline();
  211.    
  212.     printf("n_contours: %d \n n_points: %d \n flags: %d\n", outline.n_contours,
  213.                         outline.n_points,
  214.                         outline.flags
  215.                     );
  216.  
  217.     glutInit(&argc, argv);
  218.                     //显示模式     双缓冲          RGBA
  219.     glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_MULTISAMPLE );
  220.     glutInitWindowSize(SIZE_X, SIZE_Y);            //窗口大小
  221.     glutInitWindowPosition(200, 200);              //位置
  222.     winID = glutCreateWindow("Fonts");       //窗口句柄
  223.     init();
  224.     glutDisplayFunc(display);                    //显示
  225.     glutKeyboardFunc(keypress);                  //按键事件响应
  226.     glutReshapeFunc(reshape);                    //窗口事件响应
  227.     glutIdleFunc(idle);                          //闲时回调函数
  228.     glutMainLoop();                              //开始主循环
  229.  
  230.     FT_Done_Face        ( face );
  231.     FT_Done_FreeType( library );
  232.  
  233.     return 0;
  234. }

curve_a.png
curve_a.png (3.24 KiB) 查看 5107 次
curve_a.png
curve_a.png (3.24 KiB) 查看 5107 次

如果你发现某个中文字体的中文显示有问题
那是因为某些汉字组成的曲线段比较多,超过100条,curves[] 数组大小不够,然后导致操作的地址跑到了lines里面去了
导致线条错乱。(这分明是因为我的代码不好)

头像
523066680
Administrator
Administrator
帖子: 347
注册时间: 2016年07月19日 12:14
拥有现金: 锁定
储蓄: 锁定
Has thanked: 32 times
Been thanked: 31 times
联系:

FT_Outline_Get_BBox - 获取某个字形、字符轮廓的包围盒大小

帖子 #4 523066680 » 2016年11月16日 10:24

FT_Outline_Get_BBox - 获取某个字形、字符轮廓的包围盒大小

轮廓范围大小,可以自己计算,但是Freetype库已经提供了相关函数
极简示例:

Code: [全选] [展开/收缩] [Download] (Untitled.cpp)
  1. FT_UInt index = FT_Get_Char_Index(face, 'a');
  2. FT_Error error = FT_Load_Glyph(face,
  3.                                index,
  4.                                FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP);
  5. slot = face->glyph;
  6. outline = slot->outline;
  7. FT_Outline_Get_BBox( &outline, &box );
  8. printf("%d, %d, %d, %d\n", box.xMax, box.xMin, box.yMax, box.yMin);


回到 “扩展库”

在线用户

用户浏览此论坛: 没有注册用户 和 3 访客