freetype之PC机体验

freetype之PC机体验

引入#

本节代码仓库 https://gitee.com/layty/project_for_linux/tree/master/02-freetype

一个完整的字体库由两个部分组成

  • charmap 编码,也就是索引,通过指定的编码找到具体要显示的字形,一个字体可能支持多种编码
  • glyph字形,这里指的是如何去描绘(比如贝塞尔曲线)这个具体的文字

中文教程#

FreeType2 教程的第二部分

freetype使用详解(中文)

数据结构中文

官方教程#

I. Simple Glyph Loading

II. Managing Glyphs

代码结构#

mark

API手册

I. Components and APIs

II. Public Objects and Classes

III. Internal Objects and Classes

字体概念#

mark

PC上安装#

Copy
#解压 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,直接编译报错缺少头文件

Copy
In file included from example1.c:11:0: /usr/local/include/ft2build.h:56:38: fatal error: freetype/config/ftheader.h: 没有那个文件或目录 compilation terminated.

但实际上local下是有这个文件的

Copy
ls /usr/local/include/freetype2/freetype/config ftconfig.h ftheader.h ftmodule.h ftoption.h ftstdlib.h

我们可以编译的时候用-I指定头文件路径

Copy
# 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直接库的名字没有空格

Copy
#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表示数学

Copy
# gcc -o example example1.c -I /usr/local/include/freetype2 -lfreetype -lm

运行之后提示帮助,需要一个字体文件,可以去win下的C:\Windows\Fonts找到新宋体

Copy
$ ./example usage: ./example font sample-text

这里发现屏幕看的不是很清楚,主要是因为程序里设置了显示的分辨率,也就是一行出现640个点

Copy
#define WIDTH 640 #define HEIGHT 480

我们这里修改下大小,同时取消字体的旋转,否则也显示不出来

Copy
#define WIDTH 80 #横向80个点 #define HEIGHT 90 #纵向90个点

同时也要修改显示的位置

Copy
/* 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中显示不太好,可以保存到文件然后去查看

Copy
./example simsun.ttc 123 # 保存到文件看 ./example simsun.ttc 123 >123.txt

mark

代码位置

宽字符保存显示中文#

我们以前是这么保存中文的,这里处理的时候需要判断是中文,则取两个字符,如果是英文取出一个字符,非常不方便,所以这里引入了宽字符的概念,注意:wchar_t在windows占2byte,在linux4bytes.

Copy
char * str1="中文and english"

参考的网页

Copy
#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编码

Copy
$ ./a.out 0x4e2d 0x6587 0x31 0x32 0x33

我这里保存的是utf-8的格式,如果保存的是gbk的,则需要加上指定输入文件编码

Copy
-finput-charset=GBK

接着我们用freetype打印中文,加上这里的代码

Copy
for ( n = 0; n < wcslen(chinese_str); n++ ) { error = FT_Load_Char( face, chinese_str[n], FT_LOAD_RENDER ); }

mark

坐标框架体系#

上面我们设置了字体是24*24像素的大小,那么它实际上怎么显示的呢,这里我们在代码上加入打印坐标

Copy
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)显示的

Copy
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;

但是很明显原点并不在这里

mark

为什么会这样,并且可以看出来,字符显示会到原点的下方?

可以这么理解,我们先看下如下的字母,gpq等都在下方有显示,上图也能看出a的左下起始也是在横轴40的位置左右,实际上我们可以理解为基线.

mark

为了兼容中文英文以及其他字符,实际的画图是下面这样的

mark

advance有横向的也有纵向的数值,比如斜着的时候

mark

具体的代码我们是这么体现的,在写一个字符的时候

Copy
slot = face->glyph; .... /* increment pen position */ pen.x += slot->advance.x; pen.y += slot->advance.y;

字符坐标信息获取#

那么我们如何得到字符的其他信息,比如ymax等,参考文档tutorial中可以看到Measuring the Glyph Image章节

Copy
FT_EXPORT( void ) FT_Glyph_Get_CBox( FT_Glyph glyph, FT_UInt bbox_mode, FT_BBox *acbox );

注意第一个参数是FT_Glyph,而我们之前是使用FT_GlyphSlot来转换成位图的.

mark

注意 我们提取本次字符的形状数据后,也就是获得字形槽后,上次的字形槽数据就被冲掉了,那么我们如何去获取上次的字形数据呢?

参考 2. Managing Glyph Images

Copy
#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

Copy
# 韦的宽度是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

mark

posted @   zongzi10010  阅读(1435)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示
CONTENTS