TrueType字体
利用位图的方式来实现文本是一种方式,但是这种方式需要事先知道字符的点阵信息,字符的大小也很难随意更改,字体种类就更无法随意选择了。Windows系统提供了大量的TrueType字体,这是我们字体应用的巨大资源。但是在Windows9x和Windows NT/2000系统下,不能在双缓冲的OpenGL应用中直接应用GDI调用来显示TrueType字符。
为了能够在OpenGL中使用TrueType字体资源,Win32通过WGL库提供了wglUseFontBitmaps和wglUseFontOutlines函数来满足需要。两个函数的原型如下:
BOOL wglUseFontBitmaps(
HDC hdc, // device context whose font will be used
DWORD first, // glyph that is the first of a run of glyphs to
// be turned into bitmap display lists
DWORD count, // number of glyphs to turn into bitmap display
// lists
DWORD listBase // specifies starting display list
);
BOOL wglUseFontOutlines(
HDC hdc, // device context of the outline font
DWORD first, // first glyph to be turned into a display list
DWORD count, // number of glyphs to be turned into display
// lists
DWORD listBase, // specifies the starting display list
FLOAT deviation, // specifies the maximum chordal deviation from
// the true outlines
FLOAT extrusion, // extrusion value in the negative z direction
int format, // specifies line segments or polygons in
// display lists
LPGLYPHMETRICSFLOAT lpgmf
// address of buffer to receive glyph metric data
);
两个函数的实质是创建一系列的位图显示列表,当需要显示某一个字符时调用对应的显示列表就可以了,显示列表是基于当前DC选择的字体字型。
wglUseFontOutlines函数还允许设置字符的厚度,因此更多的运用于三维TrueType字符的显示。
hdc 参数表示哪一个设备上下文(DC)中选中的字体用于产生位图显示列表。first 表示在选中的字体中从哪一个字符开始创建显示列表,在此之前的字符将不被创建显示列表。count 表示创建的显示列表的个数。listBase 表示开始的显示列表。
wglUseFontOutlines()函数中的deviation 表示显示的字符与实际轮廓的最大偏移量。deviation取值不能为负数,当deviation为0时,表示偏移量等于原始字体的一个设计单位。为了简便起见,我们都将其设置为0。extrusion 表示在-Z方向的值,即字符的厚度。format 表示显示列表使用直线段还是多边形,取值分别是WGL_FONT_LINES和WGL_FONT_POLYGONS。lpgmf 是一个数组指针,数组中的每一个元素都是一个GLYPHMETRICSFLOAT结构,用于接收字符的数据。
TrueType字体是用直线和曲线来定义外轮廓,曲线使用B样条曲线。wglUseFontOutlines函数是通过将生成字型本身的二次B样条曲线分割成直线段来逼近字型原始轮廓,当两者之间误差小于deviation时即认为OK。
下面我们来使用wglUseFontBitmaps创建一个显示TrueType字体的程序。由于增加的内容较多,我们尽可能把详细的代码列出来,但是象WinMain等完全没有变化的就不列出来了。
// truetype.cpp
//
//前面一些相同的定义就不列出来了
……
#define MAXFONTNUMBER 256 //暂时最多使用256种TrueType字体
GLuint g_Base[MAXFONTNUMBER]; // 每种字体显示列表集的第一个显示列表
//创建字体函数
GLvoid glBuildFont(GLuint *base, int nHeight, DWORD fdwCharSet, LPSTR strFontName);
//释放字体资源函数
void glFreeFont(GLuint base);
//显示字符串函数
GLvoid glPrint(GLuint base, const char *fmt, ...);
//预设的字体显示列表包含不超过128个字符
#define MAX_CHARS 128
//定义一个字体结构
struct t_Font
{
int nHeight; //字符的高度
DWORD fdwCharSet; //字符集
LPSTR strFontName; //字体名
};
//预设程序中使用10种字体
t_Font g_Font[MAXFONTNUMBER]=
{
{18, ANSI_CHARSET, "Arial"},
{24, ANSI_CHARSET, "GreekC"},
{24, ANSI_CHARSET, "Italic"},
{20, ANSI_CHARSET, "Math A"},
{16, ANSI_CHARSET, "MS Serif"},
{18, ANSI_CHARSET, "ScriptC"},
{16, ANSI_CHARSET, "Symbol"},
{20, ANSI_CHARSET, "Times New Roman"},
{24, SYMBOL_CHARSET, "Wingdings"},
{18, GB2312_CHARSET, "宋体"}
};
//根据字体的大小,字符集和字体名称创建显示列表
GLvoid glBuildFont(GLuint *base, int nHeight, DWORD fdwCharSet, LPSTR strFontName)
{
HFONT hFont;
GLuint lBase;
//生成一个包含有MAX_CHARS个显示列表的显示列表集
lBase = glGenLists(MAX_CHARS);
//创建一种字体,CreateFont是一个标准的Win32 API
hFont = CreateFont( -nHeight, //字体的高度
0, //字体的宽度,缺省
0, //指定移位向量和设备X轴之间的角度
0, //字符的基线和设备X轴之间的角度
FW_NORMAL, //字体的权值,
FALSE, //是否斜体
FALSE, //是否有下划线
FALSE, //是否有删除线
fdwCharSet, //字符集
OUT_TT_PRECIS, //输出精度
CLIP_DEFAULT_PRECIS, //裁减精度
ANTIALIASED_QUALITY, //输出质量
FF_DONTCARE|DEFAULT_PITCH, //字体间距和字体族
strFontName); //字体名称
SelectObject(g_hDC, hFont);
//从第一个字符开始创建共MAX_CHARS个显示列表
wglUseFontBitmaps(g_hDC, 0, MAX_CHARS-1, lBase);
*base = lBase;
}
//释放字体显示列表
GLvoid glFreeFont(GLuint base)
{
glDeleteLists(base, MAX_CHARS);
}
//显示字符串
GLvoid glPrint(GLuint base, const char *fmt, ...)
{
//字符串
char text[256];
va_list ap;
//如果字符串为空就返回
if (fmt == NULL)
return;
va_start(ap, fmt);
vsprintf(text, fmt, ap);
va_end(ap);
glPushAttrib(GL_LIST_BIT);
glListBase(base);
//调用显示列表,显示字符
glCallLists(strlen(text), GL_UNSIGNED_BYTE, text);
glPopAttrib();
}
有关字体处理的预备工作就完成了,下面是主程序对字体的处理,显示在屏幕上。
int glInit()
{
//启用阴影平滑(Smooth Shading)。
glShadeModel(GL_SMOOTH);
glClearColor(1.0f, 1.0f, 1.0f,0.0f);
//设置深度缓冲
glClearDepth(1.0f);
//启动深度测试
glEnable(GL_DEPTH_TEST);
//深度测试的类型
glDepthFunc(GL_LEQUAL);
//进行透视修正
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
//创建10种字体
for(int i=0; i<10; i++)
{
glBuildFont(&g_Base[i],g_Font[i].nHeight, g_Font[i].fdwCharSet,
g_Font[i].strFontName);
}
return TRUE;
}
void glMain()
{
static float angle[10] ;
//延迟300毫秒, 效果更加清楚
Sleep(300);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity(); //加载单位矩阵
glTranslatef(0.0f, 0.0f, -1.0f);
float r, g, b;
// 每一种字体使用不同的颜色显示
for(int i=0; i<10; i++)
{
angle[i%10] = angle[(i+9)%10] +1.0f;
r = float(cos(angle[i]));
g = float(sin(angle[i]));
b = float(cos(2*angle[i]));
glColor3f(r, g, b);
glRasterPos2f(-0.5f, 0.35f-i*0.08);
glPrint(g_Base[i], "OpenGL True Type Font- %s", g_Font[i].strFontName);
}
SwapBuffers(g_hDC);
}
当退出程序时要记得释放掉显示列表。
void glShutdown()
{
……
for(int i=0; i<MAXFONTNUMBER; i++)
{
if(g_Base[i])
glFreeFont(g_Base[i]);
}
}
程序运行后,可以看到10行“OpenGL True Type Font”字符串都以设定的字体显示,并且不停变换颜色,效果如果8-6所示。
图8-6 OpenGL TrueType字体