支持HW团队,就支付宝领取下面的红包吧!(2018年3月31前,就几毛,也会几块,可以和其他红包叠加使用),你领取消费,HW有奖励。红包使用无条件限制,有条件请注意是不是有病毒。

小伙伴们,给大家发红包喽!人人可领,领完就能用。祝大家领取的红包金额大大大!#吱口令#长按复制此消息,打开支付宝就能领取!er1OEj73Uj

登入 注册 | 验证
| 搜索
HelloWorld论坛 : > 计算机科学、技术、教学> 编程专题> 开源免费项目> [转]SDL 多语言支持 完美显示中文
 
 
 
 
类别:游戏 阅读:5382 评论:0 时间:五月 14, 2013, 10:45 p.m. 关键字:SDL

 

来源:
http://www.cppblog.com/lf426/archive/2008/03/30/45723.html
http://www.cppblog.com/lf426/archive/2008/03/30/45733.html
http://www.cppblog.com/lf426/archive/2008/03/30/45738.html
http://www.cppblog.com/lf426/archive/2008/03/31/45796.html
http://www.cppblog.com/lf426/archive/2008/03/31/45801.html
作者:龙飞 

1、多语言支持,Win32下的GetText([原]开发环境配置(boost log4cxx ACE SDL 等)查看GetText部分)

自从开始研究SDL的文本显示,我就一直在思考在SDL中显示中文的问题。我们知道韦诺之战(Battle for Wesnoth)使用SDL开发的,并且支持多语言。所以,我一直相信Wesnoth的源代码里面一定有我所需要的答案。网络上是纵说纷纭啊,有些人干脆说,SDL不支持中文;有些人在困难面前回到了MFC的怀抱。而,既然我的目标是跨平台,并且我也相信一定能找到答案,所以,我坚持寻找。终于,完美解决了在SDL中显示中文,甚至多语言的问题。以下的几节,我将全面,详细的说明这些方法。

1.1:po,mo与gettext

线索从Wesnoth的发布游戏与源代码中开始,我们知道,在Wesnoth游戏中,有个名为po的文件夹,多国语言翻译都放在了这个文件夹下面。游戏程序中多为*.mo文件,源代码中多为*.po文件。通过搜索,po与mo的背景浮出水面——它们来自GNU项目gettext。
gettext项目是专门为多语言设计的。我们不需要修改源代码和程序的情况下,可以让程序支持多国语言。程序将根据系统所在的国家和区域选择相应的语言,当然,也可以在执行过程中让玩家自由的选择。既然是开放源代码的,自然也很容易的被移植到win32下。win32下的这个项目主页如下:
http://gnuwin32.sourceforge.net/packages/gettext.htm
为了方便的使用,我还是建议你下载完整的安装包(Complete package)。然后,你可以看英文说明,也可以凭着直觉去试验,找到哪些库和哪些DLL文件是编译和运行时必须的——当然,我也可以直接告诉你答案。
设置编译环境的问题就不再多说了,不清楚的请看前面的章节。反正都三部分:*.h文件,*.lib文件和*.dll文件,放到相应的文件夹下面并在编译时候指明就可以了。
我们下面将用到的文件有:
libintl.h:请在写源代码的时候#include进来;
libintl.lib:这是编译时候需要的库文件;
libintl3.dll和libiconv2.dll:这是程序运行时候需要的文件,放到*.exe文件可以找到的地方。

1.2:演示程序以及说明

#include <iostream>
#include <string>
#include <clocale>
#include "GNU/libintl.h"

int main(int argc, char* argv[])
{
        setlocale(LC_ALL, "");
        bindtextdomain("myText", "E:/My Documents/Visual Studio 2008/po");
        textdomain("myText");
        std::string test = gettext("Hello, World!");
        std::cout << test << std::endl;
        return 0;
}

我们先说#include进来的<clocale>,我用“<>”表示它是标准C++的一部分。它包含了函数setlocale()。这个函数在这里的两个参数——常量LC_ALL与空字符串""的意思是,在这个程序中的所有语言与区域,都设置为系统默认的语言与区域。
libintl.h是我们刚才加入的GNU的一部分,这意味着在Linux系统下,这个头文件是系统本身自带的。它包含了后面三个函数:bindtextdomain()将一个文件夹目录绑定到一个域名上,这个域名也是将来*.mo文件的文件名;textdomain()表明我们将使用的域名;gettext()中的字符串将是被多语言翻译替换的部分。
将这个程序编译,在没有多语言包的时候,程序也能正常的运行,显示“Hello, World!”。

1.3:为源程序制作po文件和mo文件

如果你已经安装了完整的安装包,找到相关文件夹的bin目录,这里有很多工具软件。你可以通过cmd的方式一步步的转换,也可以,偷点儿懒,因为有更加现成的工具可以用。但是,第一步,从源代码提取gettext()的文本,还得靠命令:xgettext。就跟用g++命令一样,假设我们的源文件名是main.cpp,我们把它先转换成一个模板文件a.pot:

xgettext -o a.pot main.cpp

你可以用vim之类的文本编辑器看看*.pot文件的内容,你会发现,一些说明,以及提取文本的详细信息被纪录了下来。

# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2008-03-30 00:24+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=CHARSET\n"
"Content-Transfer-Encoding: 8bit\n"

#: main.cpp:11
msgid "Hello, World!"
msgstr ""

下面,我们使用一个简单的小工具poedit。又一个跨平台的软件,主页在:
http://www.poedit.net/
安装运行后,选择“从POT文件更新类目”,然后打开我们刚才的a.pot,什么都不用修改(当然,你也可以把自己信息都写上去),确保“字符集”是UTF-8就可以了。然后,在英语下面也上替换的文字吧,保存的时候,相应的mo文件也就建立起来了。

msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2008-03-30 00:24+0800\n"
"PO-Revision-Date: 2008-03-30 00:25+0800\n"
"Last-Translator: lf426 <zbln426@163.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"

#: main.cpp:11
msgid "Hello, World!"
msgstr "浜茬埍鐨勪笘鐣岋紝鎴戞潵浜嗭紒"

这是po文件。怎么是乱码?那是因为windows不是用UTF-8保存的文本文件(默认一般是GB2312)。用poedit打开时候是正确显示的。我的文本内容是:“亲爱的世界,我来了!”。
如果你用的是vim,可以通过设置环境变量解决显示乱码的问题,在_vimrc文件中添加这一句:

set fileencodings=gb2312,ucs-bom,utf-8,chinese

1.4:设置mo文件的目录

下面的工作可能就有些教条了。还记得我们绑定域名的路径吧,我用的是
E:\My Documents\Visual Studio 2008\po
(请注意在C++程序里面把斜杠反过来!)
*.mo文件并不是直接放到这个路径下,而是这个路径下的./LL/LC_MESSAGES或者./LL_CC/LC_MESSAGES。其中LL表示语种,CC表示国家或区域。具体的请参考Wesnoth。就我们的中文来说,这个例子放mo文件的路径是:
E:\My Documents\Visual Studio 2008\po\zh_CN\LC_MESSAGES
现在运行程序就可以看到文本已经被替换了。如果我们删除mo文件或修改mo文件名(与绑定域名不一致),程序会继续显示原来的英文。如果我们改变系统环境,只要不是中国中文,程序都还是显示英文。如果我们要更新替换内容,直接用poedit更新po和mo文件就可以了。

1.5:构建StringData类

我们希望字符串的数据单独的保存在一个文件里,这样既方便被gettext提取,也方便修改。而且,在程序里面,我们尽量把gettext涉及到的一些特殊的设置隐藏了。所以,我们构建StringDada类,在程序中需要用到的地方,直接调用它的对象就可以了。

//FileName: string_data.h
#ifndef STRING_DATA_H
#define STRING_DATA_H

#include <clocale>
#include <string>
#include <vector>
#include "GNU/libintl.h"

class StringData
{
private:
        std::vector<std::string> data;
public:
        StringData();
        std::string operator [](const unsigned int& n) const;
};

#endif

我重载了[],这样在调用数据的时候更加直观。我们将数据都写在StringData的构造函数中,将来gettext也只需要提取StringData的实现文件就可以了。

#include "string_data.h"

StringData::StringData()
{
        setlocale(LC_ALL, "");
        bindtextdomain("StringData", "./po");
        textdomain("StringData");

        //0
        data.push_back(gettext("Up was pressed."));
        //1
        data.push_back(gettext("Down was pressed."));
        //2
        data.push_back(gettext("Left was pressed."));
        //3
        data.push_back(gettext("Right was pressed."));
        //4
        data.push_back(gettext("Other key was pressed."));
}

std::string StringData::operator [](const unsigned int& n) const
{
        if ( n >= data.size() )
                return 0;
        return data[n];
}

1.6:做个gettext的批处理

如果你按照我全面介绍的,安装了Poedit,也安装了GnuWin32,那么,我们做个批处理文件让从string_data.cpp到StringData.mo的转换更加简单吧。(如果安装路径不一样请做相应的修改)。

@set path=C:\Program Files\GnuWin32\bin;%PATH%;
xgettext --force-po -o string_data.pot string_data.cpp
msginit -l zh_CN -o StringData.po -i string_data.pot
@set path=C:\Program Files\Poedit\bin;%PATH%;
poedit StringData.po
del string_data.pot
del StringData.po

Poedit打开StringData.po的时候会报错,那是因为文件指明的编码不可用,请在“字符集”中选择UTF-8,另外,在“工程名称以及版本”中写点信息,不要使用默认值就可以了。然后翻译并保存,StringData.mo文件就生成了。

2、直接通过Unicode让SDL显示中文

2.1:SDL本身可以显示中文吗?

SDL的扩展库SDL_ttf本身具备显示中文的功能吗?网上很多观点,说不能显示的,甚至做了分析解释了原因。但是,事实是,SDL本身就可以显示中文。如果我们看看SDL_ttf.c的源代码,我们可以看到,最终用于构建SDL_Surface平面的函数,在三种显示模式(Solid, Shaded, Blended)下,都是其对应的TTF_RenderUNICODE_Xxx()函数。我们以TTF_RenderUNICODE_Blended()为例:
SDL_Surface *TTF_RenderUNICODE_Blended(TTF_Font *font, const Uint16 *text, SDL_Color fg)
可以看到,Unicode码是通过Uint16的数组传递的。在官方文档中,采用这样的形式:

// Render some UNICODE text in blended black to a new surface
// then blit to the upper left of the screen
// then free the text surface
//SDL_Surface *screen;
SDL_Color color={0,0,0};
SDL_Surface *text_surface;
Uint16 text[]={'H','e','l','l','o',' ',
'W','o','r','l','d','!'};
if(!(text_surface=TTF_RenderUNICODE_Blended(font,text,color))) {
//handle error here, perhaps print TTF_GetError at least
} else {
SDL_BlitSurface(text_surface,NULL,screen,NULL);
//perhaps we can reuse it, but I assume not for simplicity.
SDL_FreeSurface(text_surface);
}

请注意对Uint16 text[]的定义,既然是Uint16的数组,也意味着我们可以直接用数字作为这个数组的元素。

2.2:获得字符串的Unicode码

SDL的不能正确显示中文的问题,首先出在SDL_ttf没有提供正确渲染中文的函数,或者再精确点说,没有提供正确渲染GB2312码的函数。SDL_ttf提供了渲染UTF-8的函数,但是很不幸,汉字信息并不是通过UTF-8传递给程序的——即使是我们在po文件中指明了使用UTF-8,程序调用的时候依然被转化成了GB2312——至少在win32下是这样。
所以,显示中文最简单的办法——同时也是最复杂的实现,即直接给渲染Unicode的SDL_ttf函数传递汉字字符串的Unicode码。我们可以通过MFC的函数得到,而事实上,我根本没装MFC。获得汉字Unicode的方法,一方面可以查表,另外,也可以通过工具软件查找。以下是个实用的小工具,事实上,在以后的研究中,我一直用这个工具检验编码之间转换的正确性。
http://blog.ednchina.com/chinaluou/85656/Message.aspx
这下就简单了。比如我们要输出“你好”,找到它的Unicode码:4F60 597D。因为SDL是通过Uint16传递Unicode的,所以,对应的数组应该写成:(别忘了最后加一个空元素表示结尾)
Uint16 text[] = {0x4F60, 0x597D, 0};
这样,SDL就可以正确的显示中文了。注意:请使用支持中文的字库TTF文件。
但是很麻烦,不是吗?我们当然希望汉字是可以自动转换为SDL可以渲染的编码,所以,研究还得继续。所有的问题总是能解决的。

3、字符集之间的转换,win32下的libiconv([原]开发环境配置(boost log4cxx ACE SDL 等)查看GetText部分)

3.1:GNU的libiconv项目
http://www.gnu.org/software/libiconv/
再一次的,感谢伟大的GNU。我们需要的是Unicode码,在程序中转换,我们需要相应的库。libiconv支持许多字符集,包括我们将用到的GB2312,UTF-8和UCS-2(Unicode)。具体的,在项目主页上有详细的说明。我们需要新学习直接用的类容并不繁多,同样的,如果你没什么兴趣自己编译源代码,可以直接用在win32下编译好的头文件,库和动态链接库(DLL)。win32下的项目主页是:
http://gettext.sourceforge.net/
有趣的是,作者把它作为了我们前面提到的gettext的一部分。在下载页面上,我们直接选择libiconv-win32,同样的,我直接给出所需要的三部分文件的相关信息:
iconv.h:头文件,请在C++代码中#include进来;
iconv.lib:库文件,在编译时候使用;
iconv.dll:动态链接库,请放到exe文件能找到的路径下(通常与exe在同一文件夹下面)
下面,我们看看libiconv的使用方法。

3.2:libiconv的演示程序
我们还是边写程序边做说明:

#include <iostream>
#include <string>
#include <iomanip>
#include "GNU/iconv.h"

void showHex(int x);

<iomanip>和showHex函数,是用来现实16进制的。我们在前面用过。

int main(int argc, char* argv[])
{
        //src string
        const std::string str = "你好";
        //string size
        const int STR_SIZE = 256;
        //string to be changed
        const unsigned char* src = (const unsigned char*)(str.c_str());
        size_t src_len = strlen((char*)src);
        //string after changed
        unsigned char dst[STR_SIZE] = {0};
        size_t dst_len = sizeof(dst);
        //iconv's arg
        const unsigned char* in = src;
        unsigned char* out = dst;
        std::cout << "src: " << src << std::endl;

我们用来转换的字符串是“纯中文”(为什么我要加引号重点说明,后面会有原因的解释)"你好"。STR_SIZE是预留的转换内存空间。为什么不用动态存储呢?因为我试过,有错误,可能是iconv自身的限制。src是C风格的源字符串,dst是转换后的unsigned char数组。in和out是用于inconv的参数。

        //GB2312 to UCS-2 (Unicode)
        iconv_t cd;
        cd = iconv_open("UCS-2", "GB2312");
        if ((iconv_t)-1 == cd){
                return -1;
        }
        iconv(cd, (const char**)&in, &src_len, (char**)&out, &dst_len);
        iconv_close(cd);

这一段是编码的转换,详细内容请查阅iconv的doc。

        //Unicode dst
        std::cout << "dst: ";
        int unicode_len = strlen((char*)dst);
        for (int i = 0; i < unicode_len; i++) {
                showHex(dst[i]);
        }
        std::cout << std::endl;

        return 0;
}

void showHex(int x)
{
        using namespace std;
        cout << hex;
        cout << "0x" << setw(4) << setfill('0') << x << " ";
        cout << dec;
}

最后一部分是显示转换后代码。包括函数showHex()。

3.3:iconv的问题。

我们似乎已经解决问题了。但是有一个问题是,这样转换的编码是8位的,即unsigned char,而SDL需要的是16位的,即Uint16;第二个问题是,我说了,这是纯中文,你试试在“你”和“好”之间加段英语是什么效果?结论是,转换不能正常进行,在遇到第一个非汉字的时候,就终止了。
问题还是没解决,研究还得继续。

4、用iconv获得正确的Unicode,使用FriBiDi实现UTF-8到Unicode的正确转换

4.1:为什么iconv不能完全正确的转换Unicode?

我不是先知,教程里面是整理过的思路和逻辑顺序,在我研究这个问题的时候,头绪远远比教程里面乱得多。我完全是从Wesnoth的源代码去分析问题的,所以,为什么会扯上UTF-8和FriBidi,那也是因为在源代码中找到了线索。
iconv不能完全正确的获得Unicode,也就是我们刚才遇到的纯汉字转换没问题,而有英文就不行了。我并不清楚这是win32下的问题,还是在Linux下也这样,我也不清楚具体的算法和问题的根本原因,我只是通过试验得到一个算是表面原因的结论:我们知道,GB2312和Unicode汉字都使用2个字节(在UTF-8中是3个字节),英文和数字等用1个字节。iconv在得到两个字节(unsigned char即一个字节大小)代码的时候可以正确的将GB2312转化为Unicode(或者UTF-8),但是只有1个字节的时候则在转化Unicode的时候终止了,幸运的是,如果是转化为UTF-8则可以正确的进行,并且也转化为1个字节的UTF-8(只限于英文,数字等)。
所以,我们可以先通过iconv将原来的GB2312转化为UTF-8——汉字用3个字节(3个单位的unsigned char),英文、数字和基本符号用1个字节(1个单位的unsigned char)。然后,我们需要一个函数,将这种形式的UTF-8转换为SDL所需要的Uint16的Unicode。什么样的函数可以实现这种转换呢?

4.2:其它编码与Unicode之间的双向转换,GNU FriBidi
http://fribidi.freedesktop.org/wiki/
FriBidi是一个致力于Unicode编码与其它编码相互转换的开源项目,到目前为止,还是一个尚未完成的项目。我在研究Wesnoth源代码的时候看到这样的函数:fribidi_utf8_to_unicode(),所以,我想在这个函数中可能应该包含UTF-8到Unicode的算法——希望不要太复杂。在FriBidi项目中找到这个函数,它在文件fribidi_char_sets_utf8.c下面:

/* warning: the length of input string may exceed the length of the output */
int fribidi_utf8_to_unicode (char *s, int len, FriBidiChar *us)
{
        int length;
        char *t = s;

        length = 0;
        while (s - t < len)
        {
                if (*(unsigned char *) s <= 0x7f) /* one byte */
                {
                        *us++ = *s++; /* expand with 0s */
                }
                else if (*(unsigned char *) s <= 0xdf) /* 2 byte */
                {
                        *us++ =
                                ((*(unsigned char *) s & 0x1f) << 6) +
                                ((*(unsigned char *) (s + 1)) & 0x3f);
                        s += 2;
                }
                else /* 3 byte */
                {
                        *us++ =
                                ((int) (*(unsigned char *) s & 0x0f) << 12) +
                                ((*(unsigned char *) (s + 1) & 0x3f) << 6) +
                                (*(unsigned char *) (s + 2) & 0x3f);
                        s += 3;
                }
                length++;
        }
        *us = 0;
        return (length);
}

其中,我们找到FriBidiChar的定义,类似Uint32的类型;另外,函数用char表示1字节的单位。根据我的试验,至少在VC2008下是有错误的,我们一直用的是unsigned char表示1字节的单位,所以,我们需要对这个函数做些修改:
 

int myUTF8_to_UNICODE(Uint16* unicode, unsigned char* utf8, int len)
{
        int length;
        unsigned char* t = utf8;

        length = 0;
        while (utf8 - t < len){
                //one byte.ASCII as a, b, c, 1, 2, 3 ect
                if ( *(unsigned char *) utf8 <= 0x7f ) {
                        //expand with 0s.
                        *unicode++ = *utf8++;
                }
                //2 byte.
                else if ( *(unsigned char *) utf8 <= 0xdf ) {
                        *unicode++ = ((*(unsigned char *) utf8 & 0x1f) << 6) + ((*(unsigned char *) (utf8 + 1)) & 0x3f);
                        utf8 += 2;
                }
                //3 byte.Chinese may use 3 byte.
                else {
                        *unicode++ = ((int) (*(unsigned char *) utf8 & 0x0f) << 12) +
                                ((*(unsigned char *) (utf8 + 1) & 0x3f) << 6) +
                                (*(unsigned char *) (utf8 + 2) & 0x3f);
                        utf8 += 3;
                }
                length++;
        }

        *unicode = 0;

        return (length);
}

4.3:将汉字,英文,数字和符号都正确的转换为16位的Unicode

有了iconv和上面这个函数,我们终于可以将GB2312的编码正确的转换为Unicode了。

//FileName: gb2312_to_Unicode.h
#ifndef GB2312_TO_UNICODE_H_
#define GB2312_TO_UNICODE_H_

#include <iostream>
#include <vector>
#include "GNU/iconv.h"
#include "SDL/SDL.h"

std::vector<Uint16> getUnicode(const std::string& str);

#endif

实现文件中包含我们上面写的从UTF-8到Unicode的函数:

#include "gb2312_to_Unicode.h"

int myUTF8_to_UNICODE(Uint16* unicode, unsigned char* utf8, int len);

std::vector<Uint16> getUnicode(const std::string& str)
{
        const int CHAR_SIZE = 256;
        //GB2312 src
        const unsigned char* src = (const unsigned char*)(str.c_str());
        size_t src_len = strlen((char*)src);
        //Unicode dst to get
        unsigned char dst[CHAR_SIZE] = {0};
        size_t dst_len = sizeof(dst);
        //iconv arg
        const unsigned char* in = src;
        unsigned char* out = dst;

        iconv_t cd;
        //GB2312 to UTF-8
        cd = iconv_open("UTF-8", "GB2312");
        if ((iconv_t)-1 == cd){
                exit (-1);
        }
        //conversion
        iconv(cd, (const char**)&in, &src_len, (char**)&out, &dst_len);

        //UTF-8 to Unicode
        int utf8Len = strlen((char*)dst);
        Uint16 unicodeData[CHAR_SIZE] = {0};
        int unicodeLen = myUTF8_to_UNICODE(unicodeData, dst, utf8Len);
        std::vector<Uint16> unicodeVectorArray;
        for (int i = 0; i < unicodeLen; i++) {
                unicodeVectorArray.push_back(unicodeData[i]);
        }

        iconv_close(cd); 
        return unicodeVectorArray;
}

函数把一个std::string转换位Uint16的vector数组并返回,这正是SDL所需要的Unicode格式。

5、SDL完美显示中文

注意:请使用支持中文的TTF字库。
5.1:构建可以正确显示中文的SDL_ttf函数

世界终于又充满了光明!任何事情都是有答案的,不知道仅仅是因为我们还没有找到。解决了以上一系列问题,我们终于可以不依赖MFC,完全使用自由开源的资源,让SDL显示中文了!我们通过TTF_RenderUNICODE_Xxx()来构建这些函数:

//FileName: font.h
#ifndef FONT_H_
#define FONT_H_

#include "gb2312_to_Unicode.h"
#include "SDL/SDL_ttf.h"

SDL_Surface* myTTF_RenderString_Blended(TTF_Font* font, const std::string& str, SDL_Color fg);
SDL_Surface* myTTF_RenderString_Solid(TTF_Font* font, const std::string& str, SDL_Color fg);
SDL_Surface* myTTF_RenderString_Shaded(TTF_Font* font, const std::string& str, SDL_Color fg, SDL_Color bg);

#endif

三种显示模式的实现文件:

#include "font.h"

SDL_Surface* myTTF_RenderString_Blended(TTF_Font* font, const std::string& str, SDL_Color fg)
{
        SDL_Surface* textbuf;
        //Get Unicode
        std::vector<Uint16> unicodeUnit = getUnicode(str);
        int arraySize = unicodeUnit.size();
        Uint16* perOne = new Uint16[arraySize+1];
        for ( int i = 0; i < arraySize; i++ )
                perOne[i] = unicodeUnit[i];
        perOne[arraySize] = 0;


        //Render the new text
        textbuf = TTF_RenderUNICODE_Blended(font, perOne, fg);

        //Free the text buffer and return
        delete [] perOne;
        return textbuf;
}

SDL_Surface* myTTF_RenderString_Solid(TTF_Font* font, const std::string& str, SDL_Color fg)
{
        SDL_Surface* textbuf;
        //Get Unicode
        std::vector<Uint16> unicodeUnit = getUnicode(str);
        int arraySize = unicodeUnit.size();
        Uint16* perOne = new Uint16[arraySize+1];
        for ( int i = 0; i < arraySize; i++ )
                perOne[i] = unicodeUnit[i];
        perOne[arraySize] = 0;


        //Render the new text
        textbuf = TTF_RenderUNICODE_Solid(font, perOne, fg);

        //Free the text buffer and return
        delete [] perOne;
        return textbuf;
}

SDL_Surface* myTTF_RenderString_Shaded(TTF_Font* font, const std::string& str, SDL_Color fg, SDL_Color bg)
{
        SDL_Surface* textbuf;
        //Get Unicode
        std::vector<Uint16> unicodeUnit = getUnicode(str);
        int arraySize = unicodeUnit.size();
        Uint16* perOne = new Uint16[arraySize+1];
        for ( int i = 0; i < arraySize; i++ )
                perOne[i] = unicodeUnit[i];
        perOne[arraySize] = 0;


        //Render the new text
        textbuf = TTF_RenderUNICODE_Shaded(font, perOne, fg, bg);

        //Free the text buffer and return
        delete [] perOne;
        return textbuf;
}

5.2:修改DisplaySurface的类方法

其它接口都是不需要改动的,我们仅仅把类方法中,原来用于构建文本面的函数换成我们自己的函数就可以了。当然,先把这些函数#include进来:

#include "SurfaceClass.h"
#include "font.h"

//

DisplaySurface::DisplaySurface(const std::string& msg_name, const std::string& message, const ScreenSurface& screen,
        Uint8 r, Uint8 g , Uint8 b,
        int ttf_size, const std::string& ttf_fileName):
fileName(msg_name)
{
        if ( textNum == 0 )
                if ( TTF_Init() < 0 )
                        throw ErrorInfo("TTF_Init() failed!");

        SDL_Color textColor;
        textColor.r = r;
        textColor.g = g;
        textColor.b = b;

        pFont = TTF_OpenFont(ttf_fileName.c_str(), ttf_size);
        if ( pFont == 0 )
                throw ErrorInfo("TTF_OpenFont() failed!");

        pSurface = myTTF_RenderString_Blended(pFont, message, textColor);
        if ( pSurface == 0 )
                throw ErrorInfo("myTTF_RenderString_Blended() failed!");
        pScreen = screen.point();

        textNum++;
}

5.3:StringData在主程序中的调用

最后,我们演示一下StringData在主程序中的调用方法。

//must #include "string_data.h"

//Load a textSurface
StringData myData;
const std::string uInfo = myData[0];
const std::string dInfo = myData[1];
const std::string lInfo = myData[2];
const std::string rInfo = myData[3];
const std::string oInfo = myData[4];
TextSurface upMessage("upMsg", uInfo, screen);
TextSurface downMessage("downMsg", dInfo, screen, 0xFF, 0, 0);
TextSurface leftMessage("leftMsg", lInfo, screen, 0, 0xFF, 0);
TextSurface rightMessage("rightMsg", rInfo, screen, 0, 0, 0xFF);
TextSurface otherMessage("otherMsg", oInfo, screen, 100, 100, 100, 35);

嘿嘿,就这么简单!

5.4:本章演示程序和完整源代码下载

包含SDL显示中文的演示程序(win32)以及完整的源代码。
http://www.fs2you.com/zh-cn/files/62f0acf0-ff11-11dc-a4f4-0014221b798a/

[挂载人]初学MPEG [审核人]初学MPEG 推荐

个人签名--------------------------------------------------------------------------------

Please Login (or Sign Up) to leave a comment