天天看点

在SDL工程中让SDL_ttf渲染汉字

有时候在关于sdl的博文中看到一些评论,说sdl对中文的支持不佳,因为当程序涉及中文时总是输出乱码。

照我个人观点,这里面很多都是误解。下面就根据我在windows下使用sdl的情况,说说我的观点。

sdl作为一个跨平台的库,在字符方面有它独特的地方。那就是,它的运行库支持的字符编码为utf8,而不是windows中常见的各种本地字符编码。比如中文版windows使用的codepage 936,也有称其为gbk的,实际上是对基于gb2312字符集的euc-cn编码方式做了一个基于unicode字符集中的cjk子集的拓展所产生的一种字符硬件编码方式。

比如说sdl_wm_setcaption这个函数,它就接受一个utf8字符串,来设置窗口的标题。

不知为何,我在sdl的文档中并没有找到关于sdl的函数使用utf8编码的说明。这可能是造成对sdl的中文兼容性的误解的其中一个原因。

无论如何,这种设计是有道理的,因为它不仅兼容了使用不同本地字符集的平台,还避免了造成广泛的可移植性问题的ucs2\ucs4之争。

关于汉字渲染,常见的一个sdl扩展库就是sdl_ttf,它可以支持truetype字体的渲染,无疑非常吸引人。

windows版本的sdl_ttf运行库的一个问题是,它没有遵循sdl中的关于utf8的习惯,而是提供了3个类似的函数来渲染文字。

也就是ttf_renderutf8_solid,ttf_rendertext_solid和ttf_renderunicode_solid三个函数。

其中ttf_rendertext_solid能正确渲染ascii字符串,而ttf_renderutf8_solid和ttf_renderunicode_solid分别用于渲染utf8编码的和ucs2编码的字符串。

在windows中使用sdl时,如果需要渲染汉字,就需要将本地字符集转化为utf8字符集。

当然,如果从unicode字符集转换成utf8字符集,那就更方便了。另外插一句,在mingw编译环境下使用unicode字符时,务必记得给gcc编译器传递参数“-finput-charset=gbk”,否则会提示不合法的字节序列。

在本地字符编码(这里我们着重于讨论中文windows中使用的codepage 936本地字符编码),utf8字符编码和unicode字符编码3个编码方式互相转换的时候可以使用windows中的widechartomultibyte和multibytetowidechar这两个函数。当然也可以使用c运行时库的wcstombs和mbstowcs,这2个函数的可移植性更好(如果需要在windows以外的系统中作转换时这可能是有用的,不过这一点在linux下没用因为它直接使用utf8编码)。

在windows中配合widechartomultibyte和multibytetowidechar转换字符串的格式当然很好,不过如果使用wcstombs和mbstowcs时,需要注意的是windows在这里不支持utf8格式,(详见msdn中关于setlocale的条目:http://msdn.microsoft.com/zh-cn/library/x99tb11d.aspx),也就是这个时候需要我们自己来完成从unicode到utf8的转换。

这个问题可以参考一下2篇文章:

http://www.cppblog.com/jacky2019/archive/2007/03/08/19431.html

http://blog.sina.com.cn/s/blog_473f16d001000406.html

演示一小段程序,我这里没有使用unicode字符串而是使用了本地字符编码,在运行时再进行转换,也就是直接从本地编码转为utf8编码。

这里不仅使用了sdl_ttf,还需要一个雅黑.ttf文件,这个需要在系统分区的windows\fonts文件夹里复制出来,原文件名是msyh.ttf,复制到程序所在文件夹,并改名为雅黑.ttf。这里主要演示用utf字符串正确的使用文件名中带中文的文件。

在SDL工程中让SDL_ttf渲染汉字

代码

1 #include <sdl/sdl.h>

2 #include <sdl/sdl_ttf.h>

3 #include <windows.h>

4  char *localetoutf8(char *src){

5 static char *buf = null;

6 if(buf){

7 free(buf);

8 buf = null;

9 }

10 wchar_t *unicode_buf;

11 int nretlen = multibytetowidechar(cp_acp,0,src,-1,null,0);

12 unicode_buf = (wchar_t*)malloc((nretlen+1)*sizeof(wchar_t));

13 multibytetowidechar(cp_acp,0,src,-1,unicode_buf,nretlen);

14 nretlen = widechartomultibyte(cp_utf8,0,unicode_buf,-1,null,0,null,null);

15 buf = (char*)malloc(nretlen+1);

16 widechartomultibyte(cp_utf8,0,unicode_buf,-1,buf,nretlen,null,null);

17 free(unicode_buf);

18 return buf;

19 }

20 

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

22 sdl_surface *screen = sdl_setvideomode(640,480,32,sdl_swsurface);

23 sdl_wm_setcaption(localetoutf8("在sdl中渲染汉字吧!"),null);

24 ttf_init();

25 ttf_font *font = ttf_openfont(localetoutf8("雅黑.ttf"), 28);

26 sdl_color textcolor = {255, 255, 255};

27 if(!font){

28 messagebox(0,0,"no",0);

29 return -1;

30 }

31 sdl_surface *text = null;

32 text = ttf_renderutf8_solid(font,localetoutf8("中文!"),textcolor);

33 sdl_blitsurface(text,null,screen,null);

34 sdl_flip(screen);

35 sdl_event event;

36 while(sdl_pollevent(&event),event.type != sdl_quit);

37 return 0;

38 }

39  

在SDL工程中让SDL_ttf渲染汉字

这里有一个短小的多的函数解释了从本地字符集到utf8的相互转化。

在SDL工程中让SDL_ttf渲染汉字

代码——utf8转本地字符集

1 #include <stdio.h>

2 #include <stdlib.h>

3 #include <locale.h>

4 #include <string.h>

5  char *utf8tolocal(char *src){

6 wchar_t wbuf[100];

7 static char buf[100];

8 char *uchar = (char*)wbuf;

9 char *psrc = src;

10 //初始化数据,便于产生unicode中的ascii字符

11   memset(wbuf,0,sizeof wbuf);

12 while(*psrc!='\0'){

13 if(*psrc < 0){//如果不是ascii字符

14   uchar[1] = ((psrc[0] & 0x0f)<<4) + ((psrc[1] >>2) & 0x0f);

15 uchar[0] = ((psrc[1] & 0x03)<<6) + (psrc[2] & 0x3f);

16 psrc += 3;//对于非ascii字符,utf8编码里占3字节

17   uchar += 2;//windows中宽字符占2字节,在linux下应改为4

18   }

19 else{

20 uchar[0] = psrc[0];//对于ascii字符,可以直接复制

21   psrc += 1;

22 uchar += 2;

23 }

24 }

25 setlocale(lc_all,"");//默认的字符集是“c”,在这里改为本地字符集

26 //在cp936的系统中,上面这句调用等价于setlocale(lc_all,".936");

27   wcstombs(buf,wbuf,100);

28 //正确调用setlocale函数后,我们可以安全的使用wcstombs了

29 //它会为我们转换字符编码格式,从unicode转为本地字符集

30   return buf;

31 }

32 

33  int main()

34 {

35 char *text = null;

36 text = utf8tolocal("\xe6\xb1\x89\xe5\xad\x97");

37 printf("%s\n",text);

38 return 0;

39 }

40  

这里的"\xe6\xb1\x89\xe5\xad\x97"是一串utf8编码的字符串,内容是“汉字”。

继续阅读