使用FreeType在电脑端显示(字母或者汉子)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
============================================================================================
一、FreeType使用详解     
参考来源:
1、freetype-doc-2.10.1.tar/freetype-doc-2.10.1/freetype-2.10.1/docs/tutorial/step1.html(文字说明)
2、freetype-2.10.1.tar/freetype-2.10.1/docs/reference/site/ft2-base_interface.html(函数定义)
3、韦东山视频:freetype介绍。
4、项目1数目相框_笔记\04\freetype-doc-2.10.1.tar\freetype-doc-2.10.1\freetype-2.10.1\docs\tutorial\example1.c(freetype-2.10.1工程自带的C代码)。


技巧:如果想更好地理解代码每个函数的定义,可以在source insight添加freetype-2.10.1.tar工程,    结合对应文档进行理解
============================================================================================
I. Simple Glyph Loading(简单字形加载)
    1. Header Files(头文件)
        Include the file named ft2build.h.(包含头文件 )
       
        #include <ft2build.h>
        #include FT_FREETYPE_H
       
    2. Library Initialization(库初始化)
       
        To initialize the FreeType library, create a variable of type FT_Library named, for example, library, and call the function FT_Init_FreeType.
        用法:为了初始化FreeType library。需要的创建一个FT_Library类型的变量,比如library。然后调用函数FT_Init_FreeType。
        FT_Init_FreeType的用法:{FT_Init_FreeType( &library );}返回值:0表示成功。返回错误码见fterrdef.h.
    3. Loading a Font Face(用三种方式,1、从一个字库文件创建Face对象;2、从From Memory创建一个Face对象;3、其他方式)
        a. From a Font File(通过调用FT_New_Face打开一个字库文件从而创建一个新的face(这个可以理解为一个平面)对象,其代表一个字体文件。)调用函数FT_New_Face{FT_New_Face( library,
                     "/usr/share/fonts/truetype/arial.ttf",
                     0,
                     &face )}参数1:FT_Library类型的变量;参数2:字库文件路径();参数3:经常使用0;参数4:为一指针,用来描述face这一个对象。返回值:成功为0.其他看对应文档
            要知道给定字体文件包含多少个面,可以将第三参数设置为-1,然后检查face->num_faces的值,该值指示字体文件中嵌入了多少个面。
        b. From Memory(不常使用)  
            调用函数FT_New_Memory_Face( library,
                            buffer,    /* first byte in memory */
                            size,      /* size in bytes        */
                            0,         /* face_index           */
                            &face );
    4. Accessing the Face Data(访问Face对象中数据)
        可以使用的face?>num_glyphs等等一些成员.方式访问其中的数据。
    5. Setting the Current Pixel Size(设置当前像素大小,即填充生成Face)
        a、使用到的函数(设置一个字符宽和高各多少像素点)定义:  
                FT_Set_Char_Size( FT_Face     face,
                                    FT_F26Dot6  char_width,
                                    FT_F26Dot6  char_height,
                                    FT_UInt     horz_resolution,
                                    FT_UInt     vert_resolution );
        参数1:  之前得到的Face;参数2:字符的高度:参数3:字符宽度;参数4:水平方向分辨率;参数5:竖直方向分辨率
        参数2、3如何设置:
             The character widths and heights are specified in 1/64th of points,A point is a physical distance, equaling 1/72th of an inch。也就是说字符高度和宽度的单位是的1/64*1/72英寸。如果第二个参数的值是0,则表明宽度和高度相等。
        参数4、5如何设置:
            The horizontal and vertical device resolutions are expressed in dots-per-inch, or dpi。也就是说每英寸里面存在多少像素。如果第二个参数0,则表明水平和竖直相等。 If both values are zero, 72 dpi is used for both dimensions.如果都为0,表明都使用72dpi。
        如何计算一个字符里面包含多少个像素点呢:
            举例说明:
            char_width= 100
            char_height= 100
            horz_resolution= 200
            vert_resolution= 200
            计算一个字符宽度由几个像素组成:100*1/64*1/72*200=4.3像素:同理计算字符高度需要多少像素。
        b、如果我们不想使用FT_Set_Char_Size,我们可以使用FT_Set_Pixel_Sizes
            函数定义:  FT_Set_Pixel_Sizes( FT_Face  face,
                                            FT_UInt  pixel_width,
                                            FT_UInt  pixel_height );
        因为使用FT_Set_Char_Size最终是得到了一个字符需要多少个像素组成,而对于使用FT_Set_Pixel_Sizes我们可以直接使用这个函数设置pixel_width和pixel_height。
    6. Loading a Glyph Image
        a. Converting a Character Code Into a Glyph Index
            知识点:
            如果我们想要得到一个字符的glyph(关键信息),我们就需要提供这个字符的(唯一的)字符码(ascii、unicode、GBK等等。)。每一个字库文件中都存在一个charmap表。我可以通过这个表把提供字符码(可能是ascii也有点可能是unicode)作为索引在字库文件中找到该字符对应的glyph。
           
            使用的函数:  FT_Get_Char_Index( FT_Face   face,
                                            FT_ULong  charcode );
        b. Loading a Glyph From the Face
            说明:            
            1、加载找到的The glyph image到 face->glyph插槽中即slot中;
            The glyph image is always stored in a special object called a glyph slot,Each face object has a single glyph slot object that can be accessed as face->glyph.每一个Face都有一个slot object,slot object用来的存储The glyph image
           
            Loading a glyph image into the slot is performed by calling FT_Load_Glyph.加载一个a glyph image到这个插槽对象中,用法如下:
            函数:FT_Load_Glyph( FT_Face   face,
                                        FT_UInt   glyph_index,
                                        FT_Int32  load_flags );
                                       
            2、将slot(face->glyph)中The glyph image转化为位图(真正的图像,也就是字符点阵)
            The field face?>glyph?>format describes the format used for storing the glyph image in the slot. If it is not FT_GLYPH_FORMAT_BITMAP, one can immediately convert it to a bitmap through FT_Render_Glyph.
            说明:如果face?>glyph?>format里面图像格式不是FT_GLYPH_FORMAT_BITMAP,那就立即将slot(face->glyph)中的图像立即的转化为a bitmap(位图),需要使用的函数:
                                        FT_Render_Glyph( FT_GlyphSlot    slot,
                                                    FT_Render_Mode  render_mode );
            参数1:face->glyph变量。
            参数2:位图的呈现模式
        c. Using Other Charmaps
            在6.a步骤中,如果我们不想使用face中charmap表格默认的索引(unicode),我们提供可能是其他编码,我们可以通过相应的函数修该对应的charmaps
                函数:error = FT_Select_Charmap(
                                                    face,               /* target face object */
                                                    FT_ENCODING_BIG5 ); /* encoding
                参数2含义:指定使用编码,这里是FT_ENCODING_BIG5
               
                那么如何确定已经打开的字库文件中包含多少charmaps,可以用以下代码:
                for ( n = 0; n < face->num_charmaps; n++ )
                        {
                            charmap = face->charmaps[n];
                            if ( charmap->platform_id == my_platform_id &&
                                    charmap->encoding_id == my_encoding_id )
                        {
                    found = charmap;
                    break;
                    }

        d. Glyph Transformations
            如果的我们需要将显示字符做一个平移或者旋转,就需要使用一个函数:
              FT_Set_Transform( FT_Face     face,
                                FT_Matrix*  matrix,
                                FT_Vector*  delta );
            参数介绍:
            参数2:指向一个matrix的指针,首先定义一个FT_Matrix的变量。对该变量进行初始化(旋转多少度。)。
            参数3:这个字符基点(原点)在定义的显示区域的位置。
              设置参数3有一个知识点:
               字库文件中的字符基点(原点)使用的是笛卡尔坐标(位于左下角)。而对于LCD而言话,其坐标原点位于屏幕的左上角,因此设置FT_Vector类型变量时。需要将字符位置进行一个转换。矢量坐标表示为1/64个像素(也称为26.6个定点数,因此需要乘以一个64)。
    7. Simple Text Rendering
        The idea is to create a loop that loads one glyph image on each iteration, converts it to a pixmap, draws it on the target surface, then increments the current pen position.(其思想是创建一个循环,在每次迭代中加载一个字形图像,将其转换为像素图,在目标表面绘制它,然后增加当前笔的位置。)
        ========================================================================================================
        a. Basic Code(基本的代码)
        ========================================================================================================
                    ... initialize library ...
                    ... create face object ...
                    ... set character size ...

                    pen_x = 300;                 //字符显示的位置
                    pen_y = 200;
                   
                   
                    for ( n = 0; n < num_chars; n++ ) //使用一个循环遍历整个所要显示的字符
                    {
                      FT_UInt  glyph_index;                                


                      /* retrieve glyph index from character code */
                      glyph_index = FT_Get_Char_Index( face, text[n] );    //循环内step1 寻找第n个字符对应的index

                      /* load glyph image into the slot (erase previous one):将的生成的image放进slot */
                      error = FT_Load_Glyph( face, glyph_index, FT_LOAD_DEFAULT );  //循环内step2,根据index找到其对应的字形轮廓
                      if ( error )
                        continue;  /* ignore errors */

                      /* convert to an anti-aliased bitmap:转化为位图(位图是点阵吗?) */
                      error = FT_Render_Glyph( face->glyph, FT_RENDER_MODE_NORMAL );  //循环内step3,将字形轮廓转化为位图
                      if ( error )
                        continue;

                      /* now, draw to our target surface */                         //循环内step4
                      my_draw_bitmap( &slot->bitmap,
                                      pen_x + slot->bitmap_left,
                                      pen_y - slot->bitmap_top );
                      /* increment pen position */
                      pen_x += slot->advance.x >> 6;                                //循环内step5
                      pen_y += slot->advance.y >> 6; /* not useful for now */
                    }
                    总结:
                    这一快代码是循环体内最基本代码,目前我的疑惑:
                    my_draw_bitmap函数如何实现(看了源码现在也不太懂(目前不知道slot->bitmap放的是什么,点阵?,那应该是一堆的0和1。但是结合后面调用的show image函数中的{putchar( image[i][j] == 0 ? ' '
                                : image[i][j] < 128 ? '+'
                                                    : '*' );},又好像不对 。现在无法解决。))
        =======================================================================================================
        b.Refined code(精简的代码)建议使用为什么有这一部分b呢(主要是与a进行对比,不同在for循环体内)
        ========================================================================================================
                    for ( n = 0; n < num_chars; n++ )
                        {
                          /* load glyph image into the slot (erase previous one) */
                          error = FT_Load_Char( face, text[n], FT_LOAD_RENDER );   //step1
                          if ( error )
                            continue;  /* ignore errors */

                          /* now, draw to our target surface */
                          my_draw_bitmap( &slot->bitmap,
                                          pen_x + slot->bitmap_left,
                                          pen_y - slot->bitmap_top );

                          /* increment pen position */
                          pen_x += slot->advance.x >> 6;
                        }
    总结:对比a. Basic Code(基本的代码)和bRefined code(精简的代码)
                a中循环内step1~step3被b中step1替代同样可以达到效果(将得到的glyph转化为位图)
    ============================================================================================
二、使用韦老师提供的freetype-2.4.10源码进行编译安装 freetype,然后运行韦老师的代码
============================================================================================
因为编写的代码代码需要调用的安装freetype所生成的库文件(包括头文件,函数的定义等等)。
自己的体会:
1、如何安装freetype。可从源码根目录中READM出发,查看安装的过程。
2、安装freetype之后,就会在/user/local(默认)下面的目录(环境变量)中生成库文件。安装文档中也有讲。
3、下面就是执行韦老师的程序了
gcc -o example1 example1.c
出错信息:/usr/local/include/ft2build.h:56:38: fatal error: freetype/config/ftheader.h: No such file or directory
但是经过查看/usr/local/include/ft2build.h中56行 #include <freetype/config/ftheader.h>。在进行查看发现/usr/local/include/freetype2/freetype/config/ftheader.h存在头文件。这表示程序未找到对应路径因此需要指定路径:gcc -o example1 example1.c -I /usr/local/include/freetype2/。也就是说在freetype2/然后执行#include <freetype/config/ftheader.h>。执行发现

example1.c:(.text+0x25b): undefined reference to `FT_Init_FreeType'
example1.c:(.text+0x284): undefined reference to `FT_New_Face'
example1.c:(.text+0x29d): undefined reference to `FT_Set_Pixel_Sizes'
example1.c:(.text+0x2c4): undefined reference to `cos'
example1.c:(.text+0x2f5): undefined reference to `sin'
example1.c:(.text+0x332): undefined reference to `sin'
example1.c:(.text+0x363): undefined reference to `cos'
example1.c:(.text+0x3b6): undefined reference to `FT_Set_Transform'
example1.c:(.text+0x3de): undefined reference to `FT_Load_Char'
example1.c:(.text+0x465): undefined reference to `FT_Done_Face'
example1.c:(.text+0x471): undefined reference to `FT_Done_FreeType'
提示是未定义,但是,我们已经包含了对应的头文件,因此程序链接的时候未找到安装freetype生成的库文件,因此的需要指定生成的库文件(注意:去掉lib前缀):
gcc -o example1 example1.c -I /usr/local/include/freetype2/ -lfreetype   执行发现:
example1.c:(.text+0x2c4): undefined reference to `cos'
example1.c:(.text+0x2f5): undefined reference to `sin'
example1.c:(.text+0x332): undefined reference to `sin'
example1.c:(.text+0x363): undefined reference to `cos'
表明链接的时候未找到对应的数学库,因此执行(不能加-lmath而是加-lm):
gcc -o example1 example1.c -I /usr/local/include/freetype2/ -lfreetype -lm                成功
然后执行./example1 BROADW.TTF abc.
执行成功
============================================================================================
三、使用韦老师提供的freetype-2.4.10源码进行编译安装 freetype,然后运行自己写的代码
============================================================================================
自己写的代码,是可以运行出结果,只能显示单个字符,如果是多个字符的话,就会导致,字符存在重合,我的代码里面已经使用
        pen.x += slot->advance.x;
        pen.y += slot->advance.y;
        者这句代码,但是实验的结果,表明第二字符之后origin(原点仍然没用移动),后面解决这个问题。