主 机: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 }