SDL2 游戏开发之中文渲染
- W_Z_C
- 共 1271 字,阅读约 3 分钟
在游戏开发过程中,经常遇见显示中文的问题,如果你使用图形渲染中介绍的方法,会发现显示的文字是乱码,之所以是这样是因为 SDL 文字显示接口的用法决定的,这篇文章就简单的介绍一下 SDL 显示中文的具体过程。
在 vs2015 以后,vs 编辑器支持不同的文字编码,可以使用
u8
前缀标记字符串为 UTF8 编码,所以你可以使用下面的方式在 SDL 程序中显示中文:const char* test = u8"测试中文";
。具体相关信息可以查看 MSDN。
在原来介绍 SDL 文字渲染的内容中,我们介绍了 SDL 扩展库 SDL_ttf 的使用方法。其中使用了接口函数 TTF_RenderText_Blended 显示文字,不过这个接口仅仅支持西方语言的文本显示,如果想要显示其它类型的文本则需要 SDL_ttf 中的其它接口。
SDL_ttf 文字相关的接口一共有三类:
- 包含 Text 文本的接口,如:TTF_RenderText_Blended、TTF_SizeText。
- 包含 UTF8 文本的接口,如:TTF_RenderUTF8_Blended、TTF_SizeUTF8。
- 包含 UNICODE 文本的接口,如:TTF_RenderUNICODE_Blended、TTF_SizeUNICODE。
第一类函数接受的是 Latin1 编码的字符,Latin1 是 ISO-8859-1 的别名,有些环境下写作 Latin-1。Latin1 编码是单字节编码,向下兼容 ASCII,其编码范围是 0x00-0xFF,0x00-0x7F 之间完全和 ASCII 一致,0x80-0x9F 之间是控制字符,0xA0-0xFF 之间是文字符号。
第二类函数接受的是 UTF-8 编码的字符,UTF-8 是一种 Unicode 编码的实现方式,UTF-8 最大的特点是支持变长编码。
第三类函数接受的是 Unicode 编码的字符。Unicode 本身是一项规范,规定了符号对应的二进制码,但是并没有规定如何存储这些编码,所以才会出现 UTF-8、UTF-16、UTF-32 等不同的编码方式。
Unicode 字符集为每个字符分配一个码位,例如 “知” 的码位是 30693,十六进制表示为 0x77E5,所以记作 U+ 77E5。
UTF-8 用的一套 8 位为一个编码单位的边长编码方式,所以 UTF-8 可以用 C 语言的 char 型数组存储,其中每个字符可能用 1 到 4 个字节表示。
上边第三类函数接受的 Unicode 编码是只 UTF-16LE 的方式,LE 只的是小端,对应的还存在 UTF-16BE,而 UTF-8 是 UTF-8 BOM,BOM 存在 是为了区分 UTF-16LE、UTF-16BE 和 UTF-8,因为三种编码可能共存。GB2312 是中国早期给简体中文指定的编码,后来加入了繁体变为 GBK,后来融入日韩变为 GB18030。
选择字体
说了这么多,其实如果准备使用 SDL 显示中文,你需要使用后两种接口,推荐使用 UTF8,因为在其它非 Windows 平台大多以 UTF8 为默认字符集。
要想正确的显示你的中文文本,你需要先选择一个支持中文编码的字体:
因为中文的字符较多,所以大多数中文字体的大小都比较大,例如常见的黑体,有 9M 之多,与之相对的 常用的等宽字体 Courier New 大小只有 500kb 左右。
转换编码
一般我们操作系统默认是中文显示,所以默认的编码都是 GBK2312。这里需要先将 GBK2312 转换为 UTF-8 编码方式:
int GBKToUTF8(const char* gbk, int gbklen, char* utf8, int utf8len)
{
if (!(gbk && utf8)) {
return 0;
}
//转换为宽字符
int bufWLen = MultiByteToWideChar(CP_ACP, 0, gbk, gbklen, NULL, 0);
if (bufWLen == 0) {
return -1;
}
WCHAR* bufW = malloc(sizeof(WCHAR) * bufWLen);
if (!bufW) {
return -2;
}
int ret = MultiByteToWideChar(CP_ACP, 0, gbk, gbklen, bufW, bufWLen);
if (ret == 0) {
free(bufW);
return -3;
}
//转换为 UTF8
int bufMLen = WideCharToMultiByte(CP_UTF8, 0, bufW, -1, NULL, 0, NULL, NULL);
if (bufMLen == 0) {
free(bufW);
return -4;
}
CHAR* bufM = malloc(bufMLen);
if (!bufM) {
free(bufW);
return -5;
}
ret = WideCharToMultiByte(CP_UTF8, 0, bufW, -1, bufM, bufMLen, NULL, NULL);
if (ret == 0) {
free(bufW);
free(bufM);
return -6;
}
//空间不够,不截取...
if (bufMLen > utf8len) {
free(bufW);
free(bufM);
return -7;
}
//复制
memset(utf8, 0, utf8len);
memcpy(utf8, bufM, bufMLen);
free(bufW);
free(bufM);
return bufMLen;
}
文字显示
最后就是真正的显示阶段,首先加载支持汉字的字体:
TTF_Font* font = TTF_OpenFont("msyh.ttc", 48);
接着渲染显示文字的纹理图片:
SDL_Surface* surf = TTF_RenderUTF8_Blended(font, test, color);
SDL_Texture* tex = SDL_CreateTextureFromSurface(pRenderer, surf);
最后就是显示:
SDL_RenderCopy(pRenderer, ftex, NULL, NULL);
注意上面代码中使用的是 TTF_RenderUTF8_Blended 函数,如果你需要获取文本大小同样需要使用 TTF_SizeUTF8 函数。