让irrlicht支持中文输入和输出

irrlicht引擎很容易上手,对于新手来说是个不错的选择,但是由于其采用位图字体,不支持中文的输入和输出,这不禁有些遗憾。

正在装载数据……

不过借助freetype的强大功能,对irrlicht进行一些修改就可以让其实现中文的输入和输出。
       以下修改均基于irrlicht1.1和freetype2.1。读者可自行下载。设置好include和lib就可以开始了!
一、irrTypes.h
       加入typedef unsigned long u64;typedef wchar_t c16;以保持irrlicht风格,为后续工作做准备。

 

二、IGUIFont.h
       我们使用freetype实现汉字的输出,但是同一字号的字符高度是不尽相同的,所以在这里我们要加入一个函数虚函数来获得该字号字符的最大高度。
                     virtual s32 getMaxHight() =0;

三、IOSOperator.h
      修改三个虚函数的参数类型,以适用于宽字符。
       virtual const c16* getOperationSystemVersion() = 0;
virtual void copyToClipboard(const c16* text) = 0;
virtual c16* getTextFromClipboard() = 0;

四、COSOperator.h
      对应IOSOperator.h所做的修改,这里也要做相应的处理。
virtual const c16* getOperationSystemVersion();
virtual void copyToClipboard(const c16* text);
virtual c16* getTextFromClipboard();

五、COSOperator.cpp
      实现部分也要修改。
// Copyright (C) 2002-2006 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h

#include "COSOperator.h"
#include "IrrCompileConfig.h"

#ifdef _IRR_WINDOWS_
#include <windows.h>
#else
#include <string.h>
#endif

#ifdef MACOSX
#include "OSXClipboard.h"
#endif


namespace irr
{


// constructor
COSOperator::COSOperator(const c8* osVersion)
{
OperationSystem = osVersion;
}


//! destructor
COSOperator::~COSOperator()
{
}


//! returns the current operation system version as string.
const c16* COSOperator::getOperationSystemVersion()
{
return OperationSystem.c_str();
}


//! copies text to the clipboard
void COSOperator::copyToClipboard(const c16* text)
{
if (wcslen(text)==0)//if (strlen(text)==0)
return;

// Windows version
#ifdef _IRR_WINDOWS_
if (!OpenClipboard(0) || text == 0)
return;

EmptyClipboard();

HGLOBAL clipbuffer;
c16 * buffer;//char * buffer;

clipbuffer = GlobalAlloc(GMEM_DDESHARE, wcslen(text)*2+2);//clipbuffer = GlobalAlloc(GMEM_DDESHARE, strlen(text)+1);
buffer = (c16*)GlobalLock(clipbuffer);//buffer = (char*)GlobalLock(clipbuffer);

wcscpy(buffer, text);//strcpy(buffer, text);

GlobalUnlock(clipbuffer);
SetClipboardData(CF_UNICODETEXT, clipbuffer);//SetClipboardData(CF_TEXT, clipbuffer);
CloseClipboard();
#endif

#ifdef MACOSX
OSXCopyToClipboard(text);
#endif
}


//! gets text from the clipboard
//! /return Returns 0 if no string is in there.
c16* COSOperator::getTextFromClipboard()
{
#ifdef _IRR_WINDOWS_
if (!OpenClipboard(NULL))
return 0;

c16 * buffer = 0;//char * buffer = 0;

HANDLE hData = GetClipboardData( CF_UNICODETEXT );//HANDLE hData = GetClipboardData( CF_TEXT );
buffer = (c16*)GlobalLock( hData );//buffer = (char*)GlobalLock( hData );
GlobalUnlock( hData );
CloseClipboard();
return buffer;
#else
#ifdef MACOSX
return (OSXCopyFromClipboard());
#else
return 0;
#endif
#endif
}

 

} // end namespace

六、CGUIToolBar.cpp、CGUIStaticText.cpp、CGUIMenu.cpp、CGUIListBox.cpp、CGUIComboBox.cpp、
     将所有的
font->getDimension(L"A").Height
修改为
font->getMaxHight();


七、CGUIEditBox.cpp
      修改bool CGUIEditBox::processKey(SEvent& event)函数
.......
case KEY_KEY_C:
.......
core::stringw s;//core::stringc s;
.......
case KEY_KEY_X:
.......
core::stringw sc;//core::stringc sc;
.......
case KEY_KEY_V:
......
const c16* p = Operator->getTextFromClipboard();
//const c8* p = Operator->getTextFromClipboard();

八、CGUIFont.h

// Copyright (C) 2002-2006 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h

#ifndef __C_GUI_FONT_H_INCLUDED__
#define __C_GUI_FONT_H_INCLUDED__

#include "IGUIFont.h"
#include "irrString.h"
#include "IVideoDriver.h"
#include "irrArray.h"

#include "ft2build.h"
#include FT_FREETYPE_H

#pragma comment(lib,"freetype221MT.lib")
namespace irr
{
namespace gui
{

struct _Character
{
irr::core::rect<s32> CharRect;
video::ITexture *Texture;
c16 Character;
u32 UsedFor;
};

 

class CGUIFont : public IGUIFont
{
public:

//! constructor
CGUIFont(video::IVideoDriver* Driver);

//! destructor
virtual ~CGUIFont();

//! loads a font file
bool load(const c8* filename);

//! loads a font file
bool load(io::IReadFile* file);

//! draws an text and clips it to the specified rectangle if wanted
virtual void draw(const c16* text, const core::rect<s32>& position, video::SColor color, bool hcenter=false, bool vcenter=false, const core::rect<s32>* clip=0);

//! returns the dimension of a text
virtual core::dimension2d<s32> getDimension(const c16* text);

//! Calculates the index of the character in the text which is on a specific position.
virtual s32 getCharacterFromPos(const c16* text, s32 pixel_x);


virtual s32 getMaxHight();
private:

u32 CheckChar(c16 ch);
//获得字符在缓存中的位置,如果没有找到则加入该字符到缓存中
inline s32 getWidthFromCharacter(c16 c);

video::IVideoDriver* Driver;
core::array<_Character> Characters;
s32 WrongCharacter;


FT_Library    library;
FT_Face        face;
u32 MaxCache;//缓存中存储的最大字符数
s32 MaxHight;最大高度
s32 Size;//字号
};

} // end namespace gui
} // end namespace irr

#endif


九、CGUIFont.cpp
// Copyright (C) 2002-2006 Nikolaus Gebhardt
// This file is part of the "Irrlicht Engine".
// For conditions of distribution and use, see copyright notice in irrlicht.h

#include "CGUIFont.h"
#include "os.h"

namespace irr
{
namespace gui
{

//! constructor
CGUIFont::CGUIFont(video::IVideoDriver* driver)
: Driver(driver), WrongCharacter(L'?')
{
#ifdef _DEBUG
setDebugName("CGUIFont");
#endif
MaxCache=500;
MaxHight=0;
if (Driver)
Driver->grab();
}

 

//! destructor
CGUIFont::~CGUIFont()
{

FT_Done_Face(face);
FT_Done_FreeType(library);
if (Driver)
Driver->drop();
}

 

//! loads a font file
bool CGUIFont::load(io::IReadFile* file)
{

return load(file->getFileName());
}


//! loads a font file
bool CGUIFont::load(const c8* filename)
{
if (FT_Init_FreeType( &library ))
{
os::Printer::log("Init FreeType Library Error.", ELL_ERROR);
return false;
}
if (FT_New_Face( library,filename,0,&face ))
{
os::Printer::log("Init FreeType New Face Error.", ELL_ERROR);
return false;
}
FT_Set_Pixel_Sizes(face,0,16);
return true;
}

 

//! returns the dimension of a text
core::dimension2d<s32> CGUIFont::getDimension(const c16* text)
{
core::dimension2d<s32> dim(0,0);
u32 id;
c16 c;
while (*text)
{
c=*text;
id=CheckChar(c);

dim.Width+=Characters[id].CharRect.getWidth();
text++;
}
dim.Height=getMaxHight();
return dim;
}


inline s32 CGUIFont::getWidthFromCharacter(c16 c)
{
u32 i=CheckChar(c);

return Characters[i].CharRect.getWidth();
}


//! draws an text and clips it to the specified rectangle if wanted
void CGUIFont::draw(const c16* text, const core::rect<s32>& position, video::SColor color, bool hcenter, bool vcenter, const core::rect<s32>* clip)
{
if (!Driver)
return;

core::dimension2d<s32> textDimension;
core::position2d<s32> offset = position.UpperLeftCorner;

if (hcenter || vcenter)
{
textDimension = getDimension(text);

if (hcenter)
offset.X = ((position.getWidth() - textDimension.Width)>>1) + offset.X;

if (vcenter)
offset.Y = ((position.getHeight() - textDimension.Height)>>1) + offset.Y;
}

u32 n;

core::position2d<s32> pos=offset;
while(*text)
{
n=CheckChar(*text);
offset.Y=pos.Y;
//if(MaxHight>2*Characters[n].CharRect.getHeight()) 
offset.Y+=MaxHight-Characters[n].CharRect.getHeight();
//else offset.Y+=(MaxHight-Characters[n].CharRect.getHeight())/2;
Driver->draw2DImage(Characters[n].Texture, offset,Characters[n].CharRect, clip, color, true);

offset.X += Characters[n].CharRect.getWidth();

++text;
}
}


//! Calculates the index of the character in the text which is on a specific position.
s32 CGUIFont::getCharacterFromPos(const c16* text, s32 pixel_x)
{
s32 x = 0;
s32 idx = 0;

while (text[idx])
{
x += getWidthFromCharacter(text[idx]);

if (x >= pixel_x)
return idx;

++idx;
}

return -1;
}

 

u32 CGUIFont::CheckChar(c16 ch)
{
int min=0,c=0;
_Character chtmp;

for (;c<Characters.size();c++)
{
if(Characters[c].UsedFor<Characters[min].UsedFor) 
min=c;
if(Characters[c].Character==ch) 
{
Characters[c].UsedFor++;
return c;
}
}

int imgw = 1;
int imgh = 1;
u64 *texd;
FT_Bitmap bits;
int texw;
int texh;
s32 offset;
u32 idx=FT_Get_Char_Index(face, ch );
if(idx==0) idx=FT_Get_Char_Index(face, WrongCharacter );


if (!FT_Load_Glyph(face,idx,FT_LOAD_NO_HINTING|FT_LOAD_NO_BITMAP)){
FT_GlyphSlot glyph = face->glyph;
if (glyph->format == ft_glyph_format_outline ){
if (!FT_Render_Glyph( glyph, FT_RENDER_MODE_NORMAL)){
bits = glyph->bitmap;
unsigned char *pt = bits.buffer;
texw = bits.width;
texh = bits.rows;
for(;;){
if (imgw >= texw){
break;
} else {
imgw <<= 1;
}
}
for(;;){
if (imgh >= texh){
break;
} else {
imgh <<= 1;
}
}
if (imgw > imgh){//取较大者
imgh = imgw;
} else {
imgw = imgh;
}
texd = new u64[imgw*imgh];
memset(texd,0,imgw*imgh*sizeof(u64));
u64 *texp = texd;


for (int i = 0;i < bits.rows;i++){
u64 *rowp = texp;
for (int j = 0;j < bits.width;j++){
if (*pt) *rowp = ((*pt) << 24)+0xffffff;
else *rowp = 0x000000;
pt++;
rowp++;
}
texp += imgw;
}
}
}
c8 name[128];
sprintf(name,"TTFontGlyph%d",idx);
video::IImage *img = Driver->createImageFromData(video::ECF_A8R8G8B8,core::dimension2d<s32>(imgw,imgh),texd);
bool flg16 = Driver->getTextureCreationFlag(video::ETCF_ALWAYS_16_BIT);
bool flg32 = Driver->getTextureCreationFlag(video::ETCF_ALWAYS_32_BIT);
Driver->setTextureCreationFlag(video::ETCF_ALWAYS_16_BIT,false);
Driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT,true);
chtmp.Texture = Driver->addTexture(name,img);
img->drop();
Driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT,flg32);
Driver->setTextureCreationFlag(video::ETCF_ALWAYS_16_BIT,flg16);
delete texd;
}

else if (!FT_Load_Glyph(face,idx,FT_LOAD_NO_HINTING|FT_LOAD_RENDER|FT_LOAD_MONOCHROME)){
FT_GlyphSlot glyph = face->glyph;
FT_Bitmap bits = glyph->bitmap;
u8 *pt = bits.buffer;
texw = bits.width;
texh = bits.rows;
for(;;){
if (imgw >= texw){
break;
} else {
imgw <<= 1;
}
}
for(;;){
if (imgh >= texh){
break;
} else {
imgh <<= 1;
}
}
if (imgw > imgh){
imgh = imgw;
} else {
imgw = imgh;
}
u64 *texd16 = new u64[imgw*imgh];
memset(texd16,0,imgw*imgh*sizeof(u64));
u64 *texp = texd;
offset = Size - bits.rows;
for (int y = 0;y < bits.rows;y++){
u64 *rowp = texp;
for (int x = 0;x < bits.width;x++){
if (pt[y * bits.pitch + (x / 8)] & (0x80 >> (x % 8))){
*rowp = 0xffff;
}
rowp++;
}
texp += imgw;
}
c8 name[128];
sprintf(name,"TTFontGlyph%d_16",idx);
video::IImage *img = Driver->createImageFromData(video::ECF_A1R5G5B5,core::dimension2d<s32>(imgw,imgh),texd);
chtmp.Texture = Driver->addTexture(name,img);
img->drop();
Driver->makeColorKeyTexture(chtmp.Texture,video::SColor(0,0,0,0));
delete texd;
}
chtmp.CharRect=core::rect<s32>(0,0,bits.width,bits.rows);
chtmp.UsedFor=1;
chtmp.Character=ch;
if(bits.width>MaxHight) MaxHight=bits.width;
if(c<MaxCache)//还可以添加字符缓存
{
Characters.push_back(chtmp);
return c;  
}
else//字符缓存已满
{
Characters[min]=chtmp;
return min;
}

}

s32 CGUIFont::getMaxHight()
{
if (MaxHight==0) CheckChar(L'A');
return MaxHight;
}
} // end namespace gui
} // end namespace irr


十、CGUIEnvironment.cpp
修改loadBuidInFont()函数:
void CGUIEnvironment::loadBuidInFont()
{
const c8* filename = "g://STFANGSO.TTF";//你的默认字体文件位置

#ifdef __BIG_ENDIAN__
for (int i=0;i<sizeof(BuildInFontData)/sizeof(s32);i++) BuildInFontData[i] = OSReadSwapInt32(&BuildInFontData[i],0);
#endif

CGUIFont* font = new CGUIFont(Driver);
if (!font->load(filename)
{
os::Printer::log("Error: Could not load built-in Font.", ELL_ERROR);
font->drop();
return;
}

SFont f;
f.Filename = filename;
f.Font = font;
Fonts.push_back(f);
}

 

十一、CIrrDeviceWin32.cpp
为了实现中文的输入,我们还要修改CIrrDeviceWin32.cpp文件中的
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)函数
在函数的 switch()里面加上
case WM_IME_CHAR:
{
event.EventType = irr::EET_KEY_INPUT_EVENT;
event.KeyInput.PressedDown = true;
event.KeyInput.Key = irr::KEY_OEM_CLEAR ;
event.KeyInput.Shift = 0;
event.KeyInput.Control = 0;

char p1[2];
p1[0]=(char)((wParam&0xff00)>>8);
p1[1]=(char)(wParam&0xff);

if(p1[0]==0)
{
event.KeyInput.Char=(c16)wParam;
}
else
//多字节代码转unicode     
MultiByteToWideChar(CP_OEMCP,MB_divCOMPOSED,p1,2,&(event.KeyInput.Char),1);

dev = getDeviceFromHWnd(hWnd);

if (dev)
dev->postEventFromUser(event);

break;
}


十二、GUIIcons.h
修改这里的一些特殊符号,要不然你的控件上会出现一些莫名其妙的字符的。你可以随意修改。
const wchar_t GUI_ICON_WINDOW_MAXIMIZE[] = L"";// {257, 0};
const wchar_t GUI_ICON_WINDOW_RESTORE[] = L"";// {258, 0};
const wchar_t GUI_ICON_WINDOW_CLOSE[] = L"╳";// {259, 0};
const wchar_t GUI_ICON_WINDOW_MINIMIZE[] = L"";// {260, 0};
const wchar_t GUI_ICON_CURSOR_UP[] = L"▲";// {261, 0};
const wchar_t GUI_ICON_CURSOR_DOWN[] = L"▼";// {262, 0};
const wchar_t GUI_ICON_CURSOR_LEFT[] = L"&#9664;";// {263, 0};
const wchar_t GUI_ICON_CURSOR_RIGHT[] = L"&#9654;";// {264, 0};
const wchar_t GUI_ICON_CHECK_BOX_CHECKED[] =L"√";// {265, 0};
const wchar_t GUI_ICON_DROP_DOWN[] = L"";// {266, 0};
const wchar_t GUI_ICON_SMALL_CURSOR_UP[] = L"▲";// {267, 0};
const wchar_t GUI_ICON_SMALL_CURSOR_DOWN[] =L"▼";// {268, 0};
const wchar_t GUI_ICON_RADIO_BUTTON_CHECKED[] =L"&#8226;";// {269, 0};
const wchar_t GUI_ICON_FILE[] = L"";// {270, 0};
const wchar_t GUI_ICON_DIRECTORY[] = L"";// {271, 0};

好了,修改结束了!从新编译然后试一试吧!
下一步会将设置字号的函数加入类中,继续努力!

附件中含有效果图。今天已经很晚了,明天将把文件发上来!


发现的问题:
1。修改CGUIFont类析构函数的错误
2。修改u32 CheckChar(c16 ch)函数,修复不能输出空格的问题
作如下修改:
1。给CGUIFont类加入s32 SpaceCharWidth;私有成员
2。修改load函数为
bool CGUIFont::load(const c8* filename,s32 size)
{
if (FT_Init_FreeType( &library ))
{
os::Printer::log("Init FreeType Library Error.", ELL_ERROR);
return false;
}
if (FT_New_Face( library,filename,0,&face ))
{
os::Printer::log("Init FreeType New Face Error.", ELL_ERROR);
return false;
}
if(FT_Set_Pixel_Sizes(face,0,size))
{
os::Printer::log("Set FreeType Pixel Sizes Error.", ELL_ERROR);
return false;
}
SpaceCharWidth=getWidthFromCharacter(L'A');
return true;
}
3。修改析构函数为
CGUIFont::~CGUIFont()
{

 

if(face) FT_Done_Face(face);
if(library) FT_Done_FreeType(library);
if (Driver)
Driver->drop();
}
4。修改CheckChar()函数为
u32 CGUIFont::CheckChar(c16 ch)
{
int min=0,c=0;
_Character chtmp;

for (;c<Characters.size();c++)
{
if(Characters[c].UsedFor<Characters[min].UsedFor) 
min=c;
if(Characters[c].Character==ch) 
{
Characters[c].UsedFor++;
return c;
}
}

int imgw = 1;
int imgh = 1;
u64 *texd;
FT_Bitmap bits;
int texw;
int texh;
s32 offset;
u32 idx=FT_Get_Char_Index(face, ch );
if(idx==0) idx=FT_Get_Char_Index(face, WrongCharacter );


if (!FT_Load_Glyph(face,idx,FT_LOAD_NO_HINTING|FT_LOAD_NO_BITMAP)){
FT_GlyphSlot glyph = face->glyph;
if (glyph->format == ft_glyph_format_outline ){
if (!FT_Render_Glyph( glyph, FT_RENDER_MODE_NORMAL)){
bits = glyph->bitmap;
unsigned char *pt = bits.buffer;
texw = bits.width;
texh = bits.rows;
if(bits.width==0) texw=SpaceCharWidth;
                                                                 //新加入的
if(bits.rows==0) texh=getMaxHight);
                                                                 //新加入的

for(;;){
if (imgw >= texw){
break;
} else {
imgw <<= 1;
}
}
for(;;){
if (imgh >= texh){
break;
} else {
imgh <<= 1;
}
}
if (imgw > imgh){//取较大者
imgh = imgw;
} else {
imgw = imgh;
}
texd = new u64[imgw*imgh];
memset(texd,0,imgw*imgh*sizeof(u64));
u64 *texp = texd;

if(bits.width!=0)
{
for (int i = 0;i < bits.rows;i++){
u64 *rowp = texp;
for (int j = 0;j < bits.width;j++){
if (*pt) *rowp = ((*pt) << 24)+0xffffff;
else *rowp = 0x000000;
pt++;
rowp++;
}
texp += imgw;
}
}
}
}
c8 name[128];
sprintf(name,"TTFontGlyph%d",idx);
video::IImage *img = Driver->createImageFromData(video::ECF_A8R8G8B8,core::dimension2d<s32>(imgw,imgh),texd);
bool flg16 = Driver->getTextureCreationFlag(video::ETCF_ALWAYS_16_BIT);
bool flg32 = Driver->getTextureCreationFlag(video::ETCF_ALWAYS_32_BIT);
Driver->setTextureCreationFlag(video::ETCF_ALWAYS_16_BIT,false);
Driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT,true);
chtmp.Texture = Driver->addTexture(name,img);
img->drop();
Driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT,flg32);
Driver->setTextureCreationFlag(video::ETCF_ALWAYS_16_BIT,flg16);
delete texd;
}

else if (!FT_Load_Glyph(face,idx,FT_LOAD_NO_HINTING|FT_LOAD_RENDER|FT_LOAD_MONOCHROME)){
FT_GlyphSlot glyph = face->glyph;
FT_Bitmap bits = glyph->bitmap;
u8 *pt = bits.buffer;
texw = bits.width;
texh = bits.rows;
if(bits.width==0) texw=SpaceCharWidth;//新加入的
if(bits.rows==0) texh=getMaxHight();/新加入的
for(;;){
if (imgw >= texw){
break;
} else {
imgw <<= 1;
}
}
for(;;){
if (imgh >= texh){
break;
} else {
imgh <<= 1;
}
}
if (imgw > imgh){
imgh = imgw;
} else {
imgw = imgh;
}
u64 *texd16 = new u64[imgw*imgh];
memset(texd16,0,imgw*imgh*sizeof(u64));
u64 *texp = texd;
offset = Size - bits.rows;
for (int y = 0;y < bits.rows;y++){
u64 *rowp = texp;
for (int x = 0;x < bits.width;x++){
if (pt[y * bits.pitch + (x / 8)] & (0x80 >> (x % 8))){
*rowp = 0xffff;
}
rowp++;
}
texp += imgw;
}
c8 name[128];
sprintf(name,"TTFontGlyph%d_16",idx);
video::IImage *img = Driver->createImageFromData(video::ECF_A1R5G5B5,core::dimension2d<s32>(imgw,imgh),texd);
chtmp.Texture = Driver->addTexture(name,img);
img->drop();
Driver->makeColorKeyTexture(chtmp.Texture,video::SColor(0,0,0,0));
delete texd;
}
chtmp.CharRect=core::rect<s32>(0,0,texw,texh);
chtmp.UsedFor=1;
chtmp.Character=ch;
if(bits.width>MaxHight) MaxHight=texw;
if(c<MaxCache)//还可以添加字符缓存
{
Characters.push_back(chtmp);
return c;  
}
else//字符缓存已满
{
Characters[min]=chtmp;
return min;
}

}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值