主 机:VMWare--Ubuntu-16.04.2-x64-100ask
开发板:Mini2440--256M NandFlash, 2M NorFlash, 64M SDRAM, LCD-TD35;
bootlorder:u-boot1.16, Kernel:4.3.2;
编译器:arm-linux-gcc-4.3.2
2.3.4节_3_居中显示几行文字
一、源码修改
先算出长宽,再确定原点,再去描绘。
参考:
FreeType 2 Tutorial Step 2 — managing glyphs
5. Advanced text rendering: transformation + centering + kerning
a. packing & translating glyphs
b. Rendering a transformed glyph sequence
源码笔记:
/* 从wstr[]里面获得字形glyph并存入glyphs[]数组 */
int Get_Glyphs_Frm_Wstr(FT_Face face, wchar_t *wstr, TGlyph glyphs[])
{
int i;
PGlyph glyph = glyphs; /* current glyph in table */
int pen_x = 0; /* 假设开始点坐标为(笛卡尔坐标): (0, 0), 单位: 1/64point */
int pen_y = 0;
int error;
FT_GlyphSlot slot = face->glyph;
for(i = 0; i < wcslen(wstr); i++)
{
glyph->index = FT_Get_Char_Index( face, wstr[i] ); /* 根据字符串wstr的unicode码,
* 获得这个glyph的索引,即
* unicode码。
* index: n.索引;
*/
/* 存储当前 pen 的位置 */
glyph->pos.x = pen_x;
glyph->pos.y = pen_y;
/* load是把字符wstr[i]的glyph放入插槽 face->glyph,下次for()循环时再把新的
* 字符的glyph放入插槽, 插槽会被覆盖掉的。
*
* 注释1: 根据glyph->index把字符wstr[i]的glyph从face里面加载出来。
* FT_LOAD_DEFAULT: 不需要转换为位图,只需要一个原原本本的矢量数据,即glyph数据,
* 以后描绘的时候再转换为位图,这样可以节省时间。
*/
error = FT_Load_Glyph( face, glyph->index, FT_LOAD_DEFAULT );
if ( error ) continue;
/* 由于下次for()循环时再把新的字符的glyph放入插槽, 插槽会被覆盖掉的。所以,
* 把插槽中的face->glyph位图复制出来,存到数组glyphs[i].image中保留;
* 注1: FT_Glyph image;
*/
error = FT_Get_Glyph( face->glyph, &glyph->image );
if ( error ) continue;
/* translate the glyph image now, 现在转换字形图像;
* 函数说明: 如果字形图像的格式是可伸缩的,即矢量字体,则转换它。
*
* 把字符wstr[i]的位置信息存入glyph,即其对应的glyphs[i].image中;
* 这使得glyphs[i].image里面含有位置信息。
* 问题1: 位置信息是从glyph->pos,即glyphs[i].pos取出放入glyphs[i]->image->advance.x/y吗?
* 猜想1: 应该是的。
*/
FT_Glyph_Transform( glyph->image, 0, &glyph->pos );
pen_x += slot->advance.x; /* 单位:1/64point */
/* increment number of glyphs */
glyph++;
}
/* 返回值: count number of glyphs loaded, 即加载的字形计数 */
return (glyph - glyphs);
}
/* 计算宽字符串的边框的长/宽,(假设开始点坐标为(笛卡尔坐标): (0, 0)) */
void compute_string_bbox(TGlyph glyphs[], FT_UInt num_glyphs, FT_BBox *abbox)
{
FT_BBox bbox;
FT_BBox glyph_bbox;
int i;
bbox.xMin = bbox.yMin = 32000;
bbox.xMax = bbox.yMax = -32000;
for(i = 0; i < num_glyphs; i++)
{
FT_Glyph_Get_CBox(glyphs[i].image, FT_GLYPH_BBOX_TRUNCATE, &glyph_bbox);/* 像素为单位 */
if(glyph_bbox.xMin < bbox.xMin)
bbox.xMin = glyph_bbox.xMin;
if(glyph_bbox.xMax > bbox.xMax)
bbox.xMax = glyph_bbox.xMax;
if(glyph_bbox.yMin < bbox.yMin)
bbox.yMin = glyph_bbox.yMin;
if(glyph_bbox.yMax > bbox.yMax)
bbox.yMax = glyph_bbox.yMax;
}
*abbox = bbox;
}
void Draw_Glyphs(TGlyph glyphs[], FT_UInt num_glyphs, FT_Vector pen)
{
int i, error;
for(i = 0; i < num_glyphs; i++)
{
/* transform copy (this will also translate it to the correct position
*/
FT_Glyph_Transform(glyphs[i].image, 0, &pen); /*delta:相当于(0, 0)的pen*/
/* convert glyph image to bitmap (destroy the glyph copy!) */
error = FT_Glyph_To_Bitmap(
&glyphs[i].image,
FT_RENDER_MODE_NORMAL,
0, /* no additional translation */
1 ); /* destroy copy in "image" */
if ( !error ) /* 如果没有错误,则: */
{
FT_BitmapGlyph bit = (FT_BitmapGlyph)glyphs[i].image; /* 获得位图 */
draw_bitmap(&bit->bitmap,
bit->left,
var.yres - bit->top ); /* 描绘 */
FT_Done_Glyph(glyphs[i].image);
}
}
}
int main(...)
{
....
num_glyphs = Get_Glyphs_Frm_Wstr(face, wstr1, glyphs);
}
二、 问题及解析
问题1:关于源码中,坐标初始化部分的代码:
int pen_x = 0; /* start at (0,0) */
int pen_y = 0;
for(i = 0; i < wcslen(wstr); i++){
glyph->pos.x = pen_x;
glyph->pos.y = pen_y;
...
FT_Glyph_Transform( glyph->image, 0, &glyph->pos );
pen_x += slot->advance.x;
glyph++;
}
可否修改为这样初始化:
FT_Vector pen; /* untransformed origin */
...
pen.x = 0 * 64; /* start at (0,0) */
pen.y = 0 * 64;
for(i = 0; i < wcslen(wstr1); i++){
...
FT_Glyph_Transform( glyph->image, 0, &pen );
pen.x += slot->advance.x;
glyph++;
}
猜想1:应该可以的,实验一下!实验源码的名字是?
答:猜想正确,下面的附录中的源码就是上机验证成功的例子!
注释1:
函数 FT_Glyph_Transform()
Defined in FT_GLYPH_H (freetype/ftglyph.h).
FT_EXPORT( FT_Error ) FT_Glyph_Transform(FT_Glyph glyph, FT_Matrix* matrix, FT_Vector* delta);
函数 FT_Set_Transform()
Defined in FT_FREETYPE_H (freetype/freetype.h).
FT_EXPORT( void ) FT_Set_Transform(FT_Face face, FT_Matrix* matrix, FT_Vector* delta);
问题2:函数 Get_Glyphs_Frm_Wstr() 可否修改为:
int Get_Glyphs_Frm_Wstr(FT_Face face, wchar_t *wstr, TGlyph glyphs[])
{
for(i = 0; i < wcslen(wstr); i++)
{
error = FT_Load_Char( face, wstr1[i], FT_LOAD_RENDER ); /* 根据编码值加载glyph到slot */
glyphs[i].image = face->glyph 或者 slot;
FT_Glyph_Transform( glyph->image, 0, &glyph->pos ); /* 转换字符位图 */
pen_x += slot->advance.x; /* 单位:1/64point */
glyph++;
}
}
但是这样修改会对原来的程序产生一些不必要的问题吗?如果是的,则不建议如此用。
答:不可以这样改,经验证错误!glyphs[i].image 与 face->glyph 或者 slot;不是同一个类型,同名不同人!!!
三、编译与上板实验
$ arm-linux-gcc -finput-charset=GBK -o show_lines5_1 show_lines5_1.c -lfreetype -lm
$ ls
show_lines5_1 show_lines5_1.c
$ sudo cp show_lines5_1 /work/nfs_root/fs_mini_mdev_new/driver_test3/2.3freetype/02th_arm/06th_show_lines_center
# ./show_lines5_1 ../../simsun.ttc
如此,LCD中央显示: [ 百问网gif ]
[ www.100ask.org ]
源码:
1 /*
2 * 2019-05-30
3 * 目的: 在LCD上居中显示两行矢量文字;
4 */
5 #include <stdio.h>
6 #include <sys/mman.h>
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <unistd.h>
10 #include <linux/fb.h>
11 #include <string.h>
12 #include <fcntl.h>
13
14 #include <math.h>
15 #include <wchar.h>
16 #include <ft2build.h>
17 #include FT_FREETYPE_H
18 #include FT_GLYPH_H
19
20 typedef struct TGlyph_
21 {
22 FT_UInt index; /* glyph index */
23 FT_Vector pos; /* glyph origin on the baseline */
24 FT_Glyph image; /* glyph image */
25
26 } TGlyph, *PGlyph;
27
28 #define MAX_GLYPHS 100 /* 矢量字符串长度 */
29
30 int fd_fb;
31 static unsigned char *fbmem; /* fb的起始地址 */
32 static struct fb_var_screeninfo var;
33 static struct fb_fix_screeninfo fix;
34
35
36 /*
37 * color: 0x00RRGGBB
38 */
39 static unsigned short convert32to16(int color)
40 {
41 int red, green, blue;
42 red = (color>>16) & 0xff;
43 green = (color>>8) & 0xff;
44 blue = (color>>0) & 0xff;
45 return ((red>>3)<<11) | ((green>>2)<<5) | (blue>>3); //color: 565
46 }
47 /* 画点
48 * color: 32bit, 0x00RRGGBB
49 * 归根结底,是在帧缓冲器中填写数据,然后扫描显示到LCD屏幕;
50 */
51 static void fb_put_pixel(int x, int y, unsigned int color)
52 {
53 unsigned char * pen_8;
54 unsigned short *pen_16;
55 unsigned int *pen_32;
56 unsigned char *pixel_base = fbmem + (var.xres*y + x)*var.bits_per_pixel/8;
57
58 switch(var.bits_per_pixel)
59 {
60 case 8:
61 pen_8 = (unsigned char *)pixel_base;
62 *pen_8 = color;
63 break;
64 case 16:
65 pen_16 = (unsigned short *)pixel_base;
66 *pen_16 = convert32to16(color);
67 break;
68 case 32:
69 pen_32 = (unsigned int *)pixel_base;
70 *pen_32 = color;
71 break;
72 default:
73 printf("can't surpport %dbpp\n", var.bits_per_pixel);
74 break;
75 }
76 }
77
78 /* 绘制字符位图 */
79 void draw_bitmap(FT_Bitmap * bitmap, FT_Int x, FT_Int y)
80 {
81 FT_Int i, j;
82
83 for(j = y; j < y + bitmap->rows; j++)
84 for(i = x; i < x + bitmap->width; i++)
85 {
86 if(i < 0 || j < 0 || i >= var.xres || j >= var.yres)
87 continue;
88 //image[j][i] |= bitmap->buffer[(j - y)*bitmap->width + (i - x)];
89 fb_put_pixel(i, j, bitmap->buffer[(j - y)*bitmap->width + (i - x)]);
90
91 }
92 }
93
94 /* 从face里加载字符wstr[]的glyph到glyphs[] */
95 int Get_Glyphs_Frm_Wstr(FT_Face face, wchar_t * wstr, TGlyph glyphs[])
96 {
97 FT_GlyphSlot slot = face->glyph; /* a small shortcut */
98 FT_UInt glyph_index;
99 FT_Vector pen;
100 PGlyph pglyph = glyphs; /* current glyph in table */
101 int i, error;
102
103 pen.x = 0; /* 假设起始点坐标(笛卡尔坐标)为: (0, 0); */
104 pen.y = 0;
105 for(i = 0; i < wcslen(wstr); i++)
106 {
107 /* 从face中得到wstr[i]的索引值 */
108 pglyph->index = FT_Get_Char_Index( face, wstr[i] );
109
110 /* store current pen position */
111 //pglyph->pos = pen;
112 /* 根据索引值加载wstr[i]的glyph到插槽slot=face->glyph */
113 error = FT_Load_Glyph( face, pglyph->index, FT_LOAD_DEFAULT );
114 if ( error ) continue;
115 /* 把插槽slot=face->glyph复制到glyphs[i]中 */
116 error = FT_Get_Glyph( face->glyph, &pglyph->image );
117 if ( error ) continue;
118
119 /* 转换字形: 旋转0度,设置起始点坐标 */
120 FT_Glyph_Transform(pglyph->image, 0, &pen);
121
122 pen.x += slot->advance.x;
123 pglyph++;
124 }
125 return (pglyph - glyphs);
126 }
127
128 /* 计算字符串的边框的长/宽 */
129 void compute_string_bbox(TGlyph glyphs [], FT_UInt num_glyphs, FT_BBox * abbox)
130 {
131 FT_BBox bbox;
132 FT_BBox glyph_bbox;
133 int i;
134
135
136 bbox.xMin = bbox.yMin = 32000;
137 bbox.xMax = bbox.yMax = -32000;
138
139 for ( i = 0; i < num_glyphs; i++ )
140 {
141 FT_Glyph_Get_CBox( glyphs[i].image, FT_GLYPH_BBOX_PIXELS, &glyph_bbox );
142
143 if (glyph_bbox.xMin < bbox.xMin)
144 bbox.xMin = glyph_bbox.xMin;
145 if (glyph_bbox.yMin < bbox.yMin)
146 bbox.yMin = glyph_bbox.yMin;
147 if (glyph_bbox.xMax > bbox.xMax)
148 bbox.xMax = glyph_bbox.xMax;
149 if (glyph_bbox.yMax > bbox.yMax)
150 bbox.yMax = glyph_bbox.yMax;
151 }
152 *abbox = bbox;
153 }
154 /* 从一个正确的坐标开始描绘字符点阵 */
155 void Draw_Glyphs(TGlyph glyphs[], FT_UInt num_glyphs, FT_Vector start)
156 {
157 FT_Glyph image;
158 FT_Vector pen;
159 FT_BBox bbox;
160 int i, error;
161
162 for ( i = 0; i < num_glyphs; i++ )
163 {
164 /* transform copy (this will also translate it to the correct position */
165 FT_Glyph_Transform( glyphs[i].image, 0, &start );
166
167 /* convert glyph image to bitmap (destroy the glyph copy!) */
168 error = FT_Glyph_To_Bitmap(
169 &glyphs[i].image,
170 FT_RENDER_MODE_NORMAL,
171 0, /* no additional translation */
172 1 ); /* destroy copy in "image" */
173 if ( !error )
174 {
175 FT_BitmapGlyph bit = (FT_BitmapGlyph)glyphs[i].image;
176
177 draw_bitmap( &bit->bitmap,
178 bit->left,
179 var.yres - bit->top );
180
181 FT_Done_Glyph( glyphs[i].image );
182 }
183 }
184 }
185
186 int main(int argc, char **argv)
187 {
188 FT_Library library;
189 FT_Face face;
190
191 FT_Vector pen; /* untransformed origin */
192 FT_GlyphSlot slot;
193
194 int error;
195 int i;
196 wchar_t wstr1[] = L"百问网gif";
197 wchar_t wstr2[] = L"www.100ask.org";
198
199 TGlyph glyphs[MAX_GLYPHS]; /* glyphs table */
200 PGlyph pglyph; /* current glyph in table */
201 FT_UInt num_glyphs;
202
203 FT_BBox bbox;
204 int line_box_width;
205 int line_box_height;
206
207
208 if(argc != 2)
209 {
210 printf("Usage: %s <font_file>\n", argv[0]);
211 return -1;
212 }
213
214 /* 一、显示固定尺寸汉字/字符 */
215 /* 1、LCD初始化 */
216 fd_fb = open("/dev/fb0", O_RDWR);
217 if(fd_fb < 0)
218 {
219 printf("can't open /dev/fb0\n");
220 return -1;
221 }
222 if(ioctl(fd_fb, FBIOGET_VSCREENINFO, &var)) //可变参数,成功,返回0; 出错,返回-1;
223 {
224 printf("can't get var\n");
225 return -1;
226 }
227 if(ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix)) //固定参数,成功,返回0; 出错,返回-1;
228 {
229 printf("can't get fix\n");
230 return -1;
231 }
232
233
234 fbmem = (unsigned char *)mmap(NULL, fix.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
235 if(fbmem == (unsigned char *)-1)
236 {
237 printf("can't mmap fbmem\n");
238 return -1;
239 }
240
241 /* 2、清屏 */
242 memset(fbmem, 0, fix.smem_len);
243
244
245
246 /* 二、显示矢量字体 */
247 error = FT_Init_FreeType( &library ); /* 初始化 Freetype 库*/
248 if ( error )
249 {
250 printf("FT_Init_FreeType error!\n");
251 return -1;
252 }
253 error = FT_New_Face( library, argv[1], 0, &face ); /* 打开一个字体文件 */
254 if ( error )
255 {
256 printf("FT_New_Face error!\n");
257 return -1;
258 }
259 slot = face->glyph;
260 FT_Set_Pixel_Sizes(face, 24, 0); /* 设置字体大小: 24*24 Pixel */
261
262 /* 居中显示字符串wstr1[] */
263 num_glyphs = Get_Glyphs_Frm_Wstr(face, wstr1, glyphs);
264 compute_string_bbox(glyphs, num_glyphs, &bbox);
265 line_box_width = bbox.xMax - bbox.xMin;
266 line_box_height = bbox.yMax - bbox.yMin;
267
268 pen.x = (var.xres - line_box_width)/2 * 64;
269 pen.y = (var.yres - line_box_height)/2 * 64;
270 Draw_Glyphs(glyphs, num_glyphs, pen);
271
272
273 /* 居中显示字符串wstr2[] */
274 num_glyphs = Get_Glyphs_Frm_Wstr(face, wstr2, glyphs);
275 compute_string_bbox(glyphs, num_glyphs, &bbox);
276 line_box_width = bbox.xMax - bbox.xMin;
277 line_box_height = bbox.yMax - bbox.yMin;
278
279 pen.x = (var.xres - line_box_width)/2 * 64;
280 pen.y -= 24 * 64;
281 Draw_Glyphs(glyphs, num_glyphs, pen);
282
283 return 0;
284 }