Ogre 代码分析 -- 关于中文字体分析OgreFont

来源:互联网 发布:国内音频矩阵品牌 编辑:程序博客网 时间:2024/06/05 21:18

void MovableText::setFontName(const String &fontName)

里面加载纹理---只需改变这个纹理即可达到中文显示

mpMaterial = mpFont->getMaterial()->clone(mName + "Material");

这个纹理是通过 Font->getMaterial()获得的材质!

         inline const MaterialPtr& getMaterial()
        {
            return mpMaterial;
        }
OgreFont返回一个材质指针,是类成员变量

MaterialPtr mpMaterial; 这个就是成员变量-材质
TexturePtr mTexture;        这个是图片的变量

OgreFont::void Font::loadImpl()
里面有创建材质
mpMaterial = MaterialManager::getSingleton().create(
    "Fonts/" + mName, mGroup);
这个函数内下一行针对truetype的
        if (mType == FT_TRUETYPE)
        {
            createTextureFromFont();
            texLayer = mpMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0);
            // Always blend by alpha
            blendByAlpha = true;
        }
下一个函数:
createTextureFromFont();

     void Font::createTextureFromFont(void)
    {

   // Just create the texture here, and point it at ourselves for when
   // it wants to (re)load for real
   String texName = mName + "Texture";
   // Create, setting isManual to true and passing self as loader
   mTexture = TextureManager::getSingleton().create(
    texName, mGroup, true, this);
   mTexture->setTextureType(TEX_TYPE_2D);
   mTexture->setNumMipmaps(0);
   mTexture->load();

   TextureUnitState* t = mpMaterial->getTechnique(0)->getPass(0)->createTextureUnitState( texName );
   // Allow min/mag filter, but no mip
   t->setTextureFiltering(FO_LINEAR, FO_LINEAR, FO_NONE);

}
   
    也就是在这个函数内部只创建了一个空的纹理指针mTexture
   
    到这目标跟丢了 ......
    回头看下OgreFont类
    class _OgreExport Font : public Resource, public ManualResourceLoader
    继承自Resource     ManualResourceLoader
   
    virtual void ManualResourceLoader::loadResource(Resource* resource) = 0;
    这个是纯虚函数,肯定会在初始化的时候自动调用这个函数 看下OgreFont中的这个函数
   
    void Font::loadResource(Resource* res)
    这个函数是OgreFont类中最多的一个函数:-D,接下来仔细看这个函数:
    void Font::loadResource(Resource* res)
{
            FT_Library ftLibrary;
    加载TrueType库,使用Truetype转化到图片
            if( FT_Init_FreeType( &ftLibrary ) )
            OGRE_EXCEPT( Exception::ERR_INTERNAL_ERROR, "Could not init FreeType library!",
            "Font::Font");
           
    声明一个truetype的表面
            FT_Face face;
    这个shi定义子间距
            uint char_spacer = 5;
     声明一段内存来存放字体的每个像素颜色值,使用DataStreamPtr
        DataStreamPtr dataStreamPtr =
    ResourceGroupManager::getSingleton().openResource(
     mSource, mGroup, true, this);
            MemoryDataStream ttfchunk(dataStreamPtr);
    加载字体
            if( FT_New_Memory_Face( ftLibrary, ttfchunk.getPtr(), (FT_Long)ttfchunk.size() , 0, &face ) )
            OGRE_EXCEPT( Exception::ERR_INTERNAL_ERROR,
            "Could not open font face!", "Font::createTextureFromFont" );
    
     设置下truetype字体的大小 26.6每像素
             FT_F26Dot6 ftSize = (FT_F26Dot6)(mTtfSize * (1 << 6));
            if( FT_Set_Char_Size( face, ftSize, 0, mTtfResolution, mTtfResolution ) )
            OGRE_EXCEPT( Exception::ERR_INTERNAL_ERROR,
            "Could not set char size!", "Font::createTextureFromFont" );
           
     设置字体最大最小值---在这只是声明一下:
            int max_height = 0, max_width = 0;
           
     设置向后兼容大小--具体干嘛的,暂时不知道
        if (mCodePointRangeList.empty())
            {  
                mCodePointRangeList.push_back(CodePointRange(33, 166));
            }
     根据truetype glyph计算字体的大小
            size_t glyphCount = 0;
     循环所有兼容大小
            for (CodePointRangeList::const_iterator r = mCodePointRangeList.begin();
                r != mCodePointRangeList.end(); ++r)
             {
                 const CodePointRange& range = *r;
                        for(CodePoint cp = range.first; cp <= range.second; ++cp, ++glyphCount)
                         {
             根据字体face计算最大最小的字体的宽高
                        FT_Load_Char( face, cp, FT_LOAD_RENDER );
                        if( ( 2 * ( face->glyph->bitmap.rows << 6 ) - face->glyph->metrics.horiBearingY ) > max_height )
                            max_height = ( 2 * ( face->glyph->bitmap.rows << 6 ) - face->glyph->metrics.horiBearingY );
                        if( face->glyph->metrics.horiBearingY > mTtfMaxBearingY )
                            mTtfMaxBearingY = face->glyph->metrics.horiBearingY;
                        if( (face->glyph->advance.x >> 6 ) + ( face->glyph->metrics.horiBearingX >> 6 ) > max_width)
                            max_width = (face->glyph->advance.x >> 6 ) + ( face->glyph->metrics.horiBearingX >> 6 );
                         }
              }
    
     计算下字体所需要的空间
        size_t rawSize = (max_width + char_spacer) *
        ((max_height >> 6) + char_spacer) * glyphCount;
            uint32 tex_side = static_cast<uint32>(Math::Sqrt(rawSize));
     计算其他glyph
            tex_side += std::max(max_width, (max_height>>6));
    计算出字体空间
            uint32 roundUpSize = Bitwise::firstPO2From(tex_side);
           
     接下来是把glyph转换成位图矩阵,声明最终的宽高
            size_t finalWidth, finalHeight;
             if (roundUpSize*roundUpSize*0.5 >= rawSize)
            {
                finalHeight = static_cast<size_t>(roundUpSize * 0.5);
            }
            else
            {
                finalHeight = roundUpSize;
            }
            finalWidth = roundUpSize;
    
     计算出纹理图片的宽高比例
            Real textureAspect = finalWidth / finalHeight;
    计算出纹理空间的宽高,根据每个像素占用空间
            const size_t pixel_bytes = 2;   //颜色占一个字节,alpha值占一个字节
            size_t data_width = finalWidth * pixel_bytes;
            size_t data_size = finalWidth * finalHeight * pixel_bytes;
    重点:
    分配内存空间
            uchar* imageData = OGRE_ALLOC_T(uchar, data_size, MEMCATEGORY_GENERAL);
           
    对这个内存空间进行一次清空
            for (size_t i = 0; i < data_size; i += pixel_bytes)
            {
                对像素设置成255--全是白色
                imageData[i + 0] = 0xFF; // luminance
                对alpha值全部设置成0 黑色
                imageData[i + 1] = 0x00; // alpha
            }
     下面就是对纹理进行填充值了
    
     首先声明两个临时变量
            要在循环中使用
            size_t l = 0, m = 0;
            循环所有兼容大小
         for (CodePointRangeList::const_iterator r = mCodePointRangeList.begin();
    r != mCodePointRangeList.end(); ++r)
            {
                const CodePointRange& range = *r;
                for(CodePoint cp = range.first; cp <= range.second; ++cp )
                {
                声明一个truetype的返回类型
                    FT_Error ftResult;
                加载TrueType glyph Render
                    ftResult = FT_Load_Char( face, cp, FT_LOAD_RENDER );
                加载失败,判断
                    if (ftResult)
                    {  
      // problem loading this glyph, continue
                        LogManager::getSingleton().logMessage("Info: cannot load character " +
                            StringConverter::toString(cp) + " in font " + mName);
                        continue;
                    }
                   
                   FT_Int advance = (face->glyph->advance.x >> 6 ) + ( face->glyph->metrics.horiBearingX >> 6 );
                 获得glyph的位图内存
                   unsigned char* buffer = face->glyph->bitmap.buffer;
                加载失败,判断
                   if (!buffer)
                     {
                        // Yuck, FT didn't detect this but generated a null pointer!
                        LogManager::getSingleton().logMessage("Info: Freetype returned null for character " +
       StringConverter::toString(cp) + " in font " + mName);
                        continue;
                      }  
                
                    int y_bearnig = ( mTtfMaxBearingY >> 6 ) - ( face->glyph->metrics.horiBearingY >> 6 );
                开始了---循环所有兼容大小face->glyph的位图rows
                    for(int j = 0; j < face->glyph->bitmap.rows; j++ )
                    {
                        size_t row = j + m + y_bearnig;
                 获得纹理材质内存空间的地址,作为引用来替代赋值
                        uchar* pDest = &imageData[(row * data_width) + l * pixel_bytes];
                 在循环所有兼容大小face->glyph的位图的高
                        for(int k = 0; k < face->glyph->bitmap.width; k++ )
                        {
                           if (mAntialiasColour)
                                {          
                 使用字体位图的颜色
                                *pDest++= *buffer;
                                 }
                                 else
                                {
                                // Always white whether 'on' or 'off' pixel, since alpha
                                // will turn off
                                *pDest++= 0xFF;
                                }
                    给 ALPHA 赋值,使用位图
                                *pDest++= *buffer++;
       }
                    }
            
             在这调用了setGlyphTexCoords()这个函数,下面会进行这个函数分析
                    this->setGlyphTexCoords(cp,
                        (Real)l / (Real)finalWidth, // u1
                        (Real)m / (Real)finalHeight, // v1
                        (Real)( l + ( face->glyph->advance.x >> 6 ) ) / (Real)finalWidth, // u2
                        ( m + ( max_height >> 6 ) ) / (Real)finalHeight, // v2
                        textureAspect
                        );
                     l += (advance + char_spacer);
                    
                     if( finalWidth - 1 < l + ( advance ) )
                    {
                        m += ( max_height >> 6 ) + char_spacer;
                        l = 0;
                    }
            申请内存保存图片
                    DataStreamPtr memStream(
                    OGRE_NEW MemoryDataStream(imageData, data_size, true));
             保存到图片image
                     Image img;
                    img.loadRawData( memStream, finalWidth, finalHeight, PF_BYTE_LA );
             通过Image写到Texture
                ConstImagePtrList imagePtrs;
                    imagePtrs.push_back(&img);
                    tex->_loadImages( imagePtrs );
             FreeType 完毕
                    FT_Done_FreeType(ftLibrary);
            }
       
        下面分析setGlyphTexCoords
            CodePointMap::iterator i = mCodePointMap.find(id);
    if (i != mCodePointMap.end())
    {
     i->second.uvRect.left = u1;
     i->second.uvRect.top = v1;
     i->second.uvRect.right = u2;
     i->second.uvRect.bottom = v2;
     i->second.aspectRatio = textureAspect * (u2 - u1) / (v2 - v1);
    }
    else
    {
     mCodePointMap.insert(
      CodePointMap::value_type(id,
       GlyphInfo(id, UVRect(u1, v1, u2, v2),
        textureAspect * (u2 - u1) / (v2 - v1))));
    }