【项目1_电子书】第2.3.4课、1/2_在LCD上显示显示一行/两行矢量文字
主 机: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节_1_在LCD上显示显示一行矢量文字
在02th_arm/02th_arm/02th_lcd/show_font.c的源码基础上修改,改名show_lines.c:
一.只保留矢量字体的操作,其余无关代码删除;
1.2、函数及结构体说明
定义变量glyph:
FT_Glyph glyph; /* a handle to the glyph image */
typedef struct FT_GlyphRec_* FT_Glyph;
/* 根glyph结构体包含一个给定的glyph图像加上16.16固定浮点格式的前进宽度。
*/
typedef struct FT_GlyphRec_
{
FT_Library library;
const FT_Glyph_Class* clazz;
FT_Glyph_Format format;
FT_Vector advance;
} FT_GlyphRec;
定义变量slot:
FT_GlyphSlot slot;
typedef struct FT_GlyphSlotRec_* FT_GlyphSlot;
/* FreeType根 glyph 插槽类结构体。glyph slot是一个容器,可以在其中加载单个glyph,
* 无论它们是大纲格式还是位图格式。
*/
typedef struct FT_GlyphSlotRec_
{
FT_Library library;
FT_Face face;
FT_GlyphSlot next;
FT_UInt reserved; /* retained for binary compatibility */
FT_Vector advance;
FT_Glyph_Format format;
FT_Bitmap bitmap;
FT_Int bitmap_left;
FT_Int bitmap_top;
...
} FT_GlyphSlotRec;
定义变量face:
FT_Face face;
typedef struct FT_FaceRec_* FT_Face;
/* freetype根face类结构体。Face对象为字体文件中的字体建模。
*/
typedef struct FT_FaceRec_
{
FT_Long num_faces;
FT_Long face_index;
FT_Long face_flags;
FT_Long style_flags;
FT_Long num_glyphs;
FT_BBox bbox;
FT_GlyphSlot glyph;
/*@private begin */
/*@private end */
} FT_FaceRec;
因此,slot = face->glyph;中的glyph是FT_GlyphSlot类型变量,而一般单独定义的glyph变量是:FT_Glyph glyph;/* a handle to the glyph image */
同名不同人啊!!!不可以相互赋值!!!赋值则错误!!!
二、问题及解决
源码:
#include ...
int fd_fb;
static unsigned char *fbmem; /* fb的起始地址 */
static struct fb_var_screeninfo var;
static struct fb_fix_screeninfo fix;
/*
* color: 0x00RRGGBB
*/
static unsigned short convert32to16(int color)
{
int red, green, blue;
red = (color>>16) & 0xff;
green = (color>>8) & 0xff;
blue = (color>>0) & 0xff;
return ((red>>3)<<11) | ((green>>2)<<5) | (blue>>3); //color: 565
}
/* 画点
* color: 32bit, 0x00RRGGBB
* 归根结底,是在帧缓冲器中填写数据,然后扫描显示到LCD屏幕;
*/
static void fb_put_pixel(int x, int y, unsigned int color)
{
unsigned char * pen_8;
unsigned short *pen_16;
unsigned int *pen_32;
unsigned char *pixel_base = fbmem + (var.xres*y + x)*var.bits_per_pixel/8;
switch(var.bits_per_pixel)
{
case 8:
pen_8 = (unsigned char *)pixel_base;
*pen_8 = color;
break;
case 16:
pen_16 = (unsigned short *)pixel_base;
*pen_16 = convert32to16(color);
break;
case 32:
pen_32 = (unsigned int *)pixel_base;
*pen_32 = color;
break;
default:
printf("can't surpport %dbpp\n", var.bits_per_pixel);
break;
}
}
/* 绘制字符位图 */
void draw_bitmap(FT_Bitmap * bitmap, FT_Int x, FT_Int y)
{
FT_Int i, j;
for(j = y; j < y + bitmap->rows; j++)
for(i = x; i < x + bitmap->width; i++)
{
if(i < 0 || j < 0 || i >= var.xres || j >= var.yres)
continue;
fb_put_pixel(i, j, bitmap->buffer[(j - y)*bitmap->width + (i - x)]);
}
}
int main(int argc, char **argv)
{
FT_Library library; // typedef struct FT_LibraryRec_ *FT_Library;
// typedef struct FT_LibraryRec_{ ... } FT_LibraryRec;
FT_Face face; // typedef struct FT_FaceRec_* FT_Face;
// typedef struct FT_FaceRec_
{...
FT_GlyphSlot glyph; ... //typedef struct FT_GlyphSlotRec_* FT_GlyphSlot;
//同下面的变量slot的类型一样;
} FT_FaceRec;
FT_GlyphSlot slot; // typedef struct FT_GlyphSlotRec_* FT_GlyphSlot;
//typedef struct FT_GlyphSlotRec_
{...
FT_Library library;
FT_Face face;
FT_Glyph_Metrics metrics;
FT_Vector advance; //同下面变量pen的类型一样;
//vector:n.矢量,向量。
//advance:n.进步,进展; adj.预先的;事先的;先头部队。
FT_Glyph_Format format;
FT_Bitmap bitmap;
FT_Int bitmap_left;
FT_Int bitmap_top;
...
} FT_GlyphSlotRec;
FT_Vector pen; /* 未转换的起始点 */
typedef struct FT_Vector_
{
FT_Pos x; //typedef signed long FT_Pos;
FT_Pos y;
} FT_Vector;
FT_Matrix matrix; /* transformation matrix */
int error;
int i;
wchar_t wstr1[] = L"百问网gif";
wchar_t wstr2[] = L"www.100ask.org";
/* 一、LCD初始化 */
/* 二、显示矢量字体 */
error = FT_Init_FreeType( &library ); /* 初始化 Freetype 库*/
if ( error ) {。。。}
error = FT_New_Face( library, argv[1], 0, &face ); /* 打开一个字体文件 */
if ( error ) {。。。}
slot = face->glyph; //指针变量slot里面存放的是
FT_Set_Pixel_Sizes(face, 24, 0); /* 字体尺寸: 24x24像素 */
/* 确定起始点坐标:
* lcd_x = 0;
* lcd_y = 24;
* 笛卡尔坐标系:
* x = lcd_x = 0;
* y = var.yres - lcd_y = var.yres - 24;
*/
pen.x = 0*64;
pen.y = (var.yres - 24)*64;
for(i = 0; i < wcslen(wstr1); i++)
{
FT_Set_Transform( face, 0, &pen ); /* 设置转换参数: 旋转0度 */
/* 根据编码值加载glyph到slot */
/* load时是把glyph放入插槽face->glyph */
error = FT_Load_Char( face, chinese_str[i], FT_LOAD_RENDER );
if(error)
{
printf("FT_Load_Char error.\n");
return -1;
}
draw_bitmap( &slot->bitmap,
slot->bitmap_left,
var.yres - slot->bitmap_top );
/* 增加pen的位置*/
pen.x += slot->advance.x;
}
return 0;
}
问题1:对于每次更替pen的位置,对于slot变量并没有在for()循环中显性重新赋值,但为什么可以通过
pen.x += slot->advance.x;
pen.y += slot->advance.y;
增加pen的位置?
答:slot = face->glyph;
1.指针变量slot是结构体指针类型:struct FT_GlyphSlotRec_* ;
2.指针变量face是结构体指针类型:struct FT_FaceRec_*;
3.指针变量face->glyph成员是结构体指针类型:struct FT_GlyphSlotRec_*; 且与 slot 同一类型;
for()循环之前,slot被赋予 字形结构体指针face->glyph值,在循环中应该是被某个函数自动装载新值了。
问题2:插槽slot里面的数据是自动装载的吗?
猜想与推论1:pen.x += slot->advance.x;可以增加pen的位置,证明 advance.x/y是当下矢量
字体chinese_str[i]的尺寸(单位:像素),因此slot当中存放的是当下矢量字体chinese_str[i]
的glyph信息(包括bitmap, advance, bitmap_left,bitmap_top等及其他信息)。而slot变量并没有在for()循环中被矢量字体chinese_str[i]的 glyph 显性重新赋值,
却可以通过slot->advance.x/y来改变pen的位置,因此推论插槽slot里面的数据是自动装载的。
猜想与推论2:插槽slot里面的数据是在函数FT_Load_Char()或函数FT_Load_Glyph()中自动装载的。
函数FT_Load_Char() / FT_Load_Glyph()使用说明:
/* 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 );
而函数 error = FT_Load_Char( face, wstr1[i], FT_LOAD_RENDER ); /* 根据编码值加载glyph到slot */
的功能相当于FT_Get_Char_Index(), FT_Load_Glyph(), FT_Render_Glyph()三个函数的综合。
因此,插槽slot里面的数据应该是在函数FT_Load_Char()中也是可以自动装载的。
【注释】
1.FT_EXPORT( FT_Error ) FT_Set_Pixel_Sizes( FT_Face face, FT_UInt pixel_width, FT_UInt pixel_height );
//typedef unsigned int FT_UInt;
2.FT_EXPORT( FT_Error ) FT_Load_Char( FT_Face face, FT_ULong char_code, FT_Int32 load_flags );
//typedef unsigned long FT_ULong;
//typedef signed XXX FT_Int32;
问题3: 函数draw_bitmap( &slot->bitmap, slot->bitmap_left, var.yres - slot->bitmap_top )解析及参数含义
解析:由LCD是从帧缓冲器fb中提取数据并按照自左到右,自上到下的顺序扫描LCD可知,可知fb_put_pixel(x, y, color))的参数坐标(x, y)都是LCD坐标。
因此,draw_bitmap(bitmap, x, y)函数中的参数(x, y)都是LCD坐标,且是字体的左上角的像素做原点的坐标。
又因为draw_bitmap( &slot->bitmap, slot->bitmap_left, var.yres - slot->bitmap_top ),所以:
slot->bitmap_left = x_LCD_字体左上角,var.yres - slot->bitmap_top = y_LCD_字体左上角。
所以,slot->bitmap_top = var.yres - y_LCD_字体左上角 = y_笛卡尔坐标_字体左上角。
三、编译及上板操作
a. 从左边开始显示几行文字
$ arm-linux-gcc -finput-charset=GBK -o show_lines show_lines.c -lfreetype -lm
# nfs 32000000 192.168.0.106:/work/nfs_root/uImage_lcd_3th; bootm 32000000
# nfs 32000000 192.168.0.101:/work/nfs_root/uImage_lcd_3th; bootm 32000000
输入编码集是国标码,但是在字符串chinese_str[ ]的数组内存里面存储的是宽字符的类型,
宽字符的类型里面即Unicode码。
如果源代码是用UTF-8写的,即可不必添加 -finput-charset=GBK 参数,但是SourceInsight3
不支持UTF-8编码()。大陆简体中文默认国标码。
$ arm-linux-gcc -finput-charset=GBK -o show_lines_1 show_lines_1.c -lfreetype -lm
$ sudo cp show_lines_1 /work/nfs_root/fs_mini_mdev_new/driver_test3/2.3freetype/02th_arm/
# ./show_lines_1 ../../simsun.ttc
//LCD显示:百问网gif
==========================================================
-------------------------------------
2.3.4节_2_显示两行矢量文字
1、打印方框?
FT_Glyph glyph; /* a handle to the glyph image */
FT_BBox bbox; /* 含有每一个矢量文字的xMin, yMin; xMax, yMax;*/
for(i = 0; i < wcslen(wstr1); i++)
{
error = FT_Get_Glyph( face->glyph, &glyph );
if ( error )
{
printf("FT_Get_Glyph error!\n");
return -1;
}
FT_Glyph_Get_CBox( glyph, FT_GLYPH_BBOX_TRUNCATE, &bbox );
}
补充:结构体解析
typedef struct FT_BBox_
{
FT_Pos xMin, yMin;
FT_Pos xMax, yMax;} FT_BBox;
<Struct>
/* FT_BBox
/*
/* <Description>
/* A structure used to hold an outline's bounding box, i.e., the coordinates of its extrema in the horizontal and vertical directions.
/* 翻译:用于保存轮廓边界框的结构体,即,在水平和垂直方向上其极值的坐标。
/* outline:概述; 略述; 显示…的轮廓; 勾勒…的外形; 梗概; 轮廓线; 略图;
/* bounding: 边界;
2、修改源码:
int line_box_ymin = 10000; //笛卡尔坐标系;
int line_box_ymax = 0; //笛卡尔坐标系;
pen.x = 0 * 64;
pen.y = (var.yres - (line_box_ymax - line_box_ymin + 24)) * 64;
for(i = 0; i < wcslen(wstr1); i++)
{
error = FT_Get_Glyph( face->glyph, &glyph );
FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_TRUNCATE, &bbox );
if (line_box_ymin > bbox.yMin)
line_box_ymin = bbox.yMin;
if (line_box_ymax < bbox.yMax)
line_box_ymax = bbox.yMax;
}
源码: show_lines4.c
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 int fd_fb; 21 static unsigned char *fbmem; /* fb的起始地址 */ 22 static struct fb_var_screeninfo var; 23 static struct fb_fix_screeninfo fix; 24 25 26 /* 27 * color: 0x00RRGGBB 28 */ 29 static unsigned short convert32to16(int color) 30 { 31 int red, green, blue; 32 red = (color>>16) & 0xff; 33 green = (color>>8) & 0xff; 34 blue = (color>>0) & 0xff; 35 return ((red>>3)<<11) | ((green>>2)<<5) | (blue>>3); //color: 565 36 } 37 /* 画点 38 * color: 32bit, 0x00RRGGBB 39 * 归根结底,是在帧缓冲器中填写数据,然后扫描显示到LCD屏幕; 40 */ 41 static void fb_put_pixel(int x, int y, unsigned int color) 42 { 43 unsigned char * pen_8; 44 unsigned short *pen_16; 45 unsigned int *pen_32; 46 unsigned char *pixel_base = fbmem + (var.xres*y + x)*var.bits_per_pixel/8; 47 48 switch(var.bits_per_pixel) 49 { 50 case 8: 51 pen_8 = (unsigned char *)pixel_base; 52 *pen_8 = color; 53 break; 54 case 16: 55 pen_16 = (unsigned short *)pixel_base; 56 *pen_16 = convert32to16(color); 57 break; 58 case 32: 59 pen_32 = (unsigned int *)pixel_base; 60 *pen_32 = color; 61 break; 62 default: 63 printf("can't surpport %dbpp\n", var.bits_per_pixel); 64 break; 65 } 66 } 67 68 /* 绘制字符位图 */ 69 void draw_bitmap(FT_Bitmap * bitmap, FT_Int x, FT_Int y) 70 { 71 FT_Int i, j; 72 73 for(j = y; j < y + bitmap->rows; j++) 74 for(i = x; i < x + bitmap->width; i++) 75 { 76 if(i < 0 || j < 0 || i >= var.xres || j >= var.yres) 77 continue; 78 //image[j][i] |= bitmap->buffer[(j - y)*bitmap->width + (i - x)]; 79 fb_put_pixel(i, j, bitmap->buffer[(j - y)*bitmap->width + (i - x)]); 80 81 } 82 } 83 84 85 int main(int argc, char **argv) 86 { 87 FT_Library library; 88 FT_Face face; 89 90 FT_Vector pen; /* untransformed origin */ 91 FT_GlyphSlot slot; 92 93 FT_Glyph glyph; /* a handle to the glyph image */ 94 FT_BBox bbox; 95 96 int error; 97 int i; 98 wchar_t wstr1[] = L"百问网gif"; 99 wchar_t wstr2[] = L"www.100ask.org"; 100 int line_box_ymin = 10000; 101 int line_box_ymax = 0; 102 103 104 if(argc != 2) 105 { 106 printf("Usage: %s <font_file>\n", argv[0]); 107 return -1; 108 } 109 110 /* 一、显示固定尺寸汉字/字符 */ 111 /* 1、LCD初始化 */ 112 fd_fb = open("/dev/fb0", O_RDWR); 113 if(fd_fb < 0) 114 { 115 printf("can't open /dev/fb0\n"); 116 return -1; 117 } 118 if(ioctl(fd_fb, FBIOGET_VSCREENINFO, &var)) //可变参数,成功,返回0; 出错,返回-1; 119 { 120 printf("can't get var\n"); 121 return -1; 122 } 123 if(ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix)) //固定参数,成功,返回0; 出错,返回-1; 124 { 125 printf("can't get fix\n"); 126 return -1; 127 } 128 129 130 fbmem = (unsigned char *)mmap(NULL, fix.smem_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0); 131 if(fbmem == (unsigned char *)-1) 132 { 133 printf("can't mmap fbmem\n"); 134 return -1; 135 } 136 137 /* 2、清屏 */ 138 memset(fbmem, 0, fix.smem_len); 139 140 141 142 /* 二、显示矢量字体 */ 143 error = FT_Init_FreeType( &library ); /* 初始化 Freetype 库*/ 144 if ( error ) 145 { 146 printf("FT_Init_FreeType error!\n"); 147 return -1; 148 } 149 error = FT_New_Face( library, argv[1], 0, &face ); /* 打开一个字体文件 */ 150 if ( error ) 151 { 152 printf("FT_New_Face error!\n"); 153 return -1; 154 } 155 slot = face->glyph; 156 FT_Set_Pixel_Sizes(face, 24, 0); /* 设置字体大小: 24*24 Pixel */ 157 158 /* 确定起始点坐标: 159 * lcd_x = 0; 160 * lcd_y = 24; 161 * 笛卡尔坐标系: 162 * x = lcd_x = 0; 163 * y = var.yres - lcd_y = var.yres - 24; 164 */ 165 pen.x = 0 * 64; 166 pen.y = (var.yres - 24) * 64; 167 168 for(i = 0; i < wcslen(wstr1); i++) 169 { 170 /* 设置矩阵 */ 171 FT_Set_Transform( face, 0, &pen ); /* 字符保持不变换 */ 172 173 /* 根据编码值加载glyph到slot */ 174 error = FT_Load_Char( face, wstr1[i], FT_LOAD_RENDER ); 175 if ( error ) 176 { 177 printf("FT_Load_Char error!\n"); 178 return -1; 179 } 180 181 /* 确定字体边框 */ 182 error = FT_Get_Glyph( face->glyph, &glyph ); 183 if ( error ) 184 { 185 printf("FT_Get_Glyph error!\n"); 186 return -1; 187 } 188 FT_Glyph_Get_CBox( glyph, FT_GLYPH_BBOX_TRUNCATE, &bbox ); 189 if(line_box_ymin > bbox.yMin) 190 line_box_ymin = bbox.yMin; 191 if(line_box_ymax < bbox.yMax) 192 line_box_ymax = bbox.yMax; 193 194 draw_bitmap( &slot->bitmap, 195 slot->bitmap_left, 196 var.yres - slot->bitmap_top ); 197 pen.x += slot->advance.x; 198 } 199 200 /* 确定起始点坐标: 201 * lcd_x = 0; 202 * lcd_y = line_box_ymax - line_box_ymin + 24; 203 * 笛卡尔坐标系: 204 * x = lcd_x = 0; 205 * y = var.yres - lcd_y = var.yres - (line_box_ymax - line_box_ymin + 24); 206 */ 207 pen.x = 0 * 64; 208 pen.y = (var.yres - (line_box_ymax - line_box_ymin + 24)) * 64; 209 210 for(i = 0; i < wcslen(wstr2); i++) 211 { 212 /* 设置矩阵 */ 213 FT_Set_Transform( face, 0, &pen ); /* 字符保持不变换 */ 214 215 /* 根据编码值加载glyph到slot */ 216 error = FT_Load_Char( face, wstr2[i], FT_LOAD_RENDER ); 217 if ( error ) 218 { 219 printf("FT_Load_Char error!\n"); 220 return -1; 221 } 222 223 /* 确定字体边框 */ 224 error = FT_Get_Glyph( face->glyph, &glyph ); 225 if ( error ) 226 { 227 printf("FT_Get_Glyph error!\n"); 228 return -1; 229 } 230 FT_Glyph_Get_CBox( glyph, FT_GLYPH_BBOX_TRUNCATE, &bbox ); 231 if(line_box_ymin < bbox.yMin) 232 line_box_ymin = bbox.yMin; 233 if(line_box_ymax > bbox.yMax) 234 line_box_ymax = bbox.yMax; 235 236 draw_bitmap( &slot->bitmap, 237 slot->bitmap_left, 238 var.yres - slot->bitmap_top ); 239 pen.x += slot->advance.x; 240 } 241 242 243 return 0; 244 }