实现了简陋的Freetype2在DirectX下显示字体

实现了简陋的Freetype2在DirectX下显示字体
  上个月开始和同学一块在做类似CS的射击游戏demo,要自己实现游戏里的简单2DOverlay和文本显示(不用具体GUI了,菜单什么的再用CEGUI),由于我这人做事慢,所以就去实现字体这种小模块~0~(学末总是很难静下心编程唉~~),本来以为网上这类资源挺多的,搜到的无非是NeHe的openGL+ft2,还有一位仁兄模仿Ogre写的一个代码(里面有些问题),多不是很系统,自己认真花了3,4天模仿OgreFont实现了一个简单的英文字体在dx 9下使用ID3DXSprite接口进行渲染,然后照着clayman和hyzboy的提示修改成了支持中文的动态调频写入纹理那样(不过没做测试哦~~下下周得去上海2K面试,我就将就用了~)问题应该还有很多,而且应该做成考虑时间那样(LRU),以后再说吧。。。
整个流程:
实现了简陋的Freetype2在DirectX下显示字体_第1张图片
  最后把代码贴下面吧,希望对大家有帮助。

/* ***********************************************************************\
  This is a fucking
  ______   ___   _      _       
  |  ___| / _ \ | |    | |      
  | |_   / /_\ \| |__  | |  ___ 
  |  _|  |  _  || '_ \ | | / _ \
  | |    | | | || |_) || ||  __/
  \_|    \_| |_/|_.__/ |_| \___| 's free file

  filename: Font.h 
  created:  2010/07/30
  creator:    承天一
   
  purpose:  Freetype字体类
************************************************************************
*/

#ifndef __FONT_H__
#define  __FONT_H__

#include 
< map >
#include 
< ft2build.h >
#include FT_FREETYPE_H
#include FT_GLYPH_H

using   namespace  std;

namespace  Fable
{
    
class  Font :  public  NedAlloc
    {
    
public :
        typedef unsigned 
int  CodePoint;
        typedef FloatRect UVRect;
        
///  Information about the position and size of a glyph in a texture
         struct  GlyphInfo
        {
        
public :
            CodePoint codePoint;        
// 字符的unicode码
            UVRect uvRect;                 // 纹理区域
             float  aspectRatio;
            USHORT    useCount;            
// 字符的使用次数
            UINT    l;
            UINT    m;

        
public :
            GlyphInfo(CodePoint _code, 
const  UVRect &  _rect,  float  _aspect, UINT _l, UINT _m) 
                : codePoint(_code), uvRect(_rect), aspectRatio(_aspect), useCount(
0 ), l(_l), m(_m)
            {
            }
        };
        
///  A range of code points, inclusive on both ends
        typedef std::pair < CodePoint, CodePoint >  CodePointRange;
        typedef vector
< CodePointRange >  CodePointRangeList;
    
protected :
        
///  Map from unicode code point to texture coordinates
        typedef map < CodePoint, GlyphInfo >  CodePointMap;
        CodePointMap mCodePointMap;

        
///  Range of code points to generate glyphs for (truetype only)
        CodePointRangeList mCodePointRangeList;
    
public :

        Font();

        
~ Font();

        
void         load( const  std:: string &  name);

        
void         unLoad();

        
void         addCodePointRange( const  CodePointRange &  range)
        {
            mCodePointRangeList.push_back(range);
        }

        
/* * Clear the list of code point ranges.
        
*/
        
void         clearCodePointRanges()
        {
            mCodePointRangeList.clear();
        }
        
/* * Get a const reference to the list of code point ranges to be used to
            generate glyphs from a truetype font.
        
*/
        
const  CodePointRangeList &  getCodePointRangeList()  const
        {
            
return  mCodePointRangeList;
        }

    
protected :
        
///  Size of the truetype font, in points
         float             mTtfSize;
        
///  Resolution (dpi) of truetype font
        unsigned  int     mTtfResolution;
        
///  Max distance to baseline of this (truetype) font
         int                 mTtfMaxBearingY;
        
///  for TRUE_TYPE font only
         bool             mAntialiasColour;

        IDirect3DTexture9
*     mTexture;

        UINT            mWidth;
        UINT            mHeight;
        std::
string         mFontName;

        
///  纹理上使用区域还剩的个数
        UINT            mLeftBlankNum;
        UINT            mMaxCharNum;
        uchar
*             mImageData;
        FT_Library        mFtLibrary;
        FT_Face            mFtFace;

        UINT            mPixelBytes;
        UINT            mCharDataWidth;
        UINT            mMaxCharSize;
        UINT            mDataSize;

        
int                 mMaxHeight;
        
int                 mMaxWidth;
        
float             mTextureAspect;
        UINT            mCharSpacer;

        UINT            mImage_m;
        UINT            mImage_l;

    
public :

        UINT    getTextureWidth() 
const  {  return  mWidth;}

        UINT    getTextureHeight() 
const  {  return  mHeight;}


         inline 
const  UVRect &  getGlyphTexCoords(CodePoint id)  const
        {
            CodePointMap::const_iterator i 
=  mCodePointMap.find(id);
            
if  (i  !=  mCodePointMap.end())
            {
                
return  i -> second.uvRect;
            }
            
else
            {
                
static  UVRect nullRect( 0.0 0.0 0.0 0.0 );
                
return  nullRect;
            }
        }

        
/* * Sets the texture coordinates of a glyph.
        @remarks
            You only need to call this if you're setting up a font loaded from a texture manually.
        @note
            Also sets the aspect ratio (width / height) of this character. textureAspect
            is the width/height of the texture (may be non-square)
        
*/
        inline 
void  setGlyphTexCoords(CodePoint id, UINT u1Pixel, UINT v1Pixel, UINT u2Pixel, UINT v2Pixel,  float  textureAspect)
        {
            
float  u1  =  ( float )u1Pixel  /  ( float )mWidth, v1  =  ( float )v1Pixel  /  ( float )mHeight, u2  =  ( float )u2Pixel  /  ( float )mWidth, v2  =  ( float )v2Pixel  /  ( float )mWidth;
            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);
                i
-> second.l  =  u1Pixel;
                i
-> second.m  =  v1Pixel;
            }
            
else
            {
                mCodePointMap.insert(
                    CodePointMap::value_type(id, 
                        GlyphInfo(id, UVRect(u1, v1, u2, v2), 
                            textureAspect 
*  (u2  -  u1)   /  (v2  -  v1), u1Pixel, v1Pixel)));
            }

        }

        
/* * Gets the aspect ratio (width / height) of this character.  */
        inline 
float  getGlyphAspectRatio(CodePoint id)  const
        {
            CodePointMap::const_iterator i 
=  mCodePointMap.find(id);
            
if  (i  !=  mCodePointMap.end())
            {
                
return  i -> second.aspectRatio;
            }
            
else
            {
                
return   1.0 ;
            }
        }
        
/* * Sets the aspect ratio (width / height) of this character.
        @remarks
            You only need to call this if you're setting up a font loaded from a 
            texture manually.
        
*/
        inline 
void  setGlyphAspectRatio(CodePoint id,  float  ratio)
        {
            CodePointMap::iterator i 
=  mCodePointMap.find(id);
            
if  (i  !=  mCodePointMap.end())
            {
                i
-> second.aspectRatio  =  ratio;
            }
        }

        
/* * Gets the information available for a glyph corresponding to a
            given code point, or throws an exception if it doesn't exist;
        
*/
        
const  GlyphInfo *  getGlyphInfo(CodePoint id)  const ;


        LPDIRECT3DTEXTURE9    getFontTexture() 
const  {  return  mTexture; }

        
void     insertGlyphInfo(CodePoint id);

        
bool     hasBlankInTexture()  const  
        { 
            
return  mLeftBlankNum  >   0
        }

        
void     renderGlyphIntoTexture(CodePoint id);

        CodePoint    getLessUseChar();

        
void     removeGlyph(CodePoint id);
    
    };
}
#endif

/* ***********************************************************************\
This is a fucking
______   ___   _      _       
|  ___| / _ \ | |    | |      
| |_   / /_\ \| |__  | |  ___ 
|  _|  |  _  || '_ \ | | / _ \
| |    | | | || |_) || ||  __/
\_|    \_| |_/|_.__/ |_| \___| 's free file

filename: Font.cpp 
created:  2010/07/30
creator:    承天一

purpose:  Freetype字体类
************************************************************************
*/

#include 
" stdafx.h "
#include 
" RenderCore.h "
#include 
" Font.h "
#include 
" TextureManager.h "
#include 
< d3dx9tex.h >


#undef  max
#undef  min

namespace  Fable
{

    Font::Font()
        :mTtfMaxBearingY(
0 ), mTtfResolution( 0 ), mAntialiasColour( false ),
        mTexture(
0 ), mLeftBlankNum( 0 ),
        mImageData(nullptr), mImage_m(
0 ),mImage_l( 0 )
    {
        mWidth 
=   1024 ;
        mHeight 
=   1024 ;
        mTtfSize 
=   20 ;
        mTtfResolution 
=   96 ;
    }

    Font::
~ Font()
    {
        unLoad();
    }

    
void  Font::load( const  std:: string &  name)
    {
        mFontName 
=  name;

        FT_Library ftLibrary;

        
// 初始化库 
         if (FT_Init_FreeType( & ftLibrary))
            FA_EXCEPT(ERR_FONT, 
" FreeType初始化失败 " );

        mCharSpacer 
=   5 ;
        
if (FT_New_Face(ftLibrary, name.c_str(),  0 & mFtFace)) 
            FA_EXCEPT(ERR_FONT, 
" FreeType无法打开ttf文件 " );

        UINT maxFaceNum 
=  mFtFace -> num_faces;

        FT_F26Dot6 ftSize 
=  (FT_F26Dot6)(mTtfSize  *  ( 1   <<   6 ));

        
if (FT_Set_Char_Size( mFtFace, ftSize,  0 , mTtfResolution, mTtfResolution))
            FA_EXCEPT(ERR_FONT, 
" Could not set char size! " );

        mMaxHeight 
=   0 , mMaxWidth  =   0 ;

        
if (mCodePointRangeList.empty())
        {
            mCodePointRangeList.push_back(CodePointRange(
33 166 ));
            mCodePointRangeList.push_back(CodePointRange(
19968 40869 ));
        }

        
//  Calculate maximum width, height and bearing
         for  (CodePointRangeList::const_iterator r  =  mCodePointRangeList.begin();
            r 
!=  mCodePointRangeList.end();  ++ r)
        {
            
const  CodePointRange &  range  =   * r;
            
for (CodePoint cp  =  range.first; cp  <=  range.second;  ++ cp)
            {
                FT_Load_Char( mFtFace, cp, FT_LOAD_RENDER );

                
if ( (  2   *  ( mFtFace -> glyph -> bitmap.rows  <<   6  )  -  mFtFace -> glyph -> metrics.horiBearingY )  >  mMaxHeight )
                    mMaxHeight 
=  (  2   *  ( mFtFace -> glyph -> bitmap.rows  <<   6  )  -  mFtFace -> glyph -> metrics.horiBearingY );
                
if ( mFtFace -> glyph -> metrics.horiBearingY  >  mTtfMaxBearingY )
                    mTtfMaxBearingY 
=  mFtFace -> glyph -> metrics.horiBearingY;

                
if ( (mFtFace -> glyph -> advance.x  >>   6  )  +  ( mFtFace -> glyph -> metrics.horiBearingX  >>   6  )  >  mMaxWidth)
                    mMaxWidth 
=  (mFtFace -> glyph -> advance.x  >>   6  )  +  ( mFtFace -> glyph -> metrics.horiBearingX  >>   6  );
            }
        }

        
//  We just make a 1024 * 1024 texture, it's enough
        mTextureAspect  =   1.0f ;

        mPixelBytes 
=   2 ;
        mCharDataWidth 
=  (mMaxWidth  +  mCharSpacer)  *  mPixelBytes;
        mDataSize
=  mWidth  *  mHeight  *  mPixelBytes;
        mMaxCharSize 
=  ((mMaxHeight  >>   6 +  mCharSpacer)  *  mCharDataWidth;
        mMaxCharNum 
=  mDataSize  /  mMaxCharSize;
        mLeftBlankNum 
=  mMaxCharNum;
        CON_INFO(
" Font texture size %d * %d " , mWidth, mHeight);

        mImageData
=  FA_NEW_ARRAY_T(uchar, mDataSize);
        
//  Reset content (transparent, white)
         for  (size_t i  =   0 ; i  <  mDataSize; i  +=  mPixelBytes)
        {
            mImageData[i 
+   0 =   0xFF //  luminance
            mImageData[i  +   1 =   0x00 ;     //  alpha
        }

        HRESULT hr 
=   0 ;
        hr 
=  D3DXCreateTexture(
            RenderCore::getInstancePtr()
-> getDevice(),
            mWidth,
            mHeight,
            
1 ,
            
0 ,
            D3DFMT_A8L8,
            D3DPOOL_MANAGED,
            
& mTexture);

        
if (FAILED(hr))
        {
            
string  msg  =  DXGetErrorDescriptionA(hr);
            FA_EXCEPT(ERR_FONT, 
" Create font Texture failed:  "   +  msg);
        }

    }

    
void  Font::unLoad()
    {
        FA_DELETE_ARRAY_T(mImageData, uchar, mDataSize);
        SAFE_RELEASE(mTexture);
        FT_Done_FreeType(mFtLibrary);
    }

    
const  Font::GlyphInfo *  Font::getGlyphInfo(CodePoint id)  const
    {
        CodePointMap::const_iterator i 
=  mCodePointMap.find(id);
        
if  (i  ==  mCodePointMap.end())
        {
            
return  nullptr;
        }
        
return   & i -> second;
    }

    
void  Font::renderGlyphIntoTexture(CodePoint id)
    {
        FT_Error ftResult;

        
//  Load & render glyph
        ftResult  =  FT_Load_Char( mFtFace, id, FT_LOAD_RENDER );
        
if  (ftResult)
        {
            
//  problem loading this glyph, continue
            CON_INFO( " Info: cannot load CodePoint %d " , id);
        }

        FT_Int advance 
=  mFtFace -> glyph -> advance.x  >>   6 ;
        unsigned 
char *  buffer  =  mFtFace -> glyph -> bitmap.buffer;

        
if  ( ! buffer)
        {
            
//  Yuck, FT didn't detect this but generated a null pointer!
            CON_INFO( " Info: Freetype returned null for character %d " , id);
        }

        
int  y_bearnig  =  ( mTtfMaxBearingY  >>   6  )  -  ( mFtFace -> glyph -> metrics.horiBearingY  >>   6  );
        
int  x_bearing  =  mFtFace -> glyph -> metrics.horiBearingX  >>   6 ;

        
for ( int  j  =   0 ; j  <  mFtFace -> glyph -> bitmap.rows;  ++ j )
        {
            size_t row 
=  j  +  mImage_m  +  y_bearnig;
            UCHAR
*  pDest  =   & mImageData[(row  *  mWidth  *  mPixelBytes)  +  (mImage_l  +  x_bearing)  *  mPixelBytes];
            
for ( int  k  =   0 ; k  <  mFtFace -> glyph -> bitmap.width;  ++ k )
            {
                
if  (mAntialiasColour)
                        {
                            
//  Use the same greyscale pixel for all components RGBA
                             * pDest ++=   * buffer;
                        }
                        
else
                        {
                            
//  Always white whether 'on' or 'off' pixel, since alpha
                            
//  will turn off
                             * pDest ++=   0xFF ;
                        }
                        
//  Always use the greyscale value for alpha
                         * pDest ++=   * buffer ++
            }
        }

        
this -> setGlyphTexCoords(id,
            mImage_l,  
//  u1
            mImage_m,   //  v1
            mImage_l  +  ( mFtFace -> glyph -> advance.x  >>   6  ),  //  u2
            mImage_m  +  ( mMaxHeight  >>   6  ),  //  v2
            mTextureAspect
            );

        
//  Advance a column
        mImage_l  +=  (advance  +  mCharSpacer);

        
//  If at end of row
         if ( mWidth  -   1   <  mImage_l  +  ( advance ) )
        {
            mImage_m 
+=  ( mMaxHeight  >>   6  )  +  mCharSpacer;
            mImage_l 
=   0 ;
        }
        
-- mLeftBlankNum;

        D3DLOCKED_RECT lockedRect;
        mTexture
-> LockRect( 0 & lockedRect, 0 0 );         

        
// 使用类型注意
        uchar *  TexData  =  (uchar * )lockedRect.pBits;

        
for (UINT i  =   0 ; i  <  mHeight;  ++ i)
        {
            
for (UINT j  =   0 ; j  <  mWidth;  ++ j)
            {
                
// Pitch数据的总长度
                 int  index  =  i  *  lockedRect.Pitch  /  mPixelBytes  +  j;
                TexData[index] 
=  mImageData[index];
            }
        }
        mTexture
-> UnlockRect( 0 );

        
//  for test
// #ifdef    _DEBUG
//         D3DXSaveTextureToFileA(".. // media // test.png",D3DXIFF_PNG, mTexture, 0);
// #endif
    }

    
void  Font::insertGlyphInfo(CodePoint id)
    {
        
if ( ! hasBlankInTexture())     // has no space left in texture    
        {
            removeGlyph(getLessUseChar());
        }
        renderGlyphIntoTexture(id);

    }

    Font::CodePoint Font::getLessUseChar()
    {
        CodePointMap::iterator i 
=  mCodePointMap.begin(), iend  =  mCodePointMap.end(), iless  =  mCodePointMap.begin();
        
while (i  !=  iend)
        {
            
if (i -> second.useCount  <  iless -> second.useCount)
                iless 
=  i;
            
++ i;
        }
        
return  iless -> second.codePoint;   
    }

    
void  Font::removeGlyph(CodePoint id)
    {
        CodePointMap::iterator it 
=  mCodePointMap.find(id);
        
if (it  !=  mCodePointMap.end())
        {
            mImage_l 
=  it -> second.l;
            mImage_m 
=  it -> second.m;
            mCodePointMap.erase(it);
            
++ mLeftBlankNum;
        }
        
else
        {
            FA_EXCEPT(ERR_FONT, 
" Can not find CodePoint to remove in void Font::removeGlyph(CodePoint id) " );
        }
    }

}

你可能感兴趣的:(实现了简陋的Freetype2在DirectX下显示字体)