shawind 发表于 2006-9-20 10:15:01

[分享]Irrlicht中显示中文的办法

用到的是日本人Zgock做的TrueType类库,CGUITTFont
原来支持的是dx8的,我机子上没有,所以改了一下,支持dx9,需要opengl也可以自己动手。
这是基于FreeType开发的,因此得去下FreeType的支持库。http://www.freetype.org/

我用的是自己编译的静态连接库,编译后的exe大约是多了250K左右。
个人觉得,这样至少比多带一个dll看起来舒服一点。

-----------------------main.cpp代码-------------------------------------------
#include <windows.h>
#include <irrlicht.h>
#include <CGUITTFont.h>

using namespace irr;      
using namespace core;   
using namespace video;
using namespace gui;

#pragma comment (lib, "Irrlicht.lib")          //irrlicht的库文件
#pragma comment (lib, "freetype221.lib")//freetype的库文件

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )
{
   IrrlichtDevice *Device = createDevice(EDT_DIRECT3D9, dimension2d<s32>(400, 300),16, false, false, 0);//这里用的是dx9
    Device->setWindowCaption(L"这是一个显示简体中文的简单例子");
    IVideoDriver*Driver= Device->getVideoDriver();
    CGUITTFace face;
    face.load("C:\\windows\\fonts\\simsun.ttc");   //载入字体,这里用的是微软的宋体
    CGUITTFont *Font = new CGUITTFont(Driver);
    Font->attach(&face,26); //字体大小      
    Font->AntiAlias = true;//反锯齿,使得字体显示更好看

    while(Device->run())
         {
       Driver->beginScene(true, true, SColor(0,100,100,100));
         Font->draw(L"HelloWorld !",rect<s32>(50,50,0,0),SColor(255,255,0,0)); //SColor格式似乎是ARGB
          Font->draw(L"你好世界!",rect<s32>(50,100,0,0),SColor(255,0,255,0));
       Driver->endScene();
         }

       Device->drop();   //释放资源
    return 0;
    }
----------------------------------------------------------------------
但是这样只是支持了中文的显示,对于中文输入还是不行。
估计那要去改irr::gui里面的东西了,不懂了,而且没找到现成的解决办法。


ps:没法上传附件,干脆用文本传了。
-----------CGUITTFont.h-----------------
#ifndef __C_GUI_TTFONT_H_INCLUDED__
#define __C_GUI_TTFONT_H_INCLUDED__

#include <ft2build.h>
#include <freetype/freetype.h>
#include <irrlicht.h>

namespace irr
{
namespace gui
{
class CGUITTFace : public IUnknown
{
public:
    FT_Library    library;
    FT_Face      face;
    bool load(const c8* filename);
};
class CGUITTGlyph : public IUnknown
{
public:
    bool cached;
    video::IVideoDriver* Driver;
    CGUITTGlyph();
    virtual ~CGUITTGlyph();
    void cache(u32 idx);
    FT_Face *face;
    u32 size;
    u32 top;
    u32 left;
    u32 texw;
    u32 texh;
    u32 imgw;
    u32 imgh;
    video::ITexture *tex;
    u32 top16;
    u32 left16;
    u32 texw16;
    u32 texh16;
    u32 imgw16;
    u32 imgh16;
    video::ITexture *tex16;
    s32 offset;
    u8 *image;
};
class CGUITTFont : public IGUIFont
{
public:

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

    //! destructor
    virtual ~CGUITTFont();

    //! loads a truetype font file
    bool attach(CGUITTFace *Face,u32 size);

    //! draws an text and clips it to the specified rectangle if wanted
    virtual void draw(const wchar_t* 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 wchar_t* text);

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

    bool AntiAlias;
    bool TransParency;
private:
    s32 CGUITTFont::getWidthFromCharacter(wchar_t c);
    u32 getGlyphByChar(wchar_t c);
    video::IVideoDriver* Driver;
    core::array< CGUITTGlyph > Glyphs;
    CGUITTFace *tt_face;
};

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

#endif

-----------------CGUITTFont.h----------------------------------

-----------------CGUITTFont.cpp-------------------------------
#include "irrlicht.h"
#include "CGUITTFont.h"

namespace irr

{
namespace gui
{

CGUITTGlyph::CGUITTGlyph() : IUnknown()
{
    tex = NULL;
    image = NULL;
}

CGUITTGlyph::~CGUITTGlyph()
{
    if (image) delete image;
//    if (tex) Driver->removeTexture(tex);
}

void CGUITTGlyph::cache(u32 idx)
{
    FT_Set_Pixel_Sizes(*face,0,size);
    if (!FT_Load_Glyph(*face,idx,FT_LOAD_NO_HINTING|FT_LOAD_NO_BITMAP)){
      FT_GlyphSlot glyph = (*face)->glyph;
      FT_Bitmapbits;
      if (glyph->format == ft_glyph_format_outline ){
            if (!FT_Render_Glyph( glyph, FT_RENDER_MODE_NORMAL)){
                bits = glyph->bitmap;
                u8 *pt = bits.buffer;
                image = new u8;
                memcpy(image,pt,bits.width * bits.rows);
                top = glyph->bitmap_top;
                left = glyph->bitmap_left;
                imgw = 1;
                imgh = 1;
                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;
                }
                u32 *texd = new u32;
                memset(texd,0,imgw*imgh*sizeof(u32));
                u32 *texp = texd;
                offset = size - bits.rows;
                bool cflag = (Driver->getDriverType() == video::EDT_DIRECT3D9);
                for (int i = 0;i < bits.rows;i++){
                  u32 *rowp = texp;
                  for (int j = 0;j < bits.width;j++){
                        if (*pt){
                            if (cflag){
                              *rowp = *pt;
                              *rowp *= 0x01010101;
                            } else {
                              *rowp = *pt << 24;
                              *rowp |= 0xffffff;
                            }
                        } else {
                            *rowp = 0;
                        }
                        pt++;
                        rowp++;
                  }
                  texp += imgw;
                }
                c8 name;
                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);
                tex = Driver->addTexture(name,img);
                Driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT,flg32);
                Driver->setTextureCreationFlag(video::ETCF_ALWAYS_16_BIT,flg16);
                delete texd;
                cached = true;
            }
      }
    }
    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;
      top16 = glyph->bitmap_top;
      left16 = glyph->bitmap_left;
      imgw16 = 1;
      imgh16 = 1;
      texw16 = bits.width;
      texh16 = bits.rows;
      for(;;){
            if (imgw16 >= texw16){
                break;
            } else {
                imgw16 <<= 1;
            }
      }
      for(;;){
            if (imgh16 >= texh16){
                break;
            } else {
                imgh16 <<= 1;
            }
      }
      if (imgw16 > imgh16){
            imgh16 = imgw16;
      } else {
            imgw16 = imgh16;
      }
      u16 *texd16 = new u16;
      memset(texd16,0,imgw16*imgh16*sizeof(u16));
      u16 *texp16 = texd16;
      offset = size - bits.rows;
      for (int y = 0;y < bits.rows;y++){
            u16 *rowp = texp16;
            for (int x = 0;x < bits.width;x++){
                if (pt & (0x80 >> (x % 8))){
                  *rowp = 0xffff;
                }
                rowp++;
            }
            texp16 += imgw16;
      }
      c8 name;
      sprintf(name,"TTFontGlyph%d_16",idx);
      video::IImage *img = Driver->createImageFromData(video::ECF_A1R5G5B5,core::dimension2d<s32>(imgw16,imgh16),texd16);
      tex16 = Driver->addTexture(name,img);
      Driver->makeColorKeyTexture(tex16,video::SColor(0,0,0,0));
      delete texd16;
    }
}

//! constructor
CGUITTFont::CGUITTFont(video::IVideoDriver* driver)
: Driver(driver)
{
    #ifdef _DEBUG
    setDebugName("CGUITTFont");
    #endif

    if (Driver)
      Driver->grab();
    AntiAlias = false;
    TransParency = false;
}



//! destructor
CGUITTFont::~CGUITTFont()
{
    if (Driver)
      Driver->drop();
}

//! loads a font file
bool CGUITTFace::load(const c8* filename)
{
    if (FT_Init_FreeType( &library )){
      return    false;
    }
    if (FT_New_Face( library,filename,0,&face )){
      return    false;
    }
    return    true;
}

bool CGUITTFont::attach(CGUITTFace *Face,u32 size)
{
    if (!Driver)
      return false;

    tt_face = Face;

    Glyphs.reallocate(tt_face->face->num_glyphs);
    Glyphs.set_used(tt_face->face->num_glyphs);
    for (int i = 0;i < tt_face->face->num_glyphs;i++){
      Glyphs.Driver = Driver;
      Glyphs.size = size;
      Glyphs.face = &(tt_face->face);
      Glyphs.cached = false;
//      Glyphs.cache((wchar_t)i + 1);
    }
    return    true;
}

u32 CGUITTFont::getGlyphByChar(wchar_t c){
    u32 idx = FT_Get_Char_Index( tt_face->face, c );
    if (idx && !Glyphs.cached)    Glyphs.cache(idx);
    return    idx;
}

//! returns the dimension of a text
core::dimension2d<s32> CGUITTFont::getDimension(const wchar_t* text)
{
    core::dimension2d<s32> dim(0, Glyphs.size);

    for(const wchar_t* p = text; *p; ++p)
    {
      dim.Width += getWidthFromCharacter(*p);
    }

    return dim;
}


inline s32 CGUITTFont::getWidthFromCharacter(wchar_t c)
{
    u32 n = getGlyphByChar(c);
    if ( n > 0){
      int w = Glyphs.texw;
      s32 left = Glyphs.left;
      if (w + left > 0) return w + left;
    }
    if (c >= 0x2000){
      return    Glyphs.size;
    } else {
      return    Glyphs.size / 2;
    }
}


//! draws an text and clips it to the specified rectangle if wanted
void CGUITTFont::draw(const wchar_t* 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;
    video::SColor colors;
    for (int i = 0;i < 4;i++){
      colors = color;
    }

    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;

    while(*text)
    {
      n = getGlyphByChar(*text);
      if ( n > 0){
            if (AntiAlias){
                s32 imgw = Glyphs.imgw;
                s32 imgh = Glyphs.imgh;
                s32 texw = Glyphs.texw;
                s32 texh = Glyphs.texh;
                s32 offx = Glyphs.left;
                s32 offy = Glyphs.size - Glyphs.top;
                if (Driver->getDriverType() != video::EDT_SOFTWARE){
                  if (!TransParency)    color.color |= 0xff000000;
                  Driver->draw2DImage(Glyphs.tex,core::position2d<s32>(offset.X+offx,offset.Y+offy),core::rect<s32>(0,0,imgw-1,imgh-1),clip,color,true);
                } else {
                  s32 a = color.getAlpha();
                  s32 r = color.getRed();
                  s32 g = color.getGreen();
                  s32 b = color.getBlue();
                  u8 *pt = Glyphs.image;
                  if (!TransParency)    a = 255;
                  for (int y = 0;y < texh;y++){
                        for (int x = 0;x < texw;x++){
                            if (!clip || clip->isPointInside(core::position2d<s32>(offset.X+x+offx,offset.Y+y+offy))){
                              if (*pt){
                                    Driver->draw2DRectangle(video::SColor((a * *pt)/255,r,g,b),core::rect<s32>(offset.X+x+offx,offset.Y+y+offy,offset.X+x+offx+1,offset.Y+y+offy+1));
                              }
                              pt++;
                            }
                        }
                  }
                }
            } else {
                s32 imgw = Glyphs.imgw16;
                s32 imgh = Glyphs.imgh16;
                s32 texw = Glyphs.texw16;
                s32 texh = Glyphs.texh16;
                s32 offx = Glyphs.left16;
                s32 offy = Glyphs.size - Glyphs.top16;
                if (!TransParency){
                  color.color |= 0xff000000;
                }
                Driver->draw2DImage(Glyphs.tex16,core::position2d<s32>(offset.X+offx,offset.Y+offy),core::rect<s32>(0,0,imgw-1,imgh-1),clip,color,true);
            }
            offset.X += getWidthFromCharacter(*text);
      } else {
            offset.X += getWidthFromCharacter(*text);
      }

      ++text;
    }
}

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

    while (text)
    {
      x += getWidthFromCharacter(text);

      if (x >= pixel_x)
            return idx;

      ++idx;
    }

    return -1;
}

#if    0

#endif
} // end namespace gui
} // end namespace irr

-----------------------CGUITTFont.cpp----------------------------
页: [1]
查看完整版本: [分享]Irrlicht中显示中文的办法