在SDL工程中让SDL_ttf渲染汉字

简介: <p style="color: rgb(73, 73, 73); font-family: simsun; font-size: 14px; line-height: 21px; background-color: rgb(215, 215, 215); margin-top: 0px; margin-bottom: 5px; padding-top: 0px; padding-bottom

有时候在关于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_SolidTTF_RenderText_SolidTTF_RenderUNICODE_Solid三个函数。

其中TTF_RenderText_Solid能正确渲染ascii字符串,而TTF_RenderUTF8_SolidTTF_RenderUNICODE_Solid分别用于渲染UTF8编码的和UCS2编码的字符串。

 

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

 

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

 

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

 

在windows中配合WideCharToMultiByteMultiByteToWideChar转换字符串的格式当然很好,不过如果使用wcstombsmbstowcs时,需要注意的是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编码的字符串,内容是“汉字”。

相关文章
|
存储 机器学习/深度学习 分布式计算
大数据技术——解锁数据的力量,引领未来趋势
【10月更文挑战第5天】大数据技术——解锁数据的力量,引领未来趋势
|
算法 调度 UED
深入理解操作系统内存管理:原理与实践
【4月更文挑战第23天】 在现代计算机系统中,操作系统的内存管理是保证系统高效、稳定运行的关键组成部分。本文旨在深入探讨操作系统中内存管理的理论基础、关键技术以及实际操作过程,通过对内存分配策略、虚拟内存技术、分页与分段机制等核心概念的详细解析,为读者提供一个清晰、全面的内存管理视角。此外,文章还将通过案例分析,展示内存管理在解决实际问题中的应用,以期加深读者对操作系统内存管理复杂性的认识和理解。
|
缓存 Go 数据格式
千万不要在Go IO上踩这些雷!
千万不要在Go IO上踩这些雷!
243 0
|
28天前
|
人工智能 调度 开发工具
MemOS 正式上线魔搭社区 MCP 广场,让你的智能体拥有「长期记忆」
MemOS 正式上线魔搭社区 MCP 广场,作为首个大模型记忆操作系统,支持标准化记忆读写,7天调用量超14.9万次。开发者可一键集成,让AI具备持久化、可调度的记忆能力,实现连续思考与长期进化。
264 3
|
10月前
|
人工智能 机器人
Flowable + Claude Desktop:AI驱动的RPA新玩法
Flowable与Anthropic推出的Claude Desktop联手,带来AI驱动的自动化新体验。用户只需告知目标,系统便能自动分析屏幕、规划步骤,完成任务如网页数据抓取或表单填写。相比传统RPA,它更灵活智能,适合应对不确定性场景。通过“看懂屏幕、理解目标”,Claude Desktop在复杂环境中游刃有余,尤其擅长处理界面更新和突发情况。这种组合将Flowable的流程管理和AI灵活性完美融合,为自动化领域注入新活力。
404 3
【进程通信】信号的捕捉原理&&用户态与内核态的区别
【进程通信】信号的捕捉原理&&用户态与内核态的区别
|
JSON 前端开发 Java
深入SpringMVC聊跨域问题的最佳实践
本文将深度剖析SpringMVC中的跨域问题及其最佳实践,尤其聚焦于Jsonp接口在技术升级中遇到的跨域难题。结合一个具体的案例,展示在技术升级过程中,原有JSONP接口出现跨域问题的表现及原因,以及如何通过自定义SpringMVC的拦截器和MessageConvertor来解决这个问题。
|
监控 数据挖掘 大数据
阿里云开源利器:DataX3.0——高效稳定的离线数据同步解决方案
对于需要集成多个数据源进行大数据分析的场景,DataX3.0同样提供了有力的支持。企业可以使用DataX将多个数据源的数据集成到一个统一的数据存储系统中,以便进行后续的数据分析和挖掘工作。这种集成能力有助于提升数据分析的效率和准确性,为企业决策提供有力支持。
费德勒权变模型(Fiedler Contingency Model)详解与Python代码示例
费德勒权变模型(Fiedler Contingency Model)详解与Python代码示例
|
程序员 Linux 开发工具
老程序员分享:OpenCPN介绍及编译
老程序员分享:OpenCPN介绍及编译
776 4