【OpenGL游戏开发之五】freetype2 显示汉字

1.FreeType2 是什么?


    它是一个为各种应用程序提供通用的字体文件访问的软件包。尤其值得注意的以下特性:

  • 提供统一的字体文件访问接口。支持位图和向量格式,包括 TrueType 、 OpenType 、 Typel 、 CID 、 CFF 、 Windows FON/FNT 、 X11 PCF 。
  • 提供高效反走样的基于 256 灰度级的位图字形的生产。
  • 模块清晰,每种字体格式对于一个模块。类库的构建可以按照你需要支持的格式进行裁减以减小代码尺寸。(最小的反走样 FreeType 库 <30Kb )


2. FreeType2 能做什么?


    FT2 已经易用于许多领域。例如:

  • 图形子系统和文本显示库
  • 文本排版(布局、分页、渲染)
  • 字体识别和转换工具

    一般来说,该库使得你能轻松的操纵字体文件。


3.如何配置openglfreetype2


4.freetype2 显示汉字


代码:

#include <Windows.h>
#include <iostream>
#include <Gl\GL.h>
#include <Gl\glut.h>  
#include <ft2build.h>
#include <freetype/freetype.h>
#include <freetype/ftglyph.h>
#include <freetype/ftoutln.h>
#include <freetype/fttrigon.h>
//#pragma comment(lib , "lib/glut32.lib")
//#pragma comment(lib , "lib/freetype2110.lib")
using namespace std;

#define MAX_NO_TEXTURES 1

#define CUBE_TEXTURE 0

GLuint texture_id[MAX_NO_TEXTURES];

struct xCharTexture
{
	GLuint  m_texID;
	wchar_t m_chaID;
	int     m_Width;
	int     m_Height;

	int     m_adv_x;
	int     m_adv_y;
	int     m_delta_x;
	int     m_delta_y;
public:
	xCharTexture()
	{
		m_texID  = 0;
		m_chaID  = 0;
		m_Width  = 0;
		m_Height = 0;
	}
}g_TexID[65536];


class xFreeTypeLib
{
	FT_Library m_FT2Lib;
	FT_Face    m_FT_Face;

public:
	int   m_w;
	int   m_h;
	void load(const char* font_file , int _w , int _h);
	GLuint loadChar(wchar_t ch);
};

void xFreeTypeLib::load(const char* font_file , int _w , int _h)
{
	FT_Library library;
	if (FT_Init_FreeType( &library) ) 
		exit(0);
	//加载一个字体,取默认的Face,一般为Regualer
	if (FT_New_Face( library, font_file, 0, &m_FT_Face )) 
		exit(0);
	//选择字符表
	FT_Select_Charmap(m_FT_Face, FT_ENCODING_UNICODE);
	m_w = _w ; m_h = _h;
	m_FT_Face->num_fixed_sizes;
	//大小要乘64.这是规定。照做就可以了。
	//FT_Set_Char_Size( m_FT_Face , 0 , m_w << 6, 96, 96);
	//用来存放指定字符宽度和高度的特定数据
	FT_Set_Pixel_Sizes(m_FT_Face,m_w, m_h);
}

GLuint xFreeTypeLib::loadChar(wchar_t ch)
{
	if(g_TexID[ch].m_texID)
	return g_TexID[ch].m_texID;
	/* 装载字形图像到字形槽(将会抹掉先前的字形图像) */ 
	if(FT_Load_Char(m_FT_Face, ch, /*FT_LOAD_RENDER|*/FT_LOAD_FORCE_AUTOHINT|
		(TRUE ? FT_LOAD_TARGET_NORMAL : FT_LOAD_MONOCHROME | FT_LOAD_TARGET_MONO) )   )
	{
		return 0;
	}

 /*if(FT_Load_Glyph( m_FT_Face, FT_Get_Char_Index( m_FT_Face, ch ), FT_LOAD_FORCE_AUTOHINT ))
  throw std::runtime_error("FT_Load_Glyph failed");*/

	xCharTexture& charTex = g_TexID[ch];
 
	//得到字模
	FT_Glyph glyph;
	//把字形图像从字形槽复制到新的FT_Glyph对象glyph中。这个函数返回一个错误码并且设置glyph。 
	if(FT_Get_Glyph( m_FT_Face->glyph, &glyph ))
		return 0;

 	//转化成位图
 	FT_Render_Glyph( m_FT_Face->glyph,   FT_RENDER_MODE_LCD );//FT_RENDER_MODE_NORMAL  ); 
 	FT_Glyph_To_Bitmap( &glyph, ft_render_mode_normal, 0, 1 );
 	FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph)glyph;

	//取道位图数据
	FT_Bitmap& bitmap=bitmap_glyph->bitmap;

	//把位图数据拷贝自己定义的数据区里.这样旧可以画到需要的东西上面了。
	int width  =  bitmap.width;
	int height =  bitmap.rows;
 
	m_FT_Face->size->metrics.y_ppem;		//伸缩距离到设备空间
	m_FT_Face->glyph->metrics.horiAdvance;  //水平文本排列


	charTex.m_Width = width;
	charTex.m_Height = height;
	charTex.m_adv_x = m_FT_Face->glyph->advance.x / 64.0f;  //步进宽度
	charTex.m_adv_y = m_FT_Face->size->metrics.y_ppem;		//m_FT_Face->glyph->metrics.horiBearingY / 64.0f;
	charTex.m_delta_x = (float)bitmap_glyph->left;			//left:字形原点(0,0)到字形位图最左边象素的水平距离.它以整数象素的形式表示。 
	charTex.m_delta_y = (float)bitmap_glyph->top - height;	//Top: 类似于字形槽的bitmap_top字段。
	glGenTextures(1,&charTex.m_texID);
	glBindTexture(GL_TEXTURE_2D,charTex.m_texID);
	char* pBuf = new char[width * height * 4];
	for(int j=0; j  < height ; j++)
	{
		for(int i=0; i < width; i++)
		{
   			unsigned char _vl =  (i>=bitmap.width || j>=bitmap.rows) ? 0 : bitmap.buffer[i + bitmap.width*j];
  			pBuf[(4*i + (height - j - 1) * width * 4)  ] = 0xFF;
   			pBuf[(4*i + (height - j - 1) * width * 4)+1] = 0xFF;
   			pBuf[(4*i + (height - j - 1) * width * 4)+2] = 0xFF;
   			pBuf[(4*i + (height - j - 1) * width * 4)+3] = _vl;
  		}
	 }

	glTexImage2D( GL_TEXTURE_2D,0,GL_RGBA,width, height,0,GL_RGBA,GL_UNSIGNED_BYTE,pBuf);  //指定一个二维的纹理图片
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_CLAMP);							   //glTexParameteri():纹理过滤
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_CLAMP);
	glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
	glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
	glTexEnvi(GL_TEXTURE_2D,GL_TEXTURE_ENV_MODE,GL_REPLACE);								//纹理进行混合

	/*gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, width, height, GL_RGBA, GL_UNSIGNED_BYTE, pBuf);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S, GL_CLAMP);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T, GL_CLAMP);
	glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
	glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
	glTexEnvi(GL_TEXTURE_2D,GL_TEXTURE_ENV_MODE,GL_REPLACE);*/
	delete[] pBuf;
	return charTex.m_chaID;
}


xFreeTypeLib g_FreeTypeLib;
float ratio;

xCharTexture* getTextChar(wchar_t ch)
{
	g_FreeTypeLib.loadChar(ch);
	return &g_TexID[ch];
}

LPWSTR AnsiToUnicode(LPCSTR lpcstr)   //参数lpcstr类型也可是char*
{ 
	LPWSTR Pwstr;
	int  i;
	i=MultiByteToWideChar(CP_ACP,0,lpcstr,-1,NULL,0);
	Pwstr=new WCHAR[i]; 
	MultiByteToWideChar(CP_ACP,0,lpcstr,-1,Pwstr,i);

	return (Pwstr);
}

//wchar_t g_UnicodeString[]=L"aaabb/x4E2D/x6587/x0031/x0032/x0033";  
const char g_UnicodeString[]="aaa VB文件格式:/n/若不明确就标为未知/n/表演者:";

		
void drawText(wchar_t* _strText,int x , int y, int maxW , int h)
{
	int sx = x;
	int sy = y;
	int maxH = h;
	size_t nLen = wcslen(_strText);

	for(int i = 0 ; i <nLen ; i ++)
	{
		if(_strText[i] =='/n')
		{
			sx = x ; sy += maxH + 12;
			continue;
		}
		xCharTexture* pCharTex = getTextChar(_strText[i]);
		glBindTexture(GL_TEXTURE_2D,pCharTex->m_texID);							//绑定到目标纹理
		glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );	
		glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
		glEnable(GL_BLEND);														//打开或关闭OpenGL的特殊功能
		glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);						//特殊的像素算法
		//glDisable(GL_TEXTURE_2D);
		int w = pCharTex->m_Width;
		int h = pCharTex->m_Height;

		int ch_x = sx + pCharTex->m_delta_x;
		int ch_y = sy - h - pCharTex->m_delta_y;

		if(maxH < h) maxH = h;
		glBegin ( GL_QUADS );													 // 定义一个或一组原始的顶点
		{
			glTexCoord2f(0.0f, 1.0f); glVertex3f(ch_x      , ch_y    ,  1.0f);
			glTexCoord2f(1.0f, 1.0f); glVertex3f(ch_x +  w, ch_y    ,  1.0f);
			glTexCoord2f(1.0f, 0.0f); glVertex3f(ch_x +  w, ch_y + h,  1.0f);
			glTexCoord2f(0.0f, 0.0f); glVertex3f(ch_x     , ch_y + h,  1.0f);
		}
		glEnd();
		sx += pCharTex->m_adv_x;
		if(sx > x + maxW)
		{
			sx = x ; sy += maxH + 12;
		}
	}
}

void init(void)
{
	glShadeModel(GL_SMOOTH|GL_FLAT);						//选择平直或平滑着色
	glClearColor(0.0f, 0.0f, 0.0f, 0.5f);					//清除色彩缓冲区
	glEnable ( GL_COLOR_MATERIAL_FACE );
	glColorMaterial ( GL_FRONT, GL_AMBIENT_AND_DIFFUSE );	// 使一个材质色彩指向当前的色彩

	//g_FreeTypeLib.load("simhei.ttf",14,14);
	// g_FreeTypeLib.load("c://windows//fonts//simhei.ttf",14,14);
	g_FreeTypeLib.load("c://windows//fonts//simhei.ttf",12,12);

	glDisable ( GL_CULL_FACE );

	//glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
}

void reshape( int w, int h )
{
 // Prevent a divide by zero, when window is too short
 // (you cant make a window of zero width).
 if(h == 0)
  h = 1;

 ratio = 1.0f * w / h;
 // Reset the coordinate system before modifying
 glMatrixMode(GL_PROJECTION);							//定义矩阵
 glLoadIdentity();										//用恒等矩阵替换当前矩阵
 
 // Set the viewport to be the entire window
    glViewport(0, 0, w, h);								//设置视窗
    glOrtho(0,w,h,0,-100,200);							//用垂直矩阵与当前矩阵相乘
 // Set the clipping volume
 glMatrixMode(GL_MODELVIEW);
 glLoadIdentity();					
 gluLookAt(0, 0, 30 ,0 , 0 ,10 , 0.0f , 1.0f , 0.0f);	//设定一个变换视点
}

void display( void )
{
   glClearColor(0.0f , 0.0f , 0.6f , 1.0f);
   glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
   /*glLoadIdentity ( );
   glPushMatrix();
   glTranslatef ( 0.0, 0.0, -5.0 );
   */
   glEnable ( GL_TEXTURE_2D );
   wchar_t *wstr = AnsiToUnicode(g_UnicodeString);
   drawText(wstr, 50, 50, 900,25);
   //glPopMatrix();
   glutSwapBuffers();
}

void keyboard ( unsigned char key, int x, int y )  // Create Keyboard Function
{
  switch ( key ) {
    case 27:        // When Escape Is Pressed...
      exit ( 0 );   // Exit The Program
      break;        // Ready For Next Case
    default:        // Now Wrap It Up
      break;
  }
}

void arrow_keys ( int a_keys, int x, int y )   // Create Special Function (required for arrow keys)
{
  switch ( a_keys ) {
    case GLUT_KEY_UP:     // When Up Arrow Is Pressed...
      glutFullScreen ( ); // Go Into Full Screen Mode
      break;
    case GLUT_KEY_DOWN:                 // When Down Arrow Is Pressed...
      glutReshapeWindow ( 900, 500 );   // Go Into A 500 By 500 Window
      break;
    default:
      break;
  }
}


void myDisplay(void)  

{  

	glClear(GL_COLOR_BUFFER_BIT);  

	glColor3f (1.0,0.0,0.0);  
	glBegin (GL_LINES);  
	glVertex2i (180,15);  
	glVertex2i (10,145);  
	glEnd();  

	glRectf(-0.5f, -0.5f, 0.5f, 0.5f);  

	glFlush();  

}  

int main(int argc,char * argv[])  
{  

	//ANSI字符串,内容长度7字节   
	char sz[20] = "中文123";  

	//UNICODE字符串,内容长度5个wchar_t(10字节)   
	wchar_t   wsz[100]   =   L"/x4E2D/x6587/x0031/x0032/x0033";      
	//运行时设定当前ANSI编码,VC格式   
	setlocale(LC_ALL,".936");  

	//GCC中格式   
	setlocale(LC_ALL, "zh_CN.GBK");  

	//VisualC++中使用小写%s,按照setlocale指定编码输出到文件   
	//GCC中使用大写%S   


	//把UNICODE字符串按照setlocale指定的编码转换成字节   
	wcstombs(sz,   wsz,   20);   
	//把字节串按照setlocale指定的编码转换成UNICODE字符串   
	mbstowcs(wsz,   sz,   20);    

	glutInit( &argc, argv );									   //用于初始化GULT库
	glutInitDisplayMode ( GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA );  //设置初始显示模式
	glutInitWindowPosition (0,0);								   //确定窗口的位置
	glutInitWindowSize  ( 900, 500 );							   //设置窗口大小
	glutCreateWindow    ( "NeHe Lesson 6- Ported by Rustad" );	   //创建一个窗口标题
	init ();
	//glutFullScreen      ( );          //Put Into Full Screen
	glutDisplayFunc     ( display );	//注册一个绘图函数
	glutReshapeFunc     ( reshape );	//设置投影的函数
	glutKeyboardFunc    ( keyboard );	//键盘回调函数
	glutSpecialFunc     ( arrow_keys );	//设置当前窗口的特殊键盘回调。
	glutIdleFunc    ( display );		//程序空闲时调用的函数
	glutMainLoop        ( );			// 进入GLUT事件处理循环

	return 0;  

}  

运行效果图:




freetype2学习资源:

[精华] FreeType 2开发文档 [中译版]:http://www.unixresources.net/linux/clf/kylix/archive/00/00/59/21/592188.html

FreeType 2官方网:http://www.freetype.org/freetype2/


  • 4
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
### 回答1: freetype2是一个开源的字体渲染库,主要用于将矢量字体转换为位图字体以供显示设备使用。单片机是一种集成了处理器、存储器和各种输入输出接口的微型计算机,用于控制和驱动各种电子设备。 使用freetype2库可以在单片机上实现字体的显示功能。具体步骤如下: 1. 首先,我们需要将所需的矢量字体文件导入到单片机的存储器。这些字体文件可以从互联网上下载或者使用字体编辑软件自行设计。 2. 在单片机程序,我们需要调用freetype2库的相关函数来解析字体文件,并将其转换为位图字体。这些函数包括字体初始化、加载字体文件、设置字体大小等。 3. 一旦字体文件被解析并转换为位图字体,我们可以通过单片机的显示接口来将字体渲染到屏幕上。这涉及到底层硬件的操作,例如设置屏幕分辨率、颜色深度等。 4. 在将字体显示到屏幕上之前,还可以通过freetype2库提供的函数设置字体的样式、颜色、对齐方式等。这使得我们可以在单片机上展示特定的字体效果。 总之,通过使用freetype2库,结合单片机的硬件接口和显示功能,我们可以实现在单片机上显示各种字体的功能。这对于某些需要显示文字信息的应用场景,如显示器、小型屏幕终端等,具有很大的实用价值。 ### 回答2: freetype是一款用于处理字体的开源软件库,它可以将字体文件的字符转换为可显示的图像。单片机是一种集成了微处理器、存储器和各种输入输出接口的芯片,它常用于控制电子设备。 如果想在单片机上显示字体,可以借助freetype2库来完成。首先,需要将字体文件转换为freetype2支持的格式,比如.ttf文件。然后,在单片机上编写程序,使用freetype2库来解析字体文件。通过指定要显示的字体样式、大小和位置等参数,可以将需要显示的字体转换为图像数据。最后,将图像数据发送给单片机的显示屏或其他输出设备,就可以在单片机上显示字体了。 在单片机上显示字体可能涉及到一些硬件驱动的问题,比如驱动液晶显示屏或其他显示设备。具体的操作步骤和代码实现会依赖于所使用的单片机和显示设备。可以参考freetype2库官方的文档和示例代码,或者搜索相关教程和案例进行学习和实践。 总之,使用freetype2库可以在单片机上显示字体,通过将字体文件转换为图像数据,并通过适当的硬件驱动将图像数据发送到显示设备上。这样,单片机就可以实现字体的显示功能了。 ### 回答3: freetype2 是一个开源的字体渲染库,它可以在计算机上进行字体的渲染和排版。单片机是指一种集成了处理器、存储器和输入输出接口等功能的微型计算机,常用于嵌入式系统。那么如何在单片机上使用freetype2来显示字呢? 首先,我们需要在计算机上安装freetype2库,并使用它来渲染和生成我们需要显示的字体。渲染好的字体可以保存为位图文件或者字库文件。 然后,将生成的字库文件或位图文件转换成单片机可以识别和处理的格式。可以使用专门的转换工具将字库文件转换成单片机所需的字模数组或字模数据表,并将其储存在单片机的存储器。 接下来,在单片机程序,我们可以通过调用相应字模的索引或地址,来显示特定的字形。我们可以使用单片机的液晶显示模块或其他合适的显示设备来显示字体,通过控制像素点的亮暗来绘制字形。 在显示字体时,我们需要根据字模的大小和位置,在显示设备上逐个点绘制字体的像素。可以采用行扫描或列扫描的方法来控制像素点的亮暗,逐行或逐列地设置像素状态,绘制完整的字形。 需要注意的是,在单片机上显示字体可能会受到处理能力和资源限制的影响。单片机的存储器容量有限,对字模的存储和处理可能会受到限制。同时,单片机的处理速度相对较慢,可能需要耗费较多的时间来完成字体的渲染和显示。 综上所述,使用freetype2库在单片机上显示字体需要将渲染好的字库文件转换成单片机可以处理的格式,并通过程序控制显示设备逐个像素地绘制字体。同时要考虑到单片机的资源和处理能力的限制,尽量优化显示效果和速度。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

热血枫叶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值