作者: bombless 来源: 博客园 发布时间:2010-11-21 15:23 阅读:116 次 原文链接 [收藏]
有时候在关于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字符串正确的使用文件名中带中文的文件。
代码
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
这里有一个短小的多的函数解释了从本地字符集到UTF8的相互转化。
代码——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编码的字符串,内容是“汉字”。