Linux嵌入式学习——Freetype矢量文字显示

该代码示例展示了如何在Linux环境下,利用FreeType库和framebuffer设备在LCD上显示旋转的汉字,同时调整字体大小和位置。程序首先打开framebuffer设备,然后加载字体文件,设置旋转角度,计算字符串的边界框,并将文字绘制到LCD上。然而,当进行换行时,旋转角度导致两行之间的间距过大是一个待解决的问题。
摘要由CSDN通过智能技术生成

一、逻辑

调用库函数,在字体文件中找到该字体,对字体进行变形,显示在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.换行的时候,当带有旋转角度时,两行之间间距较大。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值