freetype之PC机体验
freetype之PC机体验
引入
本节代码仓库 https://gitee.com/layty/project_for_linux/tree/master/02-freetype
一个完整的字体库由两个部分组成
- charmap 编码,也就是索引,通过指定的编码找到具体要显示的字形,一个字体可能支持多种编码
- glyph字形,这里指的是如何去描绘(比如贝塞尔曲线)这个具体的文字
中文教程
官方教程
- 1. Header Files
- 2. Library Initialization
- 3. Loading a Font Face
- 4. Accessing the Face Data
- 5. Setting the Current Pixel Size
- 6. Loading a Glyph Image
- 7. Simple Text Rendering
- 1. Glyph Metrics
- 2. Managing Glyph Images
- 3. Global Glyph Metrics
- 4. Simple Text Rendering: Kerning and Centering
- 5. Advanced Text Rendering: Transformation and Centering and Kerning
- 6. Accessing Metrics in Design Font Units, and Scaling Them
- Conclusion
代码结构
API手册
II. Public Objects and Classes
- 1. Object Orientation in FreeType 2
- 2. The
FT_Library
class - 3. The
FT_Face
class - 4. The
FT_Size
class - 5. The
FT_GlyphSlot
class - 6. The
FT_CharMap
class - 7. Objects Relationship
III. Internal Objects and Classes
字体概念
PC上安装
#解压
tar xjf freetype-2.4.10.tar.bz2
#重命名
mv freetype-2.4.10 freetype-2.4.10_pc
#配置
cd freetype-2.4.10_pc/
./configure
#编译
make
#安装到根目录 /usr/local/
sudo make install
book@100ask:~/stu/repo/demo_tar/freetype-2.4.10_pc$ ls /usr/local/lib/
libfreetype.a libfreetype.so libfreetype.so.6.9.0 python2.7
libfreetype.la libfreetype.so.6 pkgconfig python3.5
book@100ask:~/stu/repo/demo_tar/freetype-2.4.10_pc$ ls /usr/local/include/
freetype2 ft2build.h
官方例子
例子在freetype-doc-2.4.10.tar.bz2\freetype-2.4.10\docs\tutorial
,直接编译报错缺少头文件
In file included from example1.c:11:0:
/usr/local/include/ft2build.h:56:38: fatal error: freetype/config/ftheader.h: 没有那个文件或目录
compilation terminated.
但实际上local
下是有这个文件的
ls /usr/local/include/freetype2/freetype/config
ftconfig.h ftheader.h ftmodule.h ftoption.h ftstdlib.h
我们可以编译的时候用-I
指定头文件路径
# gcc -o example example1.c -I /usr/local/include/freetype2
/tmp/ccNUBZti.o:在函数‘main’中:
example1.c:(.text+0x25b):对‘FT_Init_FreeType’未定义的引用
example1.c:(.text+0x284):对‘FT_New_Face’未定义的引用
example1.c:(.text+0x29d):对‘FT_Set_Pixel_Sizes’未定义的引用
example1.c:(.text+0x2c4):对‘cos’未定义的引用
example1.c:(.text+0x2f5):对‘sin’未定义的引用
example1.c:(.text+0x332):对‘sin’未定义的引用
example1.c:(.text+0x363):对‘cos’未定义的引用
example1.c:(.text+0x3b6):对‘FT_Set_Transform’未定义的引用
example1.c:(.text+0x3de):对‘FT_Load_Char’未定义的引用
example1.c:(.text+0x465):对‘FT_Done_Face’未定义的引用
example1.c:(.text+0x471):对‘FT_Done_FreeType’未定义的引用
collect2: error: ld returned 1 exit status
这里提示没有库文件,这里需要使用-l直接库的名字没有空格
#gcc -o example example1.c -I /usr/local/include/freetype2 -lfreetype
/tmp/cchpuAJt.o:在函数‘main’中:
example1.c:(.text+0x2c4):对‘cos’未定义的引用
example1.c:(.text+0x2f5):对‘sin’未定义的引用
example1.c:(.text+0x332):对‘sin’未定义的引用
example1.c:(.text+0x363):对‘cos’未定义的引用
collect2: error: ld returned 1 exit status
这里提示没有数学库,这里加上-lm
,m表示数学
# gcc -o example example1.c -I /usr/local/include/freetype2 -lfreetype -lm
运行之后提示帮助,需要一个字体文件,可以去win
下的C:\Windows\Fonts
找到新宋体
$ ./example
usage: ./example font sample-text
这里发现屏幕看的不是很清楚,主要是因为程序里设置了显示的分辨率,也就是一行出现640个点
#define WIDTH 640
#define HEIGHT 480
我们这里修改下大小,同时取消字体的旋转,否则也显示不出来
#define WIDTH 80 #横向80个点
#define HEIGHT 90 #纵向90个点
同时也要修改显示的位置
/* 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;
angle = (0.0 / 360 ) * 3.14159 * 2; /* 取消旋转*/
这个实验有时候在mobaxtem
中显示不太好,可以保存到文件然后去查看
./example simsun.ttc 123
# 保存到文件看
./example simsun.ttc 123 >123.txt
宽字符保存显示中文
我们以前是这么保存中文的,这里处理的时候需要判断是中文,则取两个字符,如果是英文取出一个字符,非常不方便,所以这里引入了宽字符的概念,注意:wchar_t在windows占2byte,在linux占4bytes.
char * str1="中文and english"
#include <stdio.h>
#include <wchar.h>
int main(int argc,char *argv[])
{
int i;
wchar_t* chinese_str = L"中文123";
unsigned int *p =(wchar_t*)chinese_str;
for(i=0;i<wcslen(chinese_str);i++)
{
printf("0x%x ",p[i]);
}
printf("\n");
}
打印显示是unicode编码
$ ./a.out
0x4e2d 0x6587 0x31 0x32 0x33
我这里保存的是utf-8
的格式,如果保存的是gbk的,则需要加上指定输入文件编码
-finput-charset=GBK
接着我们用freetype打印中文,加上这里的代码
for ( n = 0; n < wcslen(chinese_str); n++ )
{
error = FT_Load_Char( face, chinese_str[n], FT_LOAD_RENDER );
}
坐标框架体系
上面我们设置了字体是24*24
像素的大小,那么它实际上怎么显示的呢,这里我们在代码上加入打印坐标
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' );
}
}
我们再来看下我们是怎么设置显示位置的,位置是在(0,40)显示的
FT_Set_Pixel_Sizes(face,24,0)
/* 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;
但是很明显原点并不在这里
为什么会这样,并且可以看出来,字符显示会到原点的下方?
可以这么理解,我们先看下如下的字母,gpq
等都在下方有显示,上图也能看出a的左下起始也是在横轴40的位置左右,实际上我们可以理解为基线.
为了兼容中文英文以及其他字符,实际的画图是下面这样的
advance有横向的也有纵向的数值,比如斜着的时候
具体的代码我们是这么体现的,在写一个字符的时候
slot = face->glyph;
....
/* increment pen position */
pen.x += slot->advance.x;
pen.y += slot->advance.y;
字符坐标信息获取
那么我们如何得到字符的其他信息,比如ymax
等,参考文档tutorial
中可以看到Measuring the Glyph Image
章节
FT_EXPORT( void )
FT_Glyph_Get_CBox( FT_Glyph glyph,
FT_UInt bbox_mode,
FT_BBox *acbox );
注意第一个参数是FT_Glyph
,而我们之前是使用FT_GlyphSlot
来转换成位图的.
注意 我们提取本次字符的形状数据后,也就是获得字形槽后,上次的字形槽数据就被冲掉了,那么我们如何去获取上次的字形数据呢?
#include FT_GLYPH_H
FT_Glyph glyph; /* a handle to the glyph image */
...
error = FT_Load_Glyph( face, glyph_index, FT_LOAD_NORMAL );
if ( error ) { ... }
error = FT_Get_Glyph( face->glyph, &glyph ); //从之前的插槽中取出FT_Glyph
if ( error ) { ... }
//--------实际代码如下--------------------------------------------
/* 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) */
error = FT_Get_Glyph( face->glyph, &glyph );
if (error) {
printf("load from face->glyph is failed\n");
return -1;
}
FT_Glyph_Get_CBox( glyph, FT_GLYPH_BBOX_TRUNCATE, &bbox );
printf("\n\nunicode is %x\n",chinese_str[n]);
printf("lcd offset is [x]=%d,[y]=%d\n",slot->bitmap_left,target_height - slot->bitmap_top);
printf("[pen.x]=%ld,[pen.y]=%ld\n",pen.x/64,pen.y/64);
printf("glyph [xMin]=%ld,[yMin]=%ld,[xMax]=%ld,[ymax]=%ld\n",bbox.xMin,bbox.yMin,bbox.xMax,bbox.yMax);
printf("advance.x/64=%ld,y.advance/64=%ld\n",slot->advance.x/64,slot->advance.y/64);// unit is 1/64 pixel
我们可以打印出来看一下,这里加入笛卡尔的坐标打印
可以看出来这里的韦字宽度是23
,有点奇怪不是24
哈哈 不过也是在框里的
下一个字符的原点就是上一个字符的原点加上一个字符的advance
# 韦的宽度是23长度是24
unicode is 97e6
lcd offset is [x]=1,[y]=20
[pen.x]=0,[pen.y]=50
glyph [xMin]=1,[yMin]=47,[xMax]=23,[ymax]=70
advance.x/64=24,y.advance/64=0
unicode is 4e2d
lcd offset is [x]=27,[y]=20
[pen.x]=24,[pen.y]=50
glyph [xMin]=27,[yMin]=47,[xMax]=46,[ymax]=70
advance.x/64=24,y.advance/64=0
# a字母的宽度是12,长度是12
unicode is 61
lcd offset is [x]=49,[y]=29
[pen.x]=48,[pen.y]=50
glyph [xMin]=49,[yMin]=50,[xMax]=60,[ymax]=61
advance.x/64=12,y.advance/64=0