天天看點

在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編碼的字元串,内容是“漢字”。

繼續閱讀