一、逻辑
调用库函数,在字体文件中找到该字体,对字体进行变形,显示在LCD上。
二、代码
用到的字体库是simsun.ttc(新宋体),Windows系统C:\Windows\Fonts目录下有该文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <sys/ioctl.h>
#include <linux/fb.h>
#include <string.h>
#include <stdlib.h>
#include <wchar.h>
#include <sys/mman.h>
#include <ft2build.h>
#include <math.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
int fd_fb; //用于接收framebuffer
struct fb_var_screeninfo var; //用于接收LCD数据
unsigned char * fb_base; // /dev/fb0映射到内存中的基地址
unsigned int pixel_width;
unsigned int line_width;
unsigned int screen_size;
int lcd_x; //lcd 上坐标
int lcd_y;
double angle = 0; //旋转角度:弧度
int font_size = 50; //字体大小
//描点函数
void lcd_put_pixel(int x,int y,int color);
//绘文字
void draw_bitmap(FT_Bitmap *bitmap,FT_Int x,FT_Int y);
//计算字符串大外框的左上角坐标(xMin,yMax)
void compute_string_bbox(FT_Face face,wchar_t *wcstr,FT_BBox *bbox,FT_Matrix matrix);
//显示字符串
void display_string(FT_Face face,wchar_t *wcstr,int lcd_x,int lcd_y,double angle);
/*
* argv[0]: ./test
* argv[1]: 字体文件路径
* argv[2]: 字体大小,可选填
*/
int main(int argc,char * argv[])
{
//判断输出参数
if(argc < 5)
{
printf("Usage:%s <font file> <angle> <lcd_x> <lcd_y> [font size]\n",argv[0]);
return -1;
}
if(argc > 5)
{
font_size = strtoul(argv[5],NULL,0);
}
angle = strtoul(argv[2],NULL,0) * 1.0 / 360.0 * 2 * 3.14159;
lcd_x = strtoul(argv[3],NULL,0);
lcd_y = strtoul(argv[4],NULL,0);
//1.打开framebuffer设备
int fd_fb = open("/dev/fb0",O_RDWR);
if(fd_fb < 0)
{
printf("can not open /dev/fb0\n");
return -1;
}
//2.获取fb_var_screeninfo数据
if(ioctl(fd_fb,FBIOGET_VSCREENINFO,&var) < 0)
{
printf("can not get fb_var\n");
return -1;
}
pixel_width = var.bits_per_pixel / 8;
line_width = pixel_width * var.xres;
screen_size = line_width * var.yres;
printf("lcd width:%d, lcd rows:%d \n",var.xres,var.yres);
//3.将LCD映射到内存中
fb_base = mmap(NULL,screen_size,PROT_READ | PROT_WRITE,MAP_SHARED,fd_fb,0);
if(fb_base == (unsigned char *)-1)
{
printf("can not mmap\n");
return -1;
}
//4.绘制函数
/* 清屏:白色 */
memset(fb_base,0xff,screen_size);
/* 绘制一个点 */
//lcd_put_pixel(var.xres/2,var.yres/2,0xff0000);
wchar_t *wcstr = L"繁忙的周一\nwww.baidu.com";
FT_Library library; /* 对应freetype库 */
FT_Face face; /* 对应字体文件 */
FT_GlyphSlot slot; /* 插槽:字体的处理结果保存在这里,slot中包含glyph和位图信息 */
//FT_Glyph glyph; /* 对应字符经过处理后的glyph,即外形、轮廓,可以从slot插槽中取 */
//FT_BBox bbox; /* 字符的外框 */
FT_Error error;
//FT_Matrix matrix; /* 变形矩阵 */
//FT_Vector pen; /* 字体的origin原点坐标,单位:1/64像素 */
angle = strtoul(argv[2],NULL,0) * 1.0 / 360.0 * 2 * 3.14159;
lcd_x = strtoul(argv[3],NULL,0);
lcd_y = strtoul(argv[4],NULL,0);
//1.初始化freetype库
error = FT_Init_FreeType(&library);
//2.加载字体文件
error = FT_New_Face(library,argv[1],0,&face);
slot = face->glyph;
//3.设置字体大小
error = FT_Set_Pixel_Sizes(face,font_size,0);
//4.选择哪种编码方式的字体文件
error = FT_Select_Charmap(face,FT_ENCODING_UNICODE);
/*
//5.设置旋转角度
matrix.xx = (FT_Fixed)( cos(angle) * 0x10000L );
matrix.xy = (FT_Fixed)(-sin(angle) * 0x10000L );
matrix.yx = (FT_Fixed)( sin(angle) * 0x10000L );
matrix.yy = (FT_Fixed)( cos(angle) * 0x10000L );
pen.x = 0;
pen.y = 0;
FT_Set_Transform(face,&matrix,&pen);
//6.加载位图
error = FT_Load_Char(face,wc[0],FT_LOAD_RENDER);
if(error)
{
printf("FT_Load_Char error\n");
return -1;
}
draw_bitmap(&slot->bitmap,var.xres/2,var.yres/2);
*/
display_string(face,wcstr,lcd_x,lcd_y,angle);
//6.关闭字体
FT_Done_Face(face);
FT_Done_FreeType(library);
//7.关闭/dev/fb0
close(fd_fb);
return 0;
}
void lcd_put_pixel(int x,int y,int color)
{
unsigned char* pen_8 = fb_base + y*line_width + x*pixel_width;
unsigned short* pen_16;
unsigned int* pen_32;
pen_16 = (unsigned short *)pen_8;
pen_32 = (unsigned int *)pen_8;
int red,green,blue;
switch(var.bits_per_pixel)
{
case 8:
*pen_8 = color;
break;
case 16:
{
/* 565 */
red = (color >> 16) & 0xff;
green = (color >> 8) & 0xff;
blue = (color >> 0) & 0xff;
*pen_16 = (((red>>3)<<11) | ((green>>2)<<5) | (blue>>3));
}
break;
case 32:
*pen_32 = color;
break;
default:
printf("can not support %d bpp\n",var.bits_per_pixel);
break;
}
}
//绘文字
void draw_bitmap(FT_Bitmap *bitmap,FT_Int x,FT_Int y)
{
FT_Int x_max = x + bitmap->width;
FT_Int y_max = y + bitmap->rows;
FT_Int i,j,p,q;
int color;
for(j=y,q=0;j<y_max;++j,++q)
{
for(i=x,p=0;i<x_max;++i,++p)
{
if(i<0 || j<0 || i>var.xres || j>var.yres)
continue;
else
{
color = bitmap->buffer[q*bitmap->width + p];
if(color == 0)
color = 0xffffff;
else
color = color << 16;
lcd_put_pixel(i,j,color);
}
}
}
}
//显示字符串
void display_string(FT_Face face,wchar_t *wcstr,int lcd_x,int lcd_y,double angle)
{
//lcd坐标系转换成笛卡尔坐标系
int x = lcd_x;
int y = var.yres - lcd_y;
FT_BBox bbox; /* 字符的外框 */
FT_Error error;
FT_Matrix matrix; /* 变形矩阵 */
FT_Vector pen; /* 字体的origin原点坐标,单位:1/64像素 */
FT_GlyphSlot slot; /* 插槽:存放字符的glyph 和 位图 */
slot = face->glyph;
//角度变形矩阵
matrix.xx = (FT_Fixed)( cos(angle) * 0x10000L );
matrix.xy = (FT_Fixed)(-sin(angle) * 0x10000L );
matrix.yx = (FT_Fixed)( sin(angle) * 0x10000L );
matrix.yy = (FT_Fixed)( cos(angle) * 0x10000L );
//获取字符串边框左上角坐标
compute_string_bbox(face,wcstr,&bbox,matrix);
//计算原点坐标
pen.x = (x - bbox.xMin) * 64; //单位:1/64像素
pen.y = (y - bbox.yMax) * 64;
//保留初始原点坐标,用于换行时使用
int originx = pen.x;
int originy = pen.y;
//依次绘制每个字符
int i;
for(i=0;i<wcslen(wcstr);++i)
{
FT_Set_Transform(face,&matrix,&pen); /* 变形 */
error = FT_Load_Char(face,wcstr[i],FT_LOAD_RENDER); /* 加载位图 */
if(error)
{
printf("FT_Load_Char error\n");
return;
}
/* 在LCD上绘制,使用LCD坐标系 */
draw_bitmap(&slot->bitmap,slot->bitmap_left,var.yres-slot->bitmap_top);
//计算下一个原点坐标
if(wcstr[i] == '\n')
{
pen.x = originx;
pen.y = originy - (bbox.yMax-bbox.yMin)*64;
}
else
{
pen.x += slot->advance.x;
pen.y += slot->advance.y;
}
}
}
//计算字符串大外框的左上角坐标(xMin,yMax),存放在bbox中
void compute_string_bbox(FT_Face face,wchar_t *wcstr,FT_BBox *bbox,FT_Matrix matrix)
{
FT_BBox temp_bbox; //临时存放边框
FT_BBox glyph_bbox; //存放每个字符边框
FT_Vector pen; //字符原点坐标和步长
FT_Glyph glyph; //字符外形
FT_GlyphSlot slot; //字符插槽
FT_Error error;
slot = face->glyph;
//初始化
temp_bbox.xMin = temp_bbox.yMin = 32000;
temp_bbox.xMax = temp_bbox.yMax = -32000;
//指定原点为(0,0)
pen.x = 0;
pen.y = 0;
//计算每个字符的外框,就可以得到整个字符串的外框
//对每个字符先transform,再load char就可以得到字符的外框了
int i;
for(i=0;i<wcslen(wcstr);++i)
{
FT_Set_Transform(face,&matrix,&pen);
error = FT_Load_Char(face,wcstr[i],FT_LOAD_RENDER);
if(error)
{
printf("FT_Load_Char error\n");
return;
}
//从slot中取出glyph
error = FT_Get_Glyph(slot,&glyph);
if(error)
{
printf("FT_Get_Glyph error\n");
return;
}
//从glyph得到外框
FT_Glyph_Get_CBox(glyph,FT_GLYPH_BBOX_TRUNCATE,&glyph_bbox);
if(glyph_bbox.xMin < temp_bbox.xMin)
temp_bbox.xMin = glyph_bbox.xMin;
if(glyph_bbox.yMin < temp_bbox.yMin)
temp_bbox.yMin = glyph_bbox.yMin;
if(glyph_bbox.xMax > temp_bbox.xMax)
temp_bbox.xMax = glyph_bbox.xMax;
if(glyph_bbox.yMax > temp_bbox.yMax)
temp_bbox.yMax = glyph_bbox.yMax;
//下一个字符原点
pen.x += slot->advance.x;
pen.y += slot->advance.y;
}
*bbox = temp_bbox;
}
三、代码不足
1.换行的时候,当带有旋转角度时,两行之间间距较大。