在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字符串正确的使用文件名中带中文的文件。

 

代码
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编码的字符串,内容是“汉字”。

时间: 2024-08-03 10:47:10

在SDL工程中让SDL_ttf渲染汉字的相关文章

制作自己的字库并在工程中显示

制作自己的字库并在工程中显示 此篇教程操作很复杂,但有着实用价值,可以用来提取网上下载的字库并制作自己的字库拿到工程项目中去显示.有时候加载自定义中文字体会非常大,动辄8-9M大小的中文字库还是很占大小的,而我们也只需要里面的几个汉字,这篇博文就是做这事情的. 首先是制作字体篇 1. 下载工具 2. 下载字体 3. 将字体导入到工具中,并根据汉字查找出汉字 4. 新建自己的字体 5. 查找出游贤明3个字的字符,并复制粘贴,赋值代码点以及名称 6. 导出为ttf字体 这样子就制作好自定义字体了.

怎么去掉Xcode工程中的某种类型的警告

问题描述  在我们的项目中,通常使用了大量的第三方代码,这些代码可能很复杂,我们不敢改动他们,可是作者已经停止更新了,当sdk升级或者是编译器升级后,这些遗留的代码可能会出现许许多多的警告,那么我们有没有办法去掉这些烦人的警告,不然一个工程几百个警告,你看着怎么都不爽吧.我们怎么去掉警告呢? 1. 最直接.最一劳永逸.最安全的方式,直接找到警告的那段代码,改为不警告.这个方式最安全. 可是它有一个问题,就是,当我们很多文件都有这种类型的警告的时候,我们就需要改动很多很多的源码了, 对于不是我们写

如何在SQL Server2000中处理半个汉字的问题

server|汉字|问题 /* written by enydraboy ,2003-07-17 *//*发表于CSDN *//* 转载请注明出处和保留此版权信息 */ 在csdn的专家论坛中,看到有一位朋友问起关于"由于从其它数据库中导入的数据存在半个汉字的问题,所以希望能在查询语句中将这半个汉字处理掉,怎么处理都行.望各位高手不吝赐教!"的问题,启初有点意外,但是后来觉得从这个问题可以带出一些SQL Server汉字中存储和处理的方法,觉得有必要总结一下. 汉字是由两个字节存储的,

在C#中应用MapObjects(渲染效果)

object 以前用MapX,里面有专题图,挺好.经过几天的试验,我把MapObjects中的各种渲染效果(类似MapX专题图,但不一样)都试了试,下面附上源代码,如果那位有不懂的地方请自己琢磨,哈哈- 代码如下: using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; name

VC在一个工程中实现多语言版本

前段时间笔者因为做一个程序,客户要求要有中英文版本,开始以为要做二个工程,只是资源文件不同,但是发现这样一个工程更新另外一个也要更新,实在是很麻烦 ,后来经过网上查找资料与自己亲身实验,发现其实只要在一个工程中就可以实现.但是这个工程中要有两个资源文件,当编译不同版本时,程序可以自动链接不同资源文件 .下面把具体的方法写出来.供大家参考一下. 第一步.创建中英文版本资源文件 我们先创建第一个中文版本工程,假设工程名称为 Example,在向导第一步的"What language would yo

Windows 7中的字体渲染

显示方面,在Vista的基础上,微软对Windows 7进行了多方面的改进,包括全新的任务栏.一些新的UI元素.新的驱动模型WDDM1.1等等.当然,也包括字体. ClearType ClearType是微软在Vista中引入的字体渲染方式.由于用户80%的时间几乎都要花在阅读上,微软特别看重用户在字体方面的体验,而ClearType得出现正是为了通过特殊的渲染方式给予LCD和CRT显示器以出色的显示效果的. PS:在Vista下,经常有人会问,字体发虚怎么办?其实只要右键 - 个性化 - 颜色

如何在Web工程中实现任务计划调度

好多朋友用过Windows的任务计划,也有不少程序迷自己曾写过时钟报警.系统自动关机等趣味程序,可却很少有朋友在Web工程中实现过类似功能.今天有空把笔者先前曾在Tomcat上实现的类似功能,搬出来与大家共享. 早在几年前,我公司跟某市财政局合作项目开发,为加强财政局对所属单位财务状况的有效监管,开发.实施了财政局数据中心项目.此项目采用B/S加C/S混合结构模式.财政局Web服务器上架设数据同步接收装置,由市属单位每天下班前把财务信息通过HTTP协议上传至财政局中心服务器,与Web服务器上的接

WorldWind系列十:RendableObject中的DirectX渲染分析:ImageLayer为例

学习WW有一段时间了,但是若想开发自己基于WW的插件,必然会遇到RendableObject中的DirectX渲染问题.所有需要渲染绘制的 WW三维插件,最终是通过继承RendableObject并实现自己的Initialize().Update().Render()方法的.想写自己的Render()方法不是简单的事情,你必然要学习DirectX编程,否则,你连看懂示例中的底层Render()方法都很难,谈何开发自己的插件. 为了突破DirectX编程对我学习WW插件的阻挠,我"快餐式"

WSS3SDK之如何定制mobile页面中的字段渲染

本节将对Windows SharePoint Services 3.0 的mobile设备访问页面中的字段渲染定制过程做一个概述.详细的步骤请参考随后的演练. 前面的Mobile页面渲染机制一节中我们说过,在页面里一个SPMobileListFIEldSelector模板选择器控件的调用,会导致从该控件开始的链式的调用. 基于当前的列表类型和字段(比如本例中的通知和标题),该对象决定了哪个RenderingTemplate会用于在mobile显示页面上渲染该字段. 该控件会寻找名为MobileC