对于汉字的字符作为位图显示时,如果采用上面的办法就会很麻烦,你必须把一个16X16点阵汉字的32个字节的点阵数据描述出来,如果有多个汉字或者汉字的点阵数目更多,如36X36等,工作量就会增加很多,并且手工处理点阵数据又容易出错。
因此,为了解决汉字点阵字符问题,我们可以借助于点阵汉字库来解决这个问题。通常汉字的存储是以机器内码的方式进行的,每个汉字占用两个字节,这两个字节都大于128,更严格来说,汉字的两个字节大小范围介于0xA1和0xFE之间。
汉字点阵字库通常是根据区位码来存储的,如果知道区位码,就可以得到汉字的点阵数据在字库文件中的偏移量,从而得到点阵数据。如果汉字的区码为qm,位码为wm,则16x16汉字点阵数据的偏移量offset就是
offset = ((qm-1)*94+(wm-1))*32;
因为每一个区的汉字数为94个,每一个汉字的点阵数据为32个字节。对于不同的字库,可能偏移量不完全相同,但是也是具有一个固定的差值,尝试一下就知道是多少了。
而区位码和汉字机器内码又有固定的关系,因此,通过一个汉字就可以得到其点阵数据。如果汉字机器内码的两个字节分别是nm0(高位), nm1(低位),则其区码和位码分别是
qm = (nm0 & 0x7F)-32;
wm = (nm1 & 0x7F)-32;
知道了如何获取汉字点阵数据的原理,下面就是通过代码来实现了。
BOOL LoadHZLib(char *);
char *hzBuffer, *pBuffer;
void glTextHZChar(char *str, int x, int y, int length);
GLubyte hzBitmap[256][32]; //这里最多支持256个汉字的字符串,如果需要更多,
//把数组改大即可
BOOL LoadHZLib(char *filename)
{
int hzHandle;
int length;
//打开点阵字库文件
hzHandle = _open(filename, _O_RDWR);
if(hzHandle <0)
{
MessageBox(NULL, "Can't open HZ lib!", "ERROR", MB_OK);
return FALSE;
}
//取得字库文件的长度
length = _filelength(hzHandle);
if(length <0)
{
_close(hzHandle);
MessageBox(NULL, "The length of lib file is error!", "ERROR", MB_OK);
return FALSE;
}
//根据字库文件的大小分配内存,以便把整个字库文件都装入到内存中
hzBuffer = (char *)malloc(length);
if(!hzBuffer)
{
_close(hzHandle);
MessageBox(NULL, "Malloc error!", "ERROR", MB_OK);
return FALSE;
}
if (_read(hzHandle, hzBuffer, length) <= 0)
{
_close(hzHandle);
free(hzBuffer);
hzBuffer = NULL;
MessageBox(NULL, "Read error!", "ERROR", MB_OK);
return FALSE;
}
_close(hzHandle);
return TRUE;
}
在glInit()中只需要加载汉字库
int glInit()
{
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glClearColor(1.0f,1.0f,1.0f,0.0f);
if(!LoadHZLib("hzk16"))
{
return FALSE;
}
return TRUE;
}
glTextHZChar是一个自定义函数,用于将一串汉字文本显示在屏幕(x, y)处,length是字符串的长度。
void glTextHZChar(char *str, int x, int y, int length)
{
int i,j;
int qm, wm;
GLubyte temp;
//限制字符串的长度
if(length>256)
{
return;
}
//将当前的变换属性值视点属性值压入属性堆栈
glPushAttrib( GL_TRANSFORM_BIT | GL_VIEWPORT_BIT );
// 使用新的投影矩阵和模型观察矩阵
glMatrixMode( GL_PROJECTION ); // 设置我们的投影矩阵
glPushMatrix();
glLoadIdentity(); // 使用新的投影矩阵
glMatrixMode( GL_MODELVIEW ); // 设置观察矩阵
glPushMatrix();
glLoadIdentity(); // 使用新的观察矩阵
glViewport( x, y, 0, 0 ); // 创建一个新的视点
glRasterPos4f( 0.0, 0.0, 0.0, 1.0 ); // 设置绘图位置(x, y, z, w)
for(j=0; j<length; j++)
{
//计算汉字的区位码
qm=(str[2*j] & 0x7F)-32; //区码=(头一字节内码 &0x7F)-32
wm=(str[2*j+1] & 0x7F)-32; //位码=(第二字节内码 &0x7F)-32
pBuffer=hzBuffer + ((qm-1)*94+(wm-1))*32;
memcpy(hzBitmap[j], pBuffer, 32);
//因为显示图像时,是从下到上显示的,而点阵数据是按照从上到下保存的
//这里对读取的数据进行上下对调
for(i=0; i<16; i++)
{
temp = hzBitmap[j][i];
hzBitmap[j][i] = hzBitmap[j][31-i];
hzBitmap[j][31-i] = temp;
}
for(i=0; i<16; i++)
{
temp = hzBitmap[j][2*i];
hzBitmap[j][2*i] = hzBitmap[j][2*i+1];
hzBitmap[j][2*i+1] = temp;
}
glBitmap(16, 16, 0, 0, 16, 0, hzBitmap[j]);
}
//恢复模型观察矩阵
glPopMatrix();
//恢复投影矩阵
glMatrixMode( GL_PROJECTION );
glPopMatrix();
//恢复变换和视点的属性值
glPopAttrib();
}
void glMain()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity(); //加载单位矩阵
glColor3f(0.0f, 0.0f, 0.0f);
glTextHZChar("欢迎来到OpenGL世界", 10, 150, 12);
glColor3f(1.0, 0.0, 1.0);
glTextHZChar("我喜欢OpenGL", 10, 100, 9);
glColor3f(0.0, 1.0, 1.0);
glTextHZChar("在OpenGL中汉字的点阵位图显示", 10, 60, 17);
SwapBuffers(g_hDC);
}
最后不要忘了释放掉hzBuffer所占用的内存。
void glShutdown()
{
……
if(hzBuffer)
{
free(hzBuffer);
hzBuffer=NULL;
}
}
程序运行后,可以看到三行不同颜色的汉字文本显示在窗口内,效果如图8-5所示。这里的字体是标准的16X16点阵的宋体,如果想显示其他大小和类型的字体,如繁体、隶书等,需要有相应的点阵字库,并且对glHZChar()进行一定的修改。