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